技术经验分享:Angular动态创建组件之Portals

简介: 技术经验分享:Angular动态创建组件之Portals

这篇文章主要介绍使用Angular api 和 CDK Portals两种方式实现动态创建组件,另外还会讲一些跟它相关的知识点,如:Angular多级依赖注入、ViewContainerRef,Portals可以翻译为 门户 ,我觉得放到这里叫 入口 更好,可以理解为动态创建组件的入口,类似于小程序或者Vue中的Slot.


cdk全名Component Development Kit 组件开发包,是Angular官方在开发基于Material Design的组件库时抽象出来单独的一个开发包,里面封装了一些开发组件时的公共逻辑并且跟Material Design 设计无关,可以用来封装自己的组件库或者直接在业务开发中使用,里面代码抽象程度非常高,非常值得学习,现在我用到的有Portals、Overlay(打开浮层相关)、SelectionModel、Drag and Drop等.


官方:


中文翻译:


动态创建组件


想想应用的路由,一般配置路由地址的时候都会给这个地址配置一个入口组件,当匹配到这个路由地址的时候就在指定的地方渲染这个组件,动态创建组件类似,在最页面未接收到用户行为的时候,我不知道页面中这块区域应该渲染那个组件,当页面加载时根据数据库设置或者用户的操作行为才能确定最终要渲染的组件,这时候就要用代码动态创建组件把目标组件渲染到正确的地方。


示例截图


使用Angular API动态创建组件


该路由的入口组件是PortalsEntryConponent组件,如上面截图所示右侧有一块虚线边框的区域,里面具体的渲染组件不确定。


第一步


先在视图模板中定义一个占位的区域,动态组件就要渲染在这个位置,起一个名称#virtualContainer


文件portals-entry.component.html


