简体   繁体   English

如何在angular2中以编程方式动态创建组件的多个独立实例?

[英]How to programmatically dynamically create multiple independent instances of component in angular2?

I have a button component fancy-textbox . 我有一个按钮组件fancy-textbox I want to make it such that users can dynamically add new fancy-textbox , but they have different labels above the textbox that is based on a scope variable that is unique to the fancy-textbox itself (or maybe from a parent scope variable that is not shared among all fancy-textboxes ). 我想这样做,以便用户可以动态添加新的fancy-textbox ,但是他们在文本框上方具有不同的标签,该标签基于fancy-textbox本身唯一的范围变量(或者可能来自父范围变量不能在所有fancy-textboxes之间共享)。 How do I do this? 我该怎么做呢? Currently, I use this directly in my template showing and hiding, but I want to be able to "dynamically add more instances of this" programatically: 当前,我直接在显示和隐藏的模板中使用它,但是我希望能够以编程方式“动态添加此的更多实例”:

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

That is good, if fancy-textboxes are only created in the DOM in one specific area. 如果仅在一个特定区域中的DOM中创建精美的文本框,那就很好了。 However, what I want to do, is to be able to dynamically create components in different sections of the 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>

Given the template above... assuming that users input the classname (eg botsection) into the textbox and hit the "createfancybutton button), I want "" " <fancy-button></fancy-button> " to be put into the appropriate section of the page, I want to be able to dynamically "create" instances of independent "fancy-button" within different sections of the page template. I could stick 3 ng-if statements with ng-for, though it seems impractical. Looking for a better alternative... 给定上面的模板...假设用户在文本框中输入类名(例如botsection)并点击“ createfancybutton按钮”,我希望将“”“ <fancy-button></fancy-button> ”放到在页面的适当部分,我希望能够在页面模板的不同部分中动态地“创建”独立的“花哨按钮”实例。我可以在ng-for中粘贴3个ng-if语句,尽管这似乎不切实际。寻找更好的选择...

UPDATE: So the steps would be: 更新:因此,步骤将是:

1) User enters "midsection" into textbox. 1)用户在文本框中输入“ midsection”。 2) User clicks on the button "Create Fancy Button" 3) - The <fancybutton></fancybutton> component would be added under the div with the classname "midsection"- 2)用户点击按钮“创建花式按钮” 3)- <fancybutton></fancybutton>组件将添加到div下,其类<fancybutton></fancybutton> ”-

The user can repeat clicking on the same "Create Fancy Button" button to create more under that. 用户可以重复单击同一“创建花式按钮”按钮以在其下创建更多按钮。 If user changes the input box to "topsection", then when user clicks on "Create Fancy Button", the fancybutton component would be added under div with "topsection". 如果用户将输入框更改为“ topsection”,则当用户单击“ Create Fancy Button”时,fancybutton组件将添加到带有“ topsection”的div下。

If the user enters "newsection", then a new div with classname "newsection" would be created under the div with the classname "container", and fancybutton component would be added to the div with classname "newsection". 如果用户输入“ newsection”,则将在div下以类名“ container”创建一个新的div,其类名为“ newsection”,并且将fancybutton组件添加到div中,其类名为“ newsection”。

Have an array of labels in your component. 组件中有一系列标签。

Use *ngFor to iterate over this array and generate a fancy textbox for each of those labels. 使用* ngFor遍历此数组,并为每个标签生成一个精美的文本框。

To add a new fancy-textbox, add a new label to the array. 要添加新的文本框,请向数组添加新标签。

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

And in the component: 并在组件中:

labels: string[] = [];

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

You need load component dynamically 您需要动态加载组件

Here is my solution 这是我的解决方案

Parent.component.ts 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 parent.component.html

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

    </div>
</div>

Fancybutton.compoent.ts 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 Fancybutton.compoent.html

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

Update 更新资料

here is the nice post about load dynamically child component using angular2 这是关于使用angular2动态加载子组件的不错的帖子

Also Avilable Plunker example 同样是Avilable Plunker示例

Looking at this 'architecturally', and leveraging JS/TS OO class inheritance, I would personally consider adding a 'compound component' ('cc': follows an OO 'composite' pattern) to each section (eg: <top-section-cc-fancytextboxes...></> or <bot-section-cc-fancytextboxes...></> ). 着眼于这种“架构”,并利用JS / TS OO类继承,我个人 考虑为每个部分添加一个“复合组件”(“ cc”:遵循OO“复合”模式)(例如: <top-section-cc-fancytextboxes...></><bot-section-cc-fancytextboxes...></> )。 These serve as 'place holders' for the various <fancy-textbox> types which could be zero or more instances inside each cc (compound component). 它们充当各种<fancy-textbox>类型的“占位符”,每个cc(复合组件)中的实例可能为零个或多个。 Now each compound component derives/implements from a base class/interface component(eg: <base-section-cc-fancytextboxes> ) which contains the base methods to manage adding multiple <fancy-textbox> types. 现在,每个复合组件都从基类/接口组件(例如<base-section-cc-fancytextboxes> )派生/实现,该组件包含管理添加多个<fancy-textbox>类型的基本方法。 The derived class methods, however, would 'know' where to add the proper <fancy-textbox> s (probably a *ngFor 'd array as mentioned above). 但是,派生的类方法将“知道”在何处添加适当的<fancy-textbox> s(可能是*ngFor*ngFor数组)。 As for instantiating a specific type of <fancy-textbox> , perhaps leveraging a class factory pattern will be useful as well--probably an AJ2 service provider that returns instances and driven by the compound components. 至于实例化<fancy-textbox>的特定类型,也许利用类工厂模式也将很有用-可能是一个AJ2服务提供者,它返回实例并由复合组件驱动。

Regardless, with AJ2's built in TS OO, and details notwithstanding, this is the vector I personally will use to solve this particular problem. 无论如何,尽管AJ2内置于TS OO,但尽管有细节,但我个人将使用它来解决此特定问题。 This is just a thought. 这只是一个想法。

I don't believe you need any other scopes/components. 我认为您不需要任何其他范围/组件。 Something like this should work. 这样的事情应该起作用。

component (TypeScript): 组件(TypeScript):

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

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

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

The "sections" property is an indexable object, which means it behaves like a hash table. “ sections”属性是可索引的对象,这意味着其行为类似于哈希表。 Each property ("key") of the sections object is an array of strings, which represent the fancy button values (ngModel). section对象的每个属性(“键”)都是一个字符串数组,这些字符串代表精美的按钮值(ngModel)。

template (HTML) 范本(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>

There is a template reference variable (#section), which provide for easy access to a DOM element in the template. 有一个模板引用变量(#section),可轻松访问模板中的DOM元素。 Then we *ngFor over the keys from the sections hashtable to create each section div. 然后,我们对部分哈希表中的键进行* ngFor,以创建每个部分div。 Finally we *ngFor over the array of strings for each section. 最后,我们对每个部分的字符串数组进行* ngFor。

More information on template reference variables can be found here. 有关模板引用变量的更多信息,请参见此处。 https://angular.io/docs/ts/latest/guide/template-syntax.html#!#ref-vars https://angular.io/docs/ts/latest/guide/template-syntax.html#!#ref-vars

Note: There may be typos as I did not test this. 注意:可能有错别字,因为我没有对此进行测试。

This is how you construct and add components programmatically: 这是您以编程方式构造和添加组件的方式:

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++;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM