Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use an event emitter on a dynamically added component?

Tags:

angular

Working off of Eric Martinez' answer from Angular 2 - Adding / Removing components on the fly, I want to limit the amount of components created. And then after deleting a component, I'm trying to emit that event to the parent so the parent knows the number of components currently created.

In this Plunker, I limit the number of components by 4, and then I'm trying to emit an event to lower that count back down. The event isn't emitting. How do you emit an event from a dynamically added component?

// dynamic component
@Component({
    selector : 'dynamic',
    template : `
    <div>
        Component number {{_idx}} <button (click)="remove()">Remove</button>
    </div>`
})
class DynamicCmp {
    _ref: ComponentRef;
    _idx: number;
    @Output lowerIndex = new EventEmitter<any>();
    remove() {
        this._ref.dispose();
        this.lowerIndex.emit(true);
    }
}

// Parent container component    
@Component({
    selector: 'my-app',
    template : `
        <button (click)="add()">Add new component</button>
        <div #location (lowerIndex)="lowerIndex();"></div>
    `
})
export class App {
    idx: number = 0;
    constructor(private _dcl: DynamicComponentLoader, private _e: ElementRef) {}

    lowerIndex() {
        this.idx--;
        console.log("subtracted");
    }

    add() {
        if (this.idx < 4) {
            this._dcl.loadIntoLocation(DynamicCmp, this._e, 'location').then((ref) => {
            ref.instance._ref = ref;
            ref.instance._idx = this.idx++;
            });

            console.log("added")
        }
    }
}

I posted the components in here because stackoverflow requires code posted with plunker links. Code is exactly the same as seen in plunker

like image 321
philip yoo Avatar asked Mar 26 '16 23:03

philip yoo


People also ask

How can you dynamically load the component?

To dynamically load the component, we use the directive as an anchor and create the component into its position in the DOM. The createComponent method requires the component type, not the instance.

How do you use event emitter?

Simply use it to emit events from your component. Take a look a the following example. @Component({ selector : 'child', template : ` <button (click)="sendNotification()">Notify my parent! </button> ` }) class Child { @Output() notifyParent: EventEmitter<any> = new EventEmitter(); sendNotification() { this.

How do I subscribe to event emitter?

subscribe() is an EventEmitter method that registers handlers for events emitted by this instance. subscribe() have three optional parameters which can be used to pass values, errors, or completion notification in EventEmitter . The next parameter is a custom handler for emitted events.

What does .emit do in angular?

🎊 Event Emitters in Angular 🎊 Data flows into your component via property bindings and flows out of your component through event bindings. If you want your component to notify his parent about something you can use the Output decorator with EventEmitter to create a custom event.


1 Answers

The issue here is that dynamically loaded components don't have a parent / child relationship with the components where they're loaded in. That's why to access properties from the dynamically loaded component you have to use ref.instance.property. The component is not being compiled along with the parent, therefore the parent knows nothing about the foster component.

Now, that being said, what you can do is to make a property that you can subscribe to and I wouldn't use an EventEmitter for that.

My solution would be to use a Subject instead (as I'm doing in a little project of my own).

Your dynamic component would look like this

class DynamicCmp {

    // Subject instead of an EventEmitter
    lowerIndex: Subject<boolean> = new Subject();

    remove() {
        // We send true, although in the example is not being used
        this.lowerIndex.next(true);

        // Remove the component from the view
        this._ref.dispose();
    }
}

Your component where you're loading the dynamic component

template : '<div #location></div>'; // Note that I removed (lowerIndex)="..."
this._dcl.loadIntoLocation(DynamicCmp, this._e, 'location').then((ref) => {
    //...

    // Subscribe to the Subject property
    ref.instance.lowerIndex.subscribe(v => {
        this.idx--;
        console.log("subtracted");
    });
});

Here's your plnkr working and updated to beta.12.

like image 177
Eric Martinez Avatar answered Oct 04 '22 22:10

Eric Martinez