?1234[/code>div class="portals-outlet"

第二步


通过ViewChild取到这个container对应的逻辑容器


文件portals-entry.component.ts


?12@ViewChild('virtualContainer', { read: ViewContainerRef }) virtualContainer: ViewContainerRef;


第三步


处理单击事件,单击按钮时动态创建一个组件,portals-entry.component.ts完整逻辑


?12345678910111213141516171819202122232425262728293031import { TaskDetailComponent } from '../task/task-detail/task-detail.component';@Component({ selector: 'app-portals-entry', templateUrl: './portals-entry.component.html', styleUrls: 【'./portals-entry.component.scss'】, providers: 【 】})export class PortalsEntryComponent implements OnInit { @ViewChild('virtualContainer', { read: ViewContainerRef }) virtualContainer: ViewContainerRef; constructor( private dynamicComponentService: DynamicComponentService, private componentFactoryResolver: ComponentFactoryResolver, private injector: Injector, ) { } ngOnInit() { } openTask() { const task = new TaskEntity(); task.id = '1000'; task.name = '写一篇关于Portals的文章'; const componentFactory = this.componentFactoryResolver.resolveComponentFactory(TaskDetailComponent); const componentRef = this.virtualContainer.createComponent/code>TaskDetailComponent<code class="erlang plain"; (componentRef.instance as TaskDetailComponent).task = task; // 传递参数 }}


代码说明


openTask()方法绑定到模板中按钮的单击事件


导入要动态创建的组件TaskDetailComponent


constructor注入injector、componentFactoryResolver 动态创建组件需要的对象,只有在组件上下文中才可以拿到这些实例对象


使用api创建组件,现根据组件类型创建一个ComponentFactory对象,然后调用viewContainer的createComponent创建组件


使用componentRef.instance获取创建的组件实例,这里用来设置组件的task属性值


其它


ViewContainerRef除了createComponent方法外还有一个createEmbeddedView方法,用于创建模板


?123@ViewChild('customTemplate')customTemplate: TemplateRef;this.virtualContainer.createEmbeddedView(this.customTemplate, { name: 'pubuzhixing' });


createEmbeddedView方法的第二个参数,用于指定模板的上下文参数,看下模板定义及如何使用参数


?123

自定义模板,传入参数name:{ {name}}


此外还可以通过ngTemplateOutlet直接插入内嵌视图模板,通过ngTemplateOutletContext指定模板的上下文参数


?1

小结


分析下Angular动态创建组件/内嵌视图的API,动态创建组件首先需要一个被创建的组件定义或模板声明,另外需要Angular上下文的环境来提供这个组件渲染在那里以及这个组件的依赖从那获取,viewContainerRef是动态组件的插入位置并且提供组件的逻辑范围,此外还需要单独传入依赖注入器injector,示例直接使用逻辑容器的injector,是不是很好理解。


示例仓储:


CDK Portal 官方文档介绍


这里先对Portal相关的内容做一个简单的说明,后面会有两个使用示例,本来这块内容准备放到最后的,最终还是决定放在前面,可以先对Portals有一个简单的了解,如果其中有翻译不准确请见谅。


地址:


-------- 文档开始


portals 提供渲染动态内容到应用的可伸缩的实现,其实就是封装了Angular动态创建组件的过程


Portals


这个Portal指是能动态渲染一个指定位置的 UI块 到页面中的一个 open slot 。


UI块 指需要被动态渲染的内容,可以是一个组件或者是一个模板,而 open slot 是一个叫做PortalOutlet的开放的占位区域。


Portals和PortalOutlets是其它概念中的低级的构造块,像overlays就是在它基础上构建的


?1Portal 包括动态组件的抽象类,可以是TemplatePortal(模板)或者ComponentPortal(组件)


方法描述


attach(PortalOutlet): T


把当前Portal附加到宿主上


detach(): void


把Portal从宿主上拆离


isAttached://代码效果参考:http://www.jhylw.com.cn/160723361.html

boolean

当前Portal是否已经附加到宿主上


?1PortalOutlet 动态组件的宿主


方法描述


attach(Portal): any


附加指定Portal


detach(): any


拆离当前附加Portal


dispose(): void


永久释放宿主资源


hasAttached: boolean


当前是否已经装在Portal


代码片段说明


CdkPortal


?12345678

The content of this template is captured by the portal.




可以通过ViewChild、ViewChildren获取到该Portal,类型应该是CdkPortal,如下所示:


?12// 模板中的Portal@ViewChild(CdkPortal) templateCDKPortal: TemplatePortal;


ComponentPortal


组件类型的Portal,需要当前组件在NgModule的entryComponents中配置才能动态创建该组件。


?1this.userSettingsPortal = new ComponentPortal(UserSettingsComponent);


CdkPortalOutlet


使用指令可以把portal outlet添加到一个ng-template,cdkPortalOutlet把当前元素指定为PortalOutlet,下面代码把userSettingsPortal绑到此portal-outlet上


?12

cdkPortalOutlet】="userSettingsPortal"

----- 文档完毕


Portals使用示例


这里首先使用新的api完成和最上面示例一样的需求,在同样的位置动态渲染TaskDetailComponent组件。


第一步


同样是设置一个宿主元素用于渲染动态组件,可以使用指令cdkPortalOutlet挂载一个PortalOutlet在这个ng-container元素上


?1234[/code>div class="portals-outlet"

第二步


与 使用Angular API动态创建组件 一节使用同一个逻辑元素作为宿主,只不过这里的获取容器的类型是CdkPortalOutlet,代码如下


?12@ViewChild('virtualContainer', { read: CdkPortalOutlet })virtualPotalOutlet: CdkPortalOutlet;


第三步


创建一个ComponentPortal类型的Portal,并且将它附加上面获取的宿主virtualPotalOutlet上,代码如下


?12345678portalOpenTask() { this.virtualPotalOutlet.detach(); const taskDetailCompoentPortal = new ComponentPortal/code>TaskDetailComponent<code class="erlang plain"; const ref = this.virtualPotalOutlet.attach(taskDetailCompoentPortal); // 此处同样可以 通过ref.instance传递task参数}


小结


这里是使用ComponentPortal的示例实现动态创建组件,Portal还有一个子类TemplatePortal是针对模板实现的,上节 CDK Portal 官方文档介绍 中有介绍,这里就不在赘述了。总之使用Portals可以很大程度上简化代码逻辑。


示例仓储:


Portals 源码分析


上面只是使用Portal的最简单用法,下面讨论下它的

相关文章
|
2月前
|
JavaScript
Angular使用@Input和@Output实现父子组件互相传参(类似Vue的props和this.emit)
Angular使用@Input和@Output实现父子组件互相传参(类似Vue的props和this.emit)
Angular 应用里异步打开对话框的技术实现
Angular 应用里异步打开对话框的技术实现
|
2月前
|
前端开发 JavaScript 开发者
【专栏:HTML与CSS前端技术趋势篇】前端框架(React/Vue/Angular)与HTML/CSS的结合使用
【4月更文挑战第30天】前端框架React、Vue和Angular助力UI开发,通过组件化、状态管理和虚拟DOM提升效率。这些框架与HTML/CSS结合,使用模板语法、样式管理及组件化思想。未来趋势包括框架简化、Web组件标准采用和CSS在框架中角色的演变。开发者需紧跟技术发展,掌握新工具,提升开发效能。
|
2月前
|
JavaScript 前端开发 开发者
【TypeScript技术专栏】TypeScript在Angular开发中的应用
【4月更文挑战第30天】本文探讨了TypeScript在Angular开发中的应用。Angular与TypeScript的结合利用了静态类型检查和ECMAScript特性,简化了大型Web应用的开发。文章涵盖组件、数据绑定、依赖注入、服务、守卫和路由以及模块化等方面,展示了如何在Angular中有效使用TypeScript。此外,还提到了TypeScript的高级应用,如泛型、高级类型和装饰器。掌握这些知识将有助于提升Angular应用的可维护性和可扩展性。
|
2月前
快速创建Angular组件并定义传参、绑定自定义事件的方法
快速创建Angular组件并定义传参、绑定自定义事件的方法
|
2月前
Angular多个页面引入同一个组件报错The Component ‘MyComponentComponent‘ is declared by more than one NgModule怎么办?
Angular多个页面引入同一个组件报错The Component ‘MyComponentComponent‘ is declared by more than one NgModule怎么办?
|
2月前
|
前端开发 JavaScript
Angular 组件模版代码里使用 ngIf 进行条件渲染的例子
Angular 组件模版代码里使用 ngIf 进行条件渲染的例子
|
9月前
|
UED
Angular 中的 code splitting 和 lazy loading 技术
Angular 中的 code splitting 和 lazy loading 技术
|
9月前
|
前端开发 JavaScript UED
Angular dynamic import 技术详解
Angular dynamic import 技术详解
|
9月前
|
开发框架 前端开发 UED
Angular 中懒加载模块初始化技术详解
Angular 中懒加载模块初始化技术详解