我正在尝试编写一个可以动态包含不同组件的组件。我的目标是能够写一篇文章,我可以写一个段落或添加一条推文。
这是DynamicArticleComponent
的代码:
@Directive({
selector: '[dynamic-query]'
})
export class QueryDirective {
constructor(public viewContainerRef: ViewContainerRef) {}
}
@Component({
selector: 'app-dynamic-article',
template:
`<ng-container *ngFor="let element of elements">
<ng-template dynamic-query></ng-template>
</ng-container>`,
styleUrls: ['dynamic-article.component.css']
})
export class DynamicArticleComponent implements AfterViewInit {
@Input() elements: Element[];
@ViewChildren(QueryDirective) queryDirectives;
constructor(private componentFactoryResolver: ComponentFactoryResolver) {}
ngAfterViewInit() {
this.queryDirectives.forEach((queryDirective: QueryDirective, index) => {
const element = this.elements[index];
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(element.component);
const containerRef = queryDirective.viewContainerRef;
containerRef.clear();
const newComponent = containerRef.createComponent(componentFactory);
(<DynamicComponent>newComponent.instance).data = element.data;
});
}
}
这些是上面代码中使用的其他类:
export class Element {
constructor(public component: Type<any>, public data) {}
}
export interface DynamicComponent {
data: any;
}
我在渲染<ng-templates>
时遇到问题。它只是呈现注释,并且在视图加载后不会更改。这就是呈现的内容:
元素正确地进入组件。我的想法是渲染所有模板,然后使用ViewChildren装饰器获取它们,并将元素渲染到它们应该的位置。这个问题有其他解决方案吗?
此外,这是元素到达DynamicArticleComponent
:
提前致谢。
答案 0 :(得分:1)
好的,我的代码存在两个主要问题。第一个非常愚蠢。我没有将指令添加到app模块声明中,因此它就像任何其他html属性一样;棱角刚刚没想到它,所以它没有找到它。但是,在将其添加到app模块后,它会抛出ExpressionChangedAfterItHasBeenCheckedError
。导致此错误的原因是我在视图加载后更改了变量。有关更深入的解释,请查看this blog post。
总而言之,我所做的是将我在ngAfterViewInit
内所做的事情提取到自己的函数中并从promise中调用它。这样做,是在同步代码完成执行后创建一个排队的微任务。要了解有关角度微观和宏观任务的更多信息,请查看以下文章:I reverse-engineered Zones (zone.js) and here is what I’ve found。
以下是代码的结果:
@Directive({
selector: '[dynamic-query]'
})
export class QueryDirective {
constructor(public viewContainerRef: ViewContainerRef) {}
}
@Component({
selector: 'app-dynamic-article',
template:
`<ng-container *ngFor="let element of elements">
<ng-template dynamic-query></ng-template>
</ng-container>`,
styleUrls: ['dynamic-article.component.css']
})
export class DynamicArticleComponent implements AfterViewInit {
@Input() elements: Element[];
@ViewChildren(QueryDirective) queryDirectives;
constructor(private componentFactoryResolver: ComponentFactoryResolver) {}
ngAfterViewInit() {
Promise.resolve(null).then(() => this.renderChildren());
}
private renderChildren() {
this.queryDirectives.forEach((queryDirective: QueryDirective, index) => {
const element = this.elements[index];
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(element.component);
const containerRef = queryDirective.viewContainerRef;
containerRef.clear();
const newComponent = containerRef.createComponent(componentFactory);
(<DynamicComponent>newComponent.instance).data = element.data;
});
}
}
此代码完全有效。希望我帮助别人。