【问题标题】:How to programmatically dynamically create multiple independent instances of component in angular2?如何以编程方式在angular2中动态创建组件的多个独立实例?
【发布时间】:2017-03-12 14:09:23
【问题描述】:

我有一个按钮组件fancy-textbox。我想让它使用户可以动态添加新的fancy-textbox,但是他们在文本框上方有不同的标签,这些标签基于fancy-textbox本身独有的范围变量(或者可能来自父范围变量未在所有fancy-textboxes 之间共享)。我该怎么做呢?目前,我直接在我的模板显示和隐藏中使用它,但我希望能够以编程方式“动态添加更多实例”:

<div *ngIf="showTextBox">
    <fancy-textbox labelForBox="TextBox 1"></fancy-textbox>  
</div>

如果花哨的文本框只在 DOM 中的一个特定区域中创建,那就太好了。但是,我想做的是能够在 DOM 的不同部分动态创建组件。

    <div class="container">
<div class="navsection"><input id="classname" type="text"><button (click)="createFancyButton()">Create Fancy Button</button></div>
    <div class="topsection">
    </div>
    <div class="midsection">
    </div>
    <div class="botsection">
    </div>
<div class="footsection"></div></div>

鉴于上面的模板...假设用户在文本框中输入类名(例如botsection)并点击“createfancybutton”按钮),我想要“” “&lt;fancy-button&gt;&lt;/fancy-button&gt;”要放入页面的适当部分,我希望能够在页面模板的不同部分动态“创建”独立“花式按钮”的实例。我可以将 3 个 ng-if 语句与 ng-for 结合起来,尽管这似乎不切实际。正在寻找更好的选择...

更新:所以步骤是:

1) 用户在文本框中输入“中段”。 2)用户点击按钮“创建花式按钮” 3) - &lt;fancybutton&gt;&lt;/fancybutton&gt; 组件将添加到类名为“midsection”的 div 下-

用户可以重复点击相同的“创建花式按钮”按钮以在其下创建更多。如果用户将输入框更改为“topsection”,那么当用户单击“Create Fancy Button”时,fancybutton 组件将添加到 div 下,并带有“topsection”。

如果用户输入“newsection”,则会在类名为“container”的div下新建一个classname为“newsection”的div,并在classname为“newsection”的div中添加fancybutton组件。

【问题讨论】:

标签: angular


【解决方案1】:

在您的组件中有一组标签。

使用 *ngFor 遍历这个数组并为每个标签生成一个精美的文本框。

要添加新的花式文本框,请在数组中添加一个新标签。

<fancy-textbox *ngFor="let label of labels" [labelForBox]="label"></fancy-textbox>  

在组件中:

labels: string[] = [];

// to add a fancy textbox:
this.labels.push('new label');

【讨论】:

  • 问题是,我希望花式文本框出现在我的 HTML 模板的几个不同部分。例如,可能有不同的特定嵌套 div 我想插入花式文本框。
  • 对于您想要实现的目标的如此模糊的描述,恐怕我无法提供更多帮助。
  • 更新了 OP。应该更清楚我在寻找什么。
  • 我不确定我是否理解 createFancyButton() 是如何决定在三个部分中的哪个部分添加花哨的按钮,但无论如何,解决方案是相同的:每个部分有一个数组,一个 ngFor每个部分,并将新标签添加到适当的数组中。如果您想避免重复自己,请使用 ngFor 生成您的三个部分,或者创建一个表示一个部分的组件。
  • createFancyButton() 知道是因为在它旁边的输入框中输入了什么。每个部分一个数组可能是正确的方法,但我担心如果我有太多“部分”会导致大量冗余代码。我添加了更新的 OP,并添加了条件:“如果用户输入“newsection”,那么将在类名为“container”的 div 下创建一个类名为“newsection”的新 div,并将添加 fancybutton 组件类名为“newsection”的 div。”
【解决方案2】:

看看这个“架构”,并利用 JS/TS OO 类继承,我会个人 考虑添加一个“复合组件”(“cc”:遵循OO'复合'模式)到每个部分(例如:&lt;top-section-cc-fancytextboxes...&gt;&lt;/&gt;&lt;bot-section-cc-fancytextboxes...&gt;&lt;/&gt;)。这些用作各种&lt;fancy-textbox&gt; 类型的“占位符”,这些类型可以是每个cc(复合组件)内的零个或多个实例。 现在每个复合组件都从基类/接口组件(例如:&lt;base-section-cc-fancytextboxes&gt;)派生/实现,其中包含管理添加多个&lt;fancy-textbox&gt; 类型的基本方法。然而,派生类方法会“知道”在哪里添加正确的&lt;fancy-textbox&gt;s(可能是上面提到的*ngFor'd 数组)。至于实例化特定类型的&lt;fancy-textbox&gt;,也许利用类工厂模式也很有用——可能是一个返回实例并由复合组件驱动的AJ2 服务提供者。

不管怎样,AJ2 内置了 TS OO,尽管有细节,这是我个人用来解决这个特殊问题的向量。这只是一个想法。

【讨论】:

  • 这看起来是最好的解决方案@MoMo。你能尝试创建一个plunker吗?这将对每个人都有巨大的帮助。
【解决方案3】:

你需要动态加载组件

这是我的解决方案

Parent.component.ts

import { Component, OnInit, ViewChild, ViewContainerRef, Input, ComponentFactoryResolver, ReflectiveInjector } from
    "@angular/core";

import { FancyButtonCompoent } from "../FancyButton.component";

@Component({
    moduleId: module.id,
    selector: "app-parent",
    templateUrl: "parent.component.html",
    styleUrls: ["parent.component.css"],
    entryComponents: [FancyButtonCompoent]

})
export class ParentCompoent {

    @ViewChild("midsection", { read: ViewContainerRef })
    midsectionContainer: ViewContainerRef;

    constructor(private resolver: ComponentFactoryResolver) {
    }

    createFancyButton() {
        //Name Is Fancybutton data binding property
        var yourdatavalues= {Name:'myname'}
        this.createDynamicbutton({
            input: yourdatavalues,
        });
    }
    //you can add your own model to get what you want like remove,move
     // var dynamiccompoent={Data:yourmodel,compoentcontainer:any}
     //fancybuttonarray:dynamiccompoent[];

 fancybuttonarray:any[];

    createDynamicbutton(elementData) {
        if (!elementData) {
            return;
        }

        // Inputs need to be in the following format to be resolved properly
        let inputProviders = Object.keys(elementData.inputs)
            .map((inputName) => { return { provide: inputName, useValue: elementData.inputs[inputName] }; });
        let resolvedInputs = ReflectiveInjector.resolve(inputProviders);

        // We create an injector out of the data we want to pass down and this components injector
        let injector = ReflectiveInjector
            .fromResolvedProviders(resolvedInputs, this.midsectionContainer.parentInjector);

        // We create a factory out of the component we want to create
        let factory = this.resolver.resolveComponentFactory(DefaultButtonComponent);

        // We create the component using the factory and the injector
        let component = factory.create(injector);

        this.midsectionContainer.insert(component.hostView)

         //your getting every instance of fancy button instance into array
         this.fancybuttonarray.push.(component )

         //if you want to clear elment if you wish
         //this.fancybuttonarray[0].destroy()
          //this.fancybuttonarray[1].destroy()


    }
}

parent.component.html

<div   class="row col-lg-12" >
    <div #midsection >

    </div>
</div>

Fancybutton.compoent.ts

    import { Component, OnInit, Injector } from '@angular/core';


@Component({
    moduleId: module.id,
    selector: 'Fancy-button',
    templateUrl: 'Fancybutton.component.html'
})
export class FancybuttonComponent {

    inputelement:yourDatamodel
    constructor(private injector: Injector) {
        this.inputElement = this.injector.get('inputElement');
    }

}

Fancybutton.compoent.html

    <div>
    <button title="inputelement.Name"(click)r ="alert(inputelement.Name)"></button>
    </div>    

更新

这是一篇关于使用angular2动态加载子组件的好帖子

也可用Plunker example

【讨论】:

  • 如果你想清除生成的组件。只需将插入的组件添加到数组并清除它们
  • ReflectiveInjector 已弃用。您可以使用 Injector 来代替“ let injector = Injector.create({ providers: [resolvedInputs], parent: this.midsectionContainer.parentInjector, });` "
【解决方案4】:

我认为您不需要任何其他范围/组件。这样的事情应该可以工作。

组件(TypeScript):

sections: {[key] : string[]} = {};

createFancyButton: (section: string) => {
    if (!this.sections[section]) {
        this.sections[section] = [];
    }
    this.sections[section].push('');
}

getSectionKeys: () => {
    return Object.keys(this.sections);
}

“sections”属性是一个可索引对象,这意味着它的行为类似于哈希表。 section 对象的每个属性(“key”)都是一个字符串数组,代表花哨的按钮值(ngModel)。

模板 (HTML)

<div class="container">
    <div class="navsection">
        <input #section type="text" placeholder="section" />
        <button (click)="createFancyButton(#section.value)">Create Fancy Button</button>
    </div>
    <div *ngFor="let key of getSectionKeys()" [class]="key">
        <fancy-textbox *ngFor="let textBox of sections[key]" [(ngModel)]="textBox"></fancy-textbox>
    </div>
</div>

有一个模板引用变量 (#section),它提供了对模板中 DO​​M 元素的轻松访问。然后我们 *ngFor 从sections hashtable 中的keys 来创建每个section div。最后,我们 *ngFor 遍历每个部分的字符串数组。

更多关于模板引用变量的信息可以在这里找到。 https://angular.io/docs/ts/latest/guide/template-syntax.html#!#ref-vars

注意:可能有错别字,因为我没有测试。

【讨论】:

    【解决方案5】:

    这是您以编程方式构建和添加组件的方式:

    import {ComponentRef, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
    
    @Component({
        selector: 'fancy-box',
        template: `<div>{{fancyContent}}</div> `,
    })
    export class FancyBox {
    
        fancyContent;
    
        doStuff() {
            console.log('done');
        }
    }
    
    @Component({
        selector: 'fancy-parent',
        template: `<div (click)="addNewFancyBox()">Add Box</div> `,
    })
    export class FancyParent {
        private counter = 0;
    
        constructor(
            private viewContainerRef: ViewContainerRef,
            private resolver: ComponentFactoryResolver) {
        }
    
        addNewFancyBox() {
    
            const factory = this.resolver.resolveComponentFactory(FancyBox);
            fancybox = this.viewContainerRef.createComponent(factory);
    
            const fancyboxElement = fancybox.instance as FancyBox;
            fancyboxElement.content = 'box number: ' + counter;
    
            fancyboxElement.doStuff();
            counter++;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-12
      • 2015-08-30
      • 1970-01-01
      • 2019-12-28
      • 1970-01-01
      • 1970-01-01
      • 2014-08-30
      • 2014-05-28
      相关资源
      最近更新 更多