【问题标题】:Angular create Dynamic Component recursivelyAngular递归创建动态组件
【发布时间】:2020-07-03 09:24:53
【问题描述】:

我正在尝试基于 Config 构建一个动态组件。该组件将递归读取配置并创建组件。发现ngAfterViewInit()方法只会被调用两次。

@Component({
    selector: "dynamic-container-component",
    template: `
        <div #container
            draggable="true"
            (dragstart)="dragstart($event)"
            (drop)="drop($event)"
            (dragover)="dragover($event)"
            style="border: 1px solid; min-height: 30px"></div>
    `
})
export default class DynamicContainerComponent {

    @Input()
    dynamicConfig: DynamicConfig;

    @ViewChild("container", {read: ElementRef})
    private elementRef: ElementRef;

    private isContainer: boolean;
    private componentRef: ComponentRef<any>;
    private componentRefs: ComponentRef<any>[] = [];
    
    constructor(
        private componentFactoryResolver: ComponentFactoryResolver,
        private injector: Injector,
        private viewContainer: ViewContainerRef,
        private render: Renderer2
    ){
        console.log("running");
    }

    ngAfterViewInit(){
        
        if (this.dynamicConfig){
            console.log(this.dynamicConfig)
            if (this.dynamicConfig.getType() == ComponentType.INPUT){
                this.isContainer = false;
                let componetFactory: ComponentFactory<InputComponent> = 
                    this.componentFactoryResolver.resolveComponentFactory(InputComponent);
                this.componentRef = this.viewContainer.createComponent(componetFactory);
                this.render.appendChild(this.elementRef.nativeElement, this.componentRef.location.nativeElement);
            }else {
                this.isContainer = true;
                let items: DynamicConfig[] = this.dynamicConfig.getItems();
                if (items){
                    for (var i=0; i<items.length; i++){
                        let item: DynamicConfig = items[i];
                        let componetFactory: ComponentFactory<DynamicContainerComponent> = 
                            this.componentFactoryResolver.resolveComponentFactory(DynamicContainerComponent);
                        let componentRef: ComponentRef<DynamicContainerComponent> = 
                            this.viewContainer.createComponent(componetFactory);
                        componentRef.instance.dynamicConfig = item;
                        this.componentRefs.push(componentRef);
                        this.render.appendChild(this.elementRef.nativeElement, componentRef.location.nativeElement);
                    }
                }
            }
        }else {
            console.log("config does not exist");
        }

    }

    dragstart(event){
        debugger;
    }

    drop(event){
        debugger;
    }

    dragover(event){
        debugger;
        event.preventDefault();
    }


}

组件将由其他组件通过以下代码创建。如果动态组件会通过componentFactoryResolver创建另一个动态组件。

    var configJson = {
        type: ComponentType.CONTAINER,
        items: [
            {
                type: ComponentType.CONTAINER,
                items: [{
                    type: ComponentType.CONTAINER,
                    items: [{
                        type: ComponentType.CONTAINER,
                        items: [{
                            type: ComponentType.INPUT
                        }]
                    }]
                }]
            }
        ]
    }

    this.config = new DynamicConfig();
    this.config.assign(configJson);
    console.log(this.config);

更新 我在 github 中发现了一个类似的问题:https://github.com/angular/angular/issues/10762

我做了其他人建议的事情。但我认为这只是一个肮脏的修复。

ngAfterViewInit(){
    setTimeout(function(){
        if (this.dynamicConfig){
            console.log(this.dynamicConfig)
            if (this.dynamicConfig.getType() == ComponentType.INPUT){
                this.isContainer = false;
                let componetFactory: ComponentFactory<InputComponent> = 
                    this.componentFactoryResolver.resolveComponentFactory(InputComponent);
                this.componentRef = this.viewContainer.createComponent(componetFactory);
                this.render.appendChild(this.elementRef.nativeElement, this.componentRef.location.nativeElement);
            }else {
                this.isContainer = true;
                let items: DynamicConfig[] = this.dynamicConfig.getItems();
                if (items){
                    for (var i=0; i<items.length; i++){
                        let item: DynamicConfig = items[i];
                        let componetFactory: ComponentFactory<DynamicContainerComponent> = 
                            this.componentFactoryResolver.resolveComponentFactory(DynamicContainerComponent);
                        let componentRef: ComponentRef<DynamicContainerComponent> = 
                            this.viewContainer.createComponent(componetFactory);
                        componentRef.instance.dynamicConfig = item;
                        this.componentRefs.push(componentRef);
                        this.render.appendChild(this.elementRef.nativeElement, componentRef.location.nativeElement);
                    }
                }
            }
        }else {
            console.log("config does not exist");
        }
    }.bind(this))
}

【问题讨论】:

    标签: javascript angular typescript


    【解决方案1】:

    当您创建动态组件时,angular 几乎完成了更改检测周期。

    这样你可以运行:

    componentRef.changeDetectorRef.detectChanges()
    

    注意setTimeout 具有类似的效果,但会在整个应用程序上触发更改检测循环

    将生命周期挂钩重命名为 ngOnInit

    另外,您将错误的输入传递给动态组件:

    let item: DynamicConfig = items[i];
              ^^^^^^^^^^^^^
        but it is not DynamicConfig instance but rather plain object
    ...
    componentRef.instance.dynamicConfig = item;
    

    应该是:

    let item: any = items[i];
    const config = new DynamicConfig();
    config.assign(item);
    componentRef.instance.dynamicConfig = config;
    

    Ng-run Example

    【讨论】:

    • 谢谢@yurzui。如果我更改为 ngOnInit,它会起作用。我想问一下为什么 ngOnInit 工作而不是 ngAfterViewInit。 angular.io/guide/lifecycle-hooks
    • 当您渲染动态组件时,它会成为变更检测树的一部分,因此可以通过角度进行检查,但只能在变更检测周期内进行检查。当您在 ngOnInit 钩子中执行此操作时,Angular 有时间在 ngOnInit 和 ngAfterViewInit 之间进行检查,但是如果您在 ngAfterViewInit 中渲染组件,则 Angular 只能在下一个更改检测周期中检查它
    猜你喜欢
    • 2017-09-10
    • 2017-02-24
    • 2018-07-23
    • 2020-11-11
    • 2017-07-07
    • 1970-01-01
    相关资源
    最近更新 更多