【问题标题】:Loading dynamically created components in order按顺序加载动态创建的组件
【发布时间】:2016-12-20 12:13:50
【问题描述】:

我正在尝试使用以下代码实现动态组件-

createWidget(template: string) {
    const html = template;
    if (!html) return;

    if (this.cmpRef) {
        this.cmpRef.destroy();
    }
    const compMetadata = new Component({
        selector: 'dynamic-html',
        template: html
    });
    this.createComponentFactory( this.compiler, compMetadata )
        .then(factory => {
            const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
            console.log("Injector: " + injector);
            this.vcRef.createComponent(factory, 0, injector, []);
            console.log( "Create Widget End.." );
        });
} 

createComponentFactory( compiler: Compiler, metadata: Component) {

    @Component( metadata )
    class DynamicComponent { };
    @NgModule({
        imports: [ CommonModule, FormsModule ],
        declarations: [ DynamicComponent ]
    })
    class DynamicHtmlModule { }
    return compiler.compileModuleAndAllComponentsAsync( DynamicHtmlModule )
        .then(( moduleWithComponentFactory: ModuleWithComponentFactories<any> ) => {
            return moduleWithComponentFactory.componentFactories.find( x => x.componentType === DynamicComponent );
        });
}

当我调用 createWidget 方法时,代码运行良好-

this.createWidget('Angular2');

但问题是,当我使用 for 循环多次调用 createWidget 时,生成的模板是反向排序的。请看我的插件https://plnkr.co/edit/hqKYsGlyuk78tg61zjfd?p=preview

在这个 plunker 示例中,Angular2 显示在 Angular1 之前。

【问题讨论】:

    标签: angular


    【解决方案1】:

    你有两个选择:

    1- 异步创建模块,您已经在这样做了,为了确保您的组件以与您的 for 循环相同的顺序出现在 DOM 中,您需要使用您的像这样的循环索引:

    for(var i = 1; i <= 2; i++) {
          this.createWidget('<h1>Angular' + i + '</h1>' , i-1);  // minus 1 because you're starting from 1  !!!!!
        }
    
    
    createWidget(template: string , index) {
            const html = template;
            if (!html) return;
    
            if (this.cmpRef) {
                this.cmpRef.destroy();
            }
            const compMetadata = new Component({
                selector: 'dynamic-html',
                template: html
            });
            this.createComponentFactory( this.compiler, compMetadata )
                .then(factory => {
                    const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
                   this.vcRef.createComponent(factory, index, injector, []);
                });
        } 
    

    因为循环内部有一个异步函数( createComponentFactory ),所以你永远不能保证第一个组件总是首先创建,第二个是第二个,因为第一个的编译可能需要超过第二个,然后第二个会被解决首先。

    对于这种情况,传递索引并确保将它们放在与 for 循环相同的顺序中会更加具体和可信。

    这是你的 plunker,工作正常。

    https://plnkr.co/edit/VmmwzzrcJq6En5tXcyl6?p=preview

    2-同步创建模块,然后您可以相信它们将始终按顺序显示,而无需自己定义索引。

    export class App {
      name:string;
      constructor(public compiler: Compiler, public vcRef: ViewContainerRef) {
        this.name = 'Angular2';
        for(var i = 1; i <= 2; i++) {
          this.createWidget('<h1>Angular' + i + '</h1>');  
        }
    
      }
    
      createWidget(template: string ) {
            const html = template;
            if (!html) return;
    
            if (this.cmpRef) {
                this.cmpRef.destroy();
            }
            const compMetadata = new Component({
                selector: 'dynamic-html',
                template: html
            });
            let factory = this.createComponentFactory( this.compiler, compMetadata )
            const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
                   this.vcRef.createComponent(factory, undefined , injector, []);
    
        } 
    
        createComponentFactory( compiler: Compiler, metadata: Component ) {
            @Component( metadata )
            class DynamicComponent { };
            @NgModule({
                imports: [ CommonModule ],
                declarations: [ DynamicComponent ]
            })
            class DynamicHtmlModule { }
            let moduleWithComponentFactory = compiler.compileModuleAndAllComponentsSync( DynamicHtmlModule )
                console.log(moduleWithComponentFactory.componentFactories)
                    return moduleWithComponentFactory.componentFactories.find( x => x.componentType === DynamicComponent );
    
        }
    
    }
    

    https://plnkr.co/edit/9WNrVABMupq1q2YKO5wi?p=preview

    【讨论】:

    • 我不知道您在哪里确保按顺序创建组件。
    • 您的解决方案曾经像魅力一样工作,但由于某种原因,在包更新后它坏了。我认为这发生在 Angular 2.4.4 之后并且仅在使用 AoT 编译时发生。我现在得到:没有为“DynamicHtmlModule”错误找到 NgModule 元数据。使用角度 cli。有什么建议吗?
    【解决方案2】:

    您可以尝试为vcRef.createComponent 方法计算index,例如

    loadedIndices: number[] = [];
    ...
    let renderedIndex = this.loadedIndices.indexOf(Math.max.apply(Math, this.loadedIndices.filter(x => x <= index))) + 1;
    
    this.vcRef.createComponent(factory, renderedIndex, injector, []);
    
    this.loadedIndices.push(index);
    this.loadedIndices.sort((a, b) => a - b);
    

    index 是每个组件的目标索引

    Plunker Example

    【讨论】:

      【解决方案3】:

      只需将0 删除

      this.vcRef.createComponent(factory, 0, injector, []);
      

      这使得每个组件都被插入到位置 0(在所有其他组件之前)。

      没有显式索引,每个组件都附加在之前添加的组件之后:

      this.vcRef.createComponent(factory, {injector: injector, projectableNodes: []});
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-09-16
        • 1970-01-01
        • 2021-09-11
        • 1970-01-01
        • 1970-01-01
        • 2017-12-13
        • 1970-01-01
        • 2023-04-10
        相关资源
        最近更新 更多