Angular通过指令动态添加组件问题
之前自己写的公共组件,都是会先引入,需要调起的时候再通过service控制公共组件状态、值、回调函数什么的。但是有一些场景不适合这种方式,还是动态添加组件更加好。通过写过的一个小组件来总结下。
创建组件
场景:鼠标移动到图标上时,展示解释性的说明文字。那就需要创建一个普通的tooltip组件。如下:
<aside class="hover-tip-wrapper"> <span>{{tiptext}}</span> </aside>
import { component, oninit } from '@angular/core'; @component({ selector: 'app-hovertip', templateurl: './hovertip.component.html', styleurls: ['./hovertip.component.scss'] }) export class hovertipcomponent implements oninit { public tiptext: string; constructor() { } ngoninit() { } }
.hover-tip-wrapper{ width: max-content; position: absolute; height: 30px; line-height: 30px; bottom: calc(100% + 5px); right: calc( -10px - 100%); background-color: rgba(#000000,.8); padding: 0 5px; border-radius: 3px; &::after{ content: ''; position: absolute; height: 0; width: 0; border: 4px solid transparent; border-top-color: rgba(#000000,.8); left: 10px; top: 100%; } span { color: #ccc; font-size: 12px; } }
非常简单的一个组件,tiptext来接收需要展示的文字。
需要注意的是,声明组件的时候,除了需要添加到declarations中外,还记得要添加到entrycomponents中。
entrycomponents: [hovertipcomponent], declarations: [hovertipcomponent, hovertipdirective]
那entrycomponents这个配置项是做什么的呢?看源码注释,大概意思就是:angular会为此配置项中的组件创建一个componentfactory,并存放在componentfactoryresolver中。动态添加组件时,需要用到组件工厂,所以此配置是必不可少的。
创建指令
通过指令为目标元素绑定事件,控制创建组件、传递tiptext以及组件的销毁。
import { input , directive , viewcontainerref , componentref, componentfactory, hostlistener , componentfactoryresolver} from '@angular/core'; import { hovertipcomponent } from './hovertip.component'; @directive({ selector: '[apphovertip]' }) export class hovertipdirective { public hovertip: componentref<hovertipcomponent>; public factory: componentfactory<hovertipcomponent>; constructor( private viewcontainer: viewcontainerref, private resolver: componentfactoryresolver ) { // 获取对应的组件工厂 this.factory = this.resolver.resolvecomponentfactory(hovertipcomponent); } @input('apphovertip') tiptext: string; // 绑定鼠标移入的事件 @hostlistener('mouseenter') onmouseenter() { // 清空所有的view this.viewcontainer.clear(); // 创建组件 this.hovertip = this.viewcontainer.createcomponent(this.factory); // 向组件实例传递参数 this.hovertip.instance.tiptext = this.tiptext; } // 绑定鼠标移出时的事件 @hostlistener('mouseleave') onmouseleave() { if (this.hovertip) { // 组件销毁 this.hovertip.destroy(); } } }
通过viewcontainerref类来管理视图,这里用到了创建组件。这个 专栏 解释的挺清楚的。这里用到了以下两个api,清除和创建。
createcomponent方法接受componentfactoty类,创建后返回的componentref类,可以获取到组件实例(instance),控制组件销毁。
大致思路是这样的,先获取到了hovertipcomponent组件对于的componentfactory,监听鼠标移入事件,在触发事件时,通过viewcontainerref类来创建组件,存下返回的组件componentref(获取实例,销毁组件时需要用到),向组件实例传递tiptext。监听鼠标移出事件,在事件触发时,销毁组件。
使用
在目标元素是绑定指令,同时传递tiptext即可。
可以正常的创建和销毁。
总结
开始做的时候,主要是对这几个类比较懵,viewcontainerref、componentref、componentfactory、componentfactoryresolver等,看看源码,查查资料,总会梳理清楚的。
参考资料:
上一篇: 详解Angular如何正确的操作DOM