Angular-自定义元素标签和动态组件

1、自定义元素

答:即浏览器允许用户自定义标签来扩展HTML,也称之为web Component,当将自定义标签添加到CustomElementRegistry(自定义元素注册表)中之后就会被浏览器识别,可在HTML的任何位置使用。在angular中可以通过自定义元素标签将angular组件(包括变更检测机制和数据绑定功能)插入到不受angular控制的HTML内容中,可用于替代动态组件

2、如何将angular组件转换为自定义元素

答:

a、使用angular内置模块@angular/el ements中的createCustomElement(ComponentClass)方法将传入的组件类转换为NgElementConstructor类型的构造器类

b、使用浏览器内置的customElements.define()方法,将自定义标签名和第一步根据angular组件生成的自定义元素标签构造器类一同传入其中,完成向浏览器的CustomElementRegistry(自定义元素注册表)中注册此自定义元素标签的过程

c、在html中使用此自定义元素标签时,浏览器会使用此标签对应的构造器类创建一个自定义元素标签的实例

注意:angular组件中的输入和输出属性对应到自定义元素标签上就是Attribute和浏览器的自定义事件,且自定义输出属性传递的数据被存放在自定义事件的事件对象中的detail属性中,并且保留着变更检测和数据绑定的功能,同时需要在angular的根模块的@NgModule装饰器对象属性entryComponent中添加此组件

3、为自定义元素标签提供类型支持

答:使用在angular的@angular/elements模块中提供的NgElement和WithProperties<T>两个类型。

NgElement:是对HTMLElement类型的扩展

WithProperties<T>:来说明angular组件中定义的其它输入和输出属性

有两种方式提供:

a、直接在使用的地方提供:

如:document.createElement('my-custom-tag') as NgElement & WithProperties<{inputAttr: string}>

b、创建一个扩展HTMlElementTagNameMap接口的类型文件,用来集中声明自定义元素标签的类型

如:

declare global {

interface HTMLElementTagNameMap {

'my-custom-tag': NgElement & WithProperties<{inputAttr: string}>

}

}

4、动态组件

答:即在应用程序运行期间根据需要动态加载的一类组件,需要提前添加到根模块的@NgModule装饰器对象的entryComponent属性中

5、创建及使用动态组件

答:有两种方式创建并使用动态组件:

> 通过angular内置的ViewContainerRef类的createComponent()方法创建动态组件,该种方式适用于angular13版本之后,具体步骤:

a、将需要作为动态组件使用的组件添加到跟模块的@NgModule装饰配置对象的entryComponent属性中

b、在需要使用动态组件的模板中指定插入动态组件的位置,如:

<ng-template #dynamicComContainer></ng-template>

c、在需要使用动态组件的组件类中使用@ViewChild装饰器获取插入位置的视图容器引用,如:

@ViewChild(ViewContainerRef) viewRef !: ViewContainerRef;

d、使用ViewContainerRef类中提供的createComponent方法创建组件引用并向视图容器中插入该组件,

且createComponent方法会返回一个ComponentRef,即所创建的组件实例的引用,通过该引用可以实现与动态组件之间的交互

> 通过angular内置的ComponentFactoryResolver类的resolveComponentFactory()方法创建对应组件类型的工厂类ComponentFactory,使用该工厂类的create方法创建该类型的组件,并在此方法的第三个参数指定插入该组件实例的页面位置(即一个dom标签元素,也可以是自定义标签元素),然后将该dom元素插入到DOM中即可。此种方式适用于angular13之前的版本,具体步骤如下:

a、通过const customEle = document.createElement('my-tag')创建一个用于插入组件视图的元素

b、通过ComponentFactoryResolver类提供的resolveComponentFactory(comClass)方法传入组件类,以解析出该组件的组件工厂类ComponentFactory

c、使用上一步解析出的工厂类ComponentFactory的create(this.injector, [], customEle)方法,并在该方法的第三个参数中传入第一步创建的好的自定义元素,以完成组件的动态创建以及插入到指定的dom元素中(此处是customEle标签元素中)

d、将第一步创建的标签元素添加到HTML中的指定位置即可:body.append(customEle);

注意:工厂类ComponentFactory的create方法返回当前创建的组件实例的引用ComponentRef,可就此引用来和该组件进行交互和数据传递

注意:在@ContentChild和@ViewChild的第二个参数对象中static为true时可以在ngOnInit钩子中访问到定义的变量,为false时只能在对应的ngAfterContentInit和ngAfterViewInit钩子中查询到值