我正在为我的个人需求和乐趣开发一个小的UI组件框架 . 我正在开发一个Tab组件并且为了测试目的,我需要在另一个组件(TabComponent)中动态注入一个组件(TabContainerComponent) . 下面是我的两个组件的代码:
tab.component.ts:
import {Component, ContentChildren} from "@angular/core";
import {TabContainerComponent} from "./tabContainer.component";
@Component({
selector: 'tab',
templateUrl: 'tab.component.html'
})
export class TabComponent {
@ContentChildren(TabContainerComponent)
tabs: TabContainerComponent[];
}
tab.component.html:
<ul>
<li *ngFor="let tab of tabs">{{ tab.title }}</li>
</ul>
<div>
<div *ngFor="let tab of tabs">
<ng-container *ngTemplateOutlet="tab.template"></ng-container>
</div>
<ng-content></ng-content>
</div>
tabContainer.component.ts:
import {Component, Input} from "@angular/core";
@Component({
selector: 'tab-container',
template: '<ng-container></ng-container>'
})
export class TabContainerComponent {
@Input()
title: string;
@Input()
template;
}
我使用ComponentFactoryResolver和ComponentFactory动态创建我的新组件(TabContainerComponent),并在addTab方法中将其注入我的其他组件(TabContainer)中的占位符:
app.component.ts :
import {
Component, ViewChild, ComponentFactoryResolver, ComponentFactory,
ComponentRef, TemplateRef, ViewContainerRef
} from '@angular/core';
import {TabContainerComponent} from "./tabContainer.component";
import {TabComponent} from "./tab.component";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
@ViewChild(TabComponent)
tab: TabComponent;
@ViewChild('tabsPlaceholder', {read: ViewContainerRef})
public tabsPlaceholder: ViewContainerRef;
@ViewChild('newTab')
newTab: TemplateRef<any>;
constructor(private resolver: ComponentFactoryResolver) {
}
addTab(): void {
let factory: ComponentFactory<TabContainerComponent> = this.resolver.resolveComponentFactory(TabContainerComponent);
let tab: ComponentRef<TabContainerComponent> = this.tabsPlaceholder.createComponent(factory);
tab.instance.title = "New tab";
tab.instance.template = this.newTab;
console.log('addTab() triggered');
}
}
点击“添加标签”按钮即可触发addMethod:
app.component.html:
<button (click)="addTab()">Add tab</button>
<tab>
<tab-container title="Tab 1" [template]="tab1"></tab-container>
<tab-container title="Tab 2" [template]="tab2"></tab-container>
<ng-container #tabsPlaceholder></ng-container>
</tab>
<ng-template #tab1>T1 template</ng-template>
<ng-template #tab2>T2 template</ng-template>
<ng-template #newTab>
This is a new tab
</ng-template>
app.module.ts :
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import {TabContainerComponent} from "./tabContainer.component";
import {TabComponent} from "./tab.component";
@NgModule({
declarations: [
AppComponent,
TabComponent,
TabContainerComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent],
entryComponents: [
TabContainerComponent
]
})
export class AppModule { }
当我点击“添加标签”按钮时,我能够看到console.log消息,我能够看到一个新的<tab-container>标签(但没有任何属性,这很奇怪) tab> tag但是Angular不更新Tab组件视图(没有创建<li>和<div>) . 我还尝试通过在TabComponent类中实现OnChanges接口来检查更改,但没有成功 .
有人有想法解决我的问题吗?
P.S . :我不想使用TabContainer组件数组来测试createComponent方法 .
Update:
演示:
https://stackblitz.com/edit/angular-imeh71?embed=1&file=src/app/app.component.ts
2 回答
这是我可以使它工作的方式 -
Tab.component.ts
将TabContainerComponent数组中的“tabs”更改为QueryList .
在 app.component.html 中添加了新模板
app.component.ts
为TabContainerComponent添加了ViewChildren查询 . 在addTab()中使用createEmbeddedView添加新的选项卡容器组件 .
我认为TabComponent中的“ContentChildren”查询应该由新添加的组件更新,但事实并非如此 . 我试图在TabCompoent中订阅查询列表的“更改”,但它不会被触发 .
但我观察到,每次添加新组件时,AppComponent中的“ViewChildren”查询都会更新 . 所以我已经将app组件的更新QueryList分配给了TabComponent的QueryList .
工作演示可用here
我修改了你的代码检查它正如你所期望的那样在视图中显示 .
Stackblitz演示
对于动态组件创建您需要使用templateRef创建嵌入视图 .
有关动态组件操作的更多信息,请查看:https://blog.angularindepth.com/working-with-dom-in-angular-unexpected-consequences-and-optimization-techniques-682ac09f6866