我正在尝试构建信息面板。直到布局是静态的,一切都运行良好,但是当我尝试使布局动态化(用户可以定义)时,在创建的组件中未调用ngOnInit
。
我有两个主要组成部分,一个是用户可以选择要查看其建筑的建筑物(SelectBoardComponent
,另一个是包含该建筑本身的建筑物(BoardComponent
)。当我从SelectBoardComponent
导航到BoardComponent
时,一切正常。但是,当我重新加载或直接访问BoardComponent
的路径时,不会在动态添加的子级中触发整个Angular周期(OnInit ....)。
我已在stackblitz
上转载了此问题为了能够找出要创建的组件,我创建了一个“组件注册表”,在其中注册了可以动态呈现的每个组件。
import {Type} from '@angular/core';
type ComponentClass = Type<any>;
const REGISTRY = new Map<string, ComponentClass>();
export const getTypeFor = (name: string): ComponentClass => {
return REGISTRY.get(name);
};
export const Register = () => {
return (cls: ComponentClass) => {
REGISTRY.set(cls.name, cls);
};
};
所以我注册了InfoCardComponent
我要渲染的组件之一
import { Component, OnInit } from '@angular/core';
import {Register} from '../register';
@Component({
selector: 'app-info-card',
templateUrl: './info-card.component.html',
styleUrls: ['./info-card.component.css']
})
@Register()
export class InfoCardComponent implements OnInit {
public called = "not called!";
constructor() { }
ngOnInit() {
this.called = "called!";
}
}
在BoardComponent
中,向要显示其内部组件的元素添加appRender
指令
appRender
处理生成组件的过程。
我根据JSON数据渲染组件。
private components = [
{
"id": 0,
"position": 0,
"cells": [
{
"id": "info",
"title": "Info panel",
"component": "InfoCardComponent",
"childs": []
}
]
}
];
private componentsRef = {};
constructor(
private appRef: ApplicationRef,
private injector: Injector,
private componentFactory: ComponentFactoryResolver,
private renderer: Renderer2,
private el: ElementRef) {
}
ngOnInit() {
this.el.nativeElement.innerHTML = '';
const {rows} = this.render();
for (const row of rows) {
this.renderer.appendChild(this.el.nativeElement, row);
}
}
render() {
const grid = {rows: []};
for (const c of this.components) {
const cells = c.cells;
if (cells.length > 0) {
const row = this.renderer.createElement('div');
for (const {component} of cells) {
const cell = this.renderer.createElement('div');
const card = this.renderer.createElement('div');
this.renderer.appendChild(card, this.createAngularComponent(component).elem);
this.renderer.appendChild(cell, card);
this.renderer.appendChild(row, cell);
}
grid.rows.push(row);
}
}
return grid;
}
private createAngularComponent(parentComponent) {
const componentElem = this.componentFactory.resolveComponentFactory(getTypeFor(parentComponent)).create(this.injector);
this.componentsRef[parentComponent] = componentElem;
this.appRef.attachView(componentElem.hostView);
return {
elem: (componentElem.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement,
};
}
这是预期的行为还是我忘记了什么?
我很乐意提供帮助。
答案 0 :(得分:2)
您遇到LifeCycleHook问题。您无法访问ngOnInit()
中的ElementRef和TemplateRef,因为尚未渲染模板。您应该使用ngAfterViewInit()
LifeCycleHook。将您的逻辑从ngOnInit()
移到ngAfterViewInit()
。
做什么:
将ngOnInit()
更改为ngAfterViewInit()
:
ngAfterViewInit() {
setTimeout(() => {
this.el.nativeElement.innerHTML = '';
const {rows} = this.render();
for (const row of rows) {
this.renderer.appendChild(this.el.nativeElement, row);
}
}, 0)
}
setTimeout()
用于修复ExpressionHasBeenChanged-Error。
不要忘记实现AfterViewInit
并将其从@angular/core
导入