50

I am using this technique to dynamically create component:

import {
 Component, Input, ViewContainerRef, ViewChild, ReflectiveInjector,    ComponentFactoryResolver,
 Output, EventEmitter
 } from '@angular/core';

 @Component({
 selector: 'dynamic-component',
 template: `
 <div #dynamicComponentContainer></div>
 `,
 })
 export default class DynamicLayerComponent {
 currentComponent = null;

 @ViewChild('dynamicComponentContainer', { read: ViewContainerRef })     dynamicComponentContainer: ViewContainerRef;
 @Output() visibility = new EventEmitter<boolean>();

// component: Class for the component you want to create
// inputs: An object with key/value pairs mapped to input name/input value
@Input() set componentData(data: {component: any, inputs: any }) {
console.log('setting');
if (!data) {
  return;
}

// Inputs need to be in the following format to be resolved properly
let inputProviders = Object.keys(data.inputs).map((inputName) => {return   {provide: inputName, useValue: data.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.dynamicComponentContainer.parentInjector);

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

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

// We insert the component into the dom container
this.dynamicComponentContainer.insert(component.hostView);

// We can destroy the old component is we like by calling destroy
if (this.currentComponent) {
  console.log('fdsafa');
  this.currentComponent.destroy();
}

this.currentComponent = component;
}

 constructor(private resolver: ComponentFactoryResolver) {
  console.log('dfsd');
 }
}

And then I use it like that:

<div *ngFor="let layer of sortedItems" class="single-layer">
  <div>
    <dynamic-component #DynamicLayer
                       [componentData]="{
  component: layer.componentClass,
  inputs: {
    layerItem: layer,
    sortFilter: sortFilter
  }
}"
                       (visibility)="setLayerVisibility(layer, $event)">
    </dynamic-component>
  </div>

The problem is that I am not able to bind to an event, it does not work when binding to (visibility). The setLayerVisibility is not called when the event occurs. How to fix that problem ?

Of course my sample component based on componentClass has the @Output set like:

  @Output() visibility = new EventEmitter<boolean>();

private visibilityChanged() {
    this.visibility.emit(this.layerItem.visible);
  }
Mistalis
  • 17,793
  • 13
  • 73
  • 97
Jerzy Gruszka
  • 1,629
  • 4
  • 15
  • 20

2 Answers2

49

Your factory:

factory.create(injector);

will return an ComponentRef object, and with this object you can access that component itself.

You could subscribe to that event via:

component.instance.visibility.subscribe(v => ...);
slaesh
  • 16,659
  • 6
  • 50
  • 52
  • 1
    cannot subscribe of undefined. Maybe related to lifecyclehooks of component generation? – Becario Senior Mar 16 '18 at 20:02
  • answer to self: it does. Just, in the function body of the subscribe do a {outputs.emit(v)} and in the function that also gets the inputs, also pass the outputs from the invoking component – yogibimbi May 12 '18 at 13:40
  • @yogibimbi Can you elaborate your answer a bit, I am getting cannot subscribe of undefined whenever I am trying to give output. I load dynamic component from directive . – sam_dev Apr 24 '20 at 04:50
  • show us your code, best would be a small stackblitz.com – slaesh Apr 24 '20 at 08:27
  • //this worked for me :) component.instance.visibility.subscribe(v => ...); – hassan khademi May 05 '20 at 08:58
25

Just subscribe the output event like this

@ViewChild('yourComponentRef', { read: ViewContainerRef }) container: ViewContainerRef;
// Reference for dynamic component
private _ref;

constructor(
private _cfr: ComponentFactoryResolver){ }

public addDynamicComponent() {
    const comp =
        this._cfr.resolveComponentFactory(<YOUR_DYNAMIC_COMPONENT_HERE>);
    this._ref = this.container.createComponent(comp);
    // Input to dynamic component
    this._ref.instance.inputVarHere = [1, 2, 3];
    // Handles output event, just emit your output here
    this._ref.instance.outputEventHere.subscribe(data => {
        console.log(data);
    });
}
public removeDynamicComponent() {
    this._ref.destroy();
}

In your html file

<!-- Section to load dynamic component -->
<div #yourComponentRef></div>
Aakash Purohit
  • 261
  • 3
  • 5