J'ai déjà introduit l'insertion de vue dynamique de l'API native, et la fonction peut déjà répondre à la plupart des scénarios d'utilisation. Cependant, il présente certaines lacunes. Il n'a pas encore résolu le besoin d'insérer du contenu en dehors de l'application Angular, et les instructions ne peuvent pas avoir d'interactions d'entrée et de sortie avec des composants insérés dynamiquement.
Heureusement, Angular fournit officiellement un ensemble de kits de développement de composants, Component Dev Kit (CDK), comme outil de base pour le développement de divers composants Angular, y compris "Portal" pour aider à la création de vues dynamiques.
Cette "vue dynamique" peut être un composant, TemplateRef ou un élément DOM, correspondant respectivement à trois types de Portal (ComponentPortal, TemplatePortal, DomPortal). La classe de base générique abstraite des trois d'entre eux est Portal<T>
, qui a trois méthodes : attach (monter sur le conteneur), detach (supprimer du conteneur), isAttached (déterminer si la vue est monté). Portal<T>
,有三个方法:attach(挂载到容器)、detach(从容器移除)、isAttached(判断视图是否是挂载状态)。
而容器也是由一个抽象类 BasePortalOutlet
定义,和视图类似,包含 attach(给容器挂载视图)、detach(从容器移除视图)、dispose(销毁容器)、isAttached(是否有挂载视图)。它的主要实现是 DomPortalOutlet
类。用以挂载三种类型的动态视图。
先来看看三种动态视图的创建。
ComponentPortal
相比原生 API,要创建一个动态组件非常的简单,只需要把组件类传入 ComponentPortal
构造函数即可。
this.componentPortal = new ComponentPortal(ExampleComponent);
可以传入任意自定义的组件类,用以创建 ComponentPortal
对象,再动态插入视图中。
✨注意:Angular 9 后的版本推荐使用 Ivy 编译器,如果是老版本编译器,传入的组件类,需要在 Module 的 entryComponents
中声明,并且这个 Module 不能懒加载。
TemplatePortal
TemplatePortal 的构建,相比组件,多了一个参数(ViewContainerRef)。看过前一篇应该对它非常熟悉了,需要依赖它调用 createEmbeddedView()
来创建嵌入视图。这里通过构造注入,直接使用当前组件的 ViewContainerRef
实例。
<ng-template #testTemplate> <p>一些需要动态插入的内容.</p> </ng-template>
@ViewChild('testTemplate') templatePortalContent: TemplateRef<any>; constructor(private _viewContainerRef: ViewContainerRef) { } ngAfterViewInit() { this.templatePortal = new TemplatePortal( this.templatePortalContent, this._viewContainerRef ); }
除了通过构造函数,TemplatePortal 也有一个指令(CdkPortal)可以便捷创建。
<ng-template cdkPortal> <p>一些需要动态插入的内容.</p> </ng-template> <!-- 或写作 --> <!-- 和上面写法是一致的效果 --> <p *cdkPortal> 一些需要动态插入的内容. </p>
然后通过 @ViewChild
就可以获得 TemplatePortal
的实例了。
DomPortal
就像上面的示例通过 @ViewChild
获取 Template 实例来创建,类似的也可以获取 ElementRef 来创建动态的 DOM。
<div #domPortalContent><span>原生DOM内容</span></div>
@ViewChild('domPortalContent') domPortalContent: ElementRef<HTMLElement>; ngAfterViewInit() { this.domPortal = new DomPortal(this.domPortalContent); }
可以动态的将这段 DOM 转移到任意位置。要注意的是,转移之后,原来的数据绑定,或者绑定的指令可能不会再继续更新。
前面三种类型的 Portal 都说了可以渲染到任意位置,那具体怎么渲染呢?
CdkPortOutlet
最简单的就是通过 CdkPortOutlet 指令了:
<div> <ng-template [cdkPortalOutlet]="anyPortal"></ng-template> </div>
给 anyPortal
传值上面三个中任意的 Portal 实例,都会动态渲染到当前位置。
和原生 API 的指令不同,它可以自动判断是什么类型的 Portal。另外,它还有个额外的事件:attached
,通过这个事件,可以获取到挂载的组件实例,或者 TemplateRef。这也让和挂载组件的交互变得十分方便了。
构造容器实例
不过既然说了是可以渲染到任意位置,那自然也包括 Angular 应用外部,要渲染到应用之外,就需要咱们通过构造函数创建容器实例。
这个容器类就是 DomPortalOutlet
,它是 PortalOutlet
BasePortalOutlet
, qui est similaire à la vue et comprend attach (monter la vue sur le conteneur), détacher (supprimer la vue du conteneur), disposer ( détruire le conteneur), isAttached (S'il existe une vue montée). Sa principale implémentation est la classe DomPortalOutlet
. Utilisé pour monter trois types de vues dynamiques. Créer du contenu dynamique
Examinons d'abord la création de trois vues dynamiques.constructor( private viewContainerRef: ViewContainerRef, @Inject(DOCUMENT) private document: any, private injector: Injector, private componentFactoryResolver: ComponentFactoryResolver ) { // 在<body>下创建外部宿主元素 const container = this.document.createElement('div'); container.classList.add('outside-portal-container'); this.outsideContainer = this.document.body.appendChild(container); // 获取应用实例 this.appRef = this.injector.get(ApplicationRef); // 创建外部容器 this.outsideOutlet = new DomPortalOutlet( this.outsideContainer, this.componentFactoryResolver, this.appRef, this.injector ); } // 在应用外部插入动态组件。 openComponentPortalOutSideAngularContext(): void { const componentPortal = new ComponentPortal(AlertComponent); const componentRef = this.outsideOutlet.attach(componentPortal); componentRef.instance.closeAlert.subscribe(() => { this.outsideOutlet.detach(); }); } // 在应用外部插入动态模板。 openTemplatePortalInsideAngularContext(): void { const templatePortal = new TemplatePortal(this.templatePortalContent, this.viewContainerRef); this.outsideOutlet.attach(templatePortal); }
ComponentPortal
, puis l'insérer dynamiquement dans la vue. entryComponents
du Module, et ce module ne peut pas être chargé paresseux. 🎜TemplatePortal🎜🎜🎜TemplatePortal a un paramètre de plus (ViewContainerRef) que le composant. Vous devriez le connaître très bien après avoir lu l'article précédent. Vous devez vous y fier pour appeler createEmbeddedView()
afin de créer une vue intégrée. Ici, grâce à l'injection de constructeur, l'instance ViewContainerRef
du composant actuel est directement utilisée. 🎜const injectionToken = new InjectionToken<any>('Sharing data with outside component portal'); const customInjector = Injector.create({ providers: [{ provide: CustomInjectionToken, useValue: 'test value' }] });
// 重点是第四个参数 new DomPortalOutlet(this.outsideContainer, this.componentFactoryResolver, this.appRef, customInjector);
constructor(@Inject(injectionToken) public customData: any) {}
TemplatePortal
via @ViewChild
. 🎜🎜🎜DomPortal🎜🎜🎜Tout comme l'exemple ci-dessus, obtenez l'instance de modèle via @ViewChild
pour la créer, vous pouvez obtenez-le également de la même manière ElementRef pour créer un DOM dynamique. 🎜// 重点是第三个参数 new TemplatePortal(this.templatePortalContent, this.viewContainerRef, { customData:'test values' });
anyPortal Toute instance de portail sera rendue dynamiquement à l'emplacement actuel. 🎜🎜Différent des commandes API natives, il peut déterminer automatiquement de quel type de portail il s'agit. De plus, il a un événement supplémentaire : <code>attad
Grâce à cet événement, vous pouvez obtenir l'instance de composant montée ou TemplateRef. Cela rend également l'interaction avec les composants montés très pratique. 🎜🎜🎜Construire une instance de conteneur🎜🎜🎜Mais comme on dit qu'elle peut être rendue dans n'importe quelle position, elle inclut naturellement également en dehors de l'application Angular. rendu dans l'application De plus, nous devons créer une instance de conteneur via le constructeur. 🎜🎜Cette classe conteneur est DomPortalOutlet
, qui est une sous-classe d'implémentation de PortalOutlet
. Ses paramètres de construction sont principalement : Element (le nœud DOM de la vue montée), ComponentFactoryResolver (identique à l'article précédent, utilisé pour construire dynamiquement des composants), appRef (l'instance globale de l'application Angular actuelle), Injector (l'injecteur, utilisé pour passer les dépendances). 🎜rrreee🎜En plus de monter la vue dans un élément DOM en dehors de l'application, elle doit également pouvoir interagir avec la vue. Les composants peuvent injecter des dépendances et les modèles peuvent transmettre des objets contextuels. 🎜rrreee🎜Modifiez légèrement le code qui crée outsideContainer et passez le customInjector en paramètre (au lieu d'utiliser l'injecteur du composant actuel)🎜rrreee🎜En conséquence, ce composant n'a besoin d'injecter des dépendances qu'en fonction de cette injectionToken :🎜rrreee🎜Give Lors de la création d'un objet TemplatePortal, il suffit de transmettre l'objet de contexte : 🎜rrreee🎜🎜Résumé🎜🎜🎜Par rapport à l'API native, le portail CDK implémente principalement : 🎜🎜🎜🎜Insertion dynamique de vues en dehors de l'application. capacité; 🎜La possibilité d'interagir avec les données de visualisation insérées à l'extérieur ;
Des commandes plus pratiques et flexibles.
Grâce à lui, il devient plus facile de créer des conteneurs de composants dynamiques, des fenêtres contextuelles, des menus flottants ou même de créer une plateforme de conception low-code.
Code source du projet : https://github.com/locotor/angular-dynamic-view-example
Exemple en ligne : https://coding-pages-bucket-1575455-8137703-14801-541995-1303365836.cos - website.ap-beijing.myqcloud.com/
Pour plus de connaissances sur la programmation, veuillez visiter : Introduction à la programmation ! !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!