按顺序加载动态创建的组件

时间:2016-12-20 12:13:50

标签: angular

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

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(&#39; Angular2&#39);

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

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

3 个答案:

答案 0 :(得分:2)

您有两种选择:

1 - 创建模块Asyncrounously,你已经在做了,为了确保你的组件以与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循环的相同顺序中。

这是你的傻瓜,工作正常。

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

只需从

中删除0即可
this.vcRef.createComponent(factory, 0, injector, []);

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

如果没有显式索引,则在添加之前添加每个组件:

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