How to programmatically dynamically create multiple independent component instances in angular2?

I have a fancy-textbox button component. I want to make it so that users can dynamically add new fancy-textbox , but they have different labels over the text box based on the scope variable that is unique to fancy-textbox (or maybe from the parent variable of the scope that is not shared between all fancy-textboxes ). How can I do it? I am currently using this directly in my template, showing and hiding, but I want to be able to "dynamically add additional instances of this" programmatically:

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

This is good if fancy text fields are created only in the DOM in one specific area. However, I want to dynamically create components in different sections of the 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 pattern above ... assuming users enter a class name (e.g. botsection) in the text box and click the createfancybutton button, I want a " <fancy-button></fancy-button> " to be placed in the corresponding section of the page, I want to be able to dynamically “create” instances of an independent “fancy button” in different sections of the page template. I could stick to 3 ng-if statements with ng-for, although this seems impractical. Looking for a better alternative ...

UPDATE: Thus, the following steps will be performed:

1) The user enters "midsection" in the text box. 2) The user clicks the Create Fancy Button button, 3) - The <fancybutton></fancybutton> will be added under the div with the class name "midsection" -

The user can click on the same “Create Fancy Button” button to create more under it. If the user changes the input field to "topsection", then when the user clicks the "Create fancy button" button, the fancybutton component will be added under the div with "topsection".

If the user enters “news,” then a new div called “newsection” will be created under the div with the class name “container”, and the fancybutton component will be added to the div called “newsection” of the class.

+7
angular
source share
5 answers

You have an array of labels in your component.

Use * ng to iterate over this array and create a fancy text box for each of these labels.

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'); 
+1
source share

Looking at this “architecturally” and using the JS / TS OO class inheritance, I would personally consider adding a “composite component” (“cc”: followed by a “composite” OO template) for each section (for example: <top-section-cc-fancytextboxes...></> or <bot-section-cc-fancytextboxes...></> ). They serve as "place holders" for various types of <fancy-textbox> , which can be zero or more instances within each cc (component component). Now each component component receives / implements a component of the base class / interface (for example: <base-section-cc-fancytextboxes> ), which contains basic methods for controlling the addition of several types of <fancy-textbox> . However, the methods of the derived class “knew” where to add the correct <fancy-textbox> (possibly the array *ngFor 'd, as mentioned above). As for creating an instance of a specific <fancy-textbox> , perhaps using a factory class template is also useful - perhaps an AJ2 service provider that returns instances and is managed by component components.

Regardless of the fact that AJ2 is built into TS OO, and despite all the details, it is a vector that I personally will use to solve this particular problem. This is just a thought.

+1
source share

I do not think you need other areas / components. Something like this should work.

(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 that it behaves like a hash table. Each property ("key") of the section object is an array of strings that represent the values ​​of fancy buttons (ngModel).

template (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) that provides easy access to the DOM element in the template. Then we * ngFor over the keys from the hashtable sections create each section of the div. Finally, we * ngFor over an array of strings for each section.

More information on reference template variables can be found here. https://angular.io/docs/ts/latest/guide/template-syntax.html#!#ref-vars

Note: there may be typos as I have not tested this.

+1
source share

Dynamic dynamic download component

Here is my solution

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> 

Update

here is a nice post about dynamically loadable component using angular2

Also Avilable Example Plunger

0
source share

Here's how you programmatically create and add components:

 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++; } } 
0
source share

All Articles