0

I have created dynamic component manually using button click. If we click on Add button it will create component next to the add button. If we click on Remove button it will remove the component.

Plunker

Here, the problem is while creating the component using ngFor, unable to remove component while clicking on Remove button. I noticed Remove function call is not happening while creating the component using ngFor.

Below my code:

Parent Component:

import{ Component, Input, Output, EventEmitter, ViewContainerRef,      ElementRef, ComponentRef, ComponentResolver,    ViewChild, ComponentFactory } from '@angular/core';
import {ChildCmpt } from './child-cmpt';
import {SharedData } from './SharedData';


    @Component({
        selector: 'my-app',
        templateUrl: 'src/parent-cmpt.html',
        directives: [ChildCmpt]
    })
    export class ParentCmpt {

        myAllNames: any;
        @Input() thing:string;
        //allItems: ItemsAddRemove;
        constructor(private resolver: ComponentResolver, private appItems: SharedData) {
            this.myAllNames = this.appItems.getNames();
        }

        idx:number = 0;

        @ViewChild('location', { read: ViewContainerRef}) location: ViewContainerRef;

        add() {
            this.resolver.resolveComponent(ChildCmpt).then((factory:ComponentFactory<any>) => {
                let ref = this.location.createComponent(factory)
                ref.instance._ref = ref;
                ref.instance._idx = this.idx++;
                ref.instance._thing = this.thing;
                //allItems.push(ref);
                this.appItems.addMyItem(ref);

            });
        }

        submit(a: any) {
            let temp = this.appItems.getMyItem();
            let componentThings = temp.map((compRef) => compRef.instance.thing);
            alert(componentThings);
        }

        removeAll = ():void => {
            // do cleanup stuff..
            this.location.clear();
            this.appItems.removeAll();
        }
    }

Child Component:

import { Component, Input, Output, EventEmitter, ViewContainerRef, ElementRef, ComponentRef, ComponentResolver, ViewChild, ComponentFactory } from '@angular/core';
import {SharedData } from './SharedData';


@Component({
    selector: 'child-cmpt',
    templateUrl: 'src/child-cmpt.html'
})
export class ChildCmpt {
    _ref:ComponentRef<any>;
    _idx:number;
    allVal = [];

    @Input() thing:string;
    public components = [];

    constructor(private resolver: ComponentResolver, private location: ViewContainerRef, private appItems: SharedData) {

    }

    remove() {
        let temp = this.appItems.getMyItem();
        delete temp[temp.indexOf(this._ref)];
        this._ref.destroy();
    }

    add() {

        this.resolver.resolveComponent(ChildCmpt).then((factory:ComponentFactory<any>) => {
            let ref = this.location.createComponent(factory, 0);
            ref.instance._ref = ref;
            ref.instance._idx = this._idx++;
            ref.instance._thing = this.thing;
            //this.allItems.push(ref);
            this.appItems.addMyItem(ref);
        });
    }
}

Please help me to fix this issue. Much appreciate your help.

Thanks in Advance.

user2932411
  • 147
  • 1
  • 4
  • 14

1 Answers1

0

Store the ComponentRef and call destory() when you want to remove it:

@Component({
    selector: 'my-app',
    templateUrl: 'src/parent-cmpt.html',
    directives: [ChildCmpt]
})
export class ParentCmpt {

    myAllNames: any;
    @Input() thing:string;
    //allItems: ItemsAddRemove;
    constructor(private resolver: ComponentResolver, private appItems: SharedData) {
        this.myAllNames = this.appItems.getNames();
    }

    idx:number = 0;

    @ViewChild('location', { read: ViewContainerRef}) location: ViewContainerRef;

    add() {
        this.resolver.resolveComponent(ChildCmpt).then((factory:ComponentFactory<any>) => {
            this.ref = this.location.createComponent(factory)
            ref.instance._ref = ref;
            ref.instance._idx = this.idx++;
            ref.instance._thing = this.thing;
            //allItems.push(ref);
            this.appItems.addMyItem(ref);

        });
    }

    submit(a: any) {
        let temp = this.appItems.getMyItem();
        let componentThings = temp.map((compRef) => compRef.instance.thing);
        alert(componentThings);
    }

    removeAll():void {
        // do cleanup stuff..
        this.ref.destory();
    }
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Thanks for your response. I tried the same, i couldnt able to store the ComponentRef while using ngFor. , below the code
    Could you please check the plunker link, I'm getting exception destory is undefined while tried to remove the component
    – user2932411 Jul 05 '16 at 08:35
  • http://stackoverflow.com/questions/36325212/angular-2-dynamic-tabs-with-user-click-chosen-components/36325468#36325468 might be of some help. I don't understand the problem with `ngFor`. Can you create a Plunker to reproduce? – Günter Zöchbauer Jul 05 '16 at 08:37
  • Below the link for your reference. [Plunker] (http://plnkr.co/edit/YtCT3C34FocTeSWPovvD?p=preview). Try to click the Remove button, then see the console logs, will get the exception : zone.js:260 Uncaught EXCEPTION: Error in src/child-cmpt.html:2:12 ORIGINAL EXCEPTION: TypeError: Cannot read property 'destroy' of undefined – user2932411 Jul 05 '16 at 08:40
  • You need to store a `ComponentRef` for each added dynamic component. I guess it would be easier the way I did it in the answer linked above where I use a wrapper component where a new wrapper instance is created for each dynamic component instance. This way you can create components by just using `ngFor` and passing the component type to the wrapper. – Günter Zöchbauer Jul 05 '16 at 08:46
  • Sure, i will follow the link will try to implement the same. Thanks for your help:) – user2932411 Jul 05 '16 at 10:09
  • I have gone through the link which you have shared. Using the link i have created an example http://plnkr.co/edit/jWyP4iSgmG6M5oajbbVd?p=preview But, still i couldnt able to store the ComponentRef. I'm getting and error VM2051 platform-browser.umd.js:1900Error: No Directive annotation found on TestName. Could you please check the plunker let me know about the issues. – user2932411 Jul 06 '16 at 04:59
  • The error is caused by the `[(ngModel)]=..." on `` because `` is not known. I updated to a new Plunker template http://plnkr.co/edit/Kp3q8IR1R2KWrAlhlPoO?p=preview but I guess you can continue on yours because the error can easily be fixed in your Plunker by just changing `` to ``. – Günter Zöchbauer Jul 06 '16 at 05:17
  • Thanks, for you Great Support, Now, I have removed from input tag. But,still it's throwing the exception: zone.js@0.6.12?main=browser:461 Unhandled Promise rejection: Cannot resolve component using 'TestName'. ; Zone: angular ; Task: Promise.then ; Value: http://plnkr.co/edit/fbDkNKddZsqtcMoqQvxZ?p=preview Let me know, how to fix the issue? – user2932411 Jul 06 '16 at 06:04
  • I haven't seen a `TestComponent` in your plunker (havent checked the link from your last comment because I'm only on my phone) – Günter Zöchbauer Jul 06 '16 at 07:28