38

I am able to load a dynamic Angular 2 component using ComponentResolver and ViewContainerRef.

However I am not able to figure out how to pass any input variable of child component into this.

parent.ts

    @Component({
     selector: "parent",
     template: "<div #childContainer ></div>"
    })
    export class ParentComponent {
      @ViewChild("childContainer", { read: ViewContainerRef }) childContainer: ViewContainerRef;

      constructor(private viewContainer: ViewContainerRef, private _cr: ComponentResolver) {}

      loadChild = (): void => {
           this._cr.resolveComponent(Child1Component).then(cmpFactory => {               
              this.childContainer.createComponent(cmpFactory);
           });
      }
    }

child1

 @Component({
   selector: "child1",
   template: "<div>{{var1}}</div><button (click)='closeMenu()'>Close</button>"
 })
 export class Child1Component {
    @Input() var1: string;
    @Output() close: EventEmitter<any> = new EventEmitter<any>();

    constructor() {}

    closeMenu = (): void => {
      this.close.emit("");
    }
 }

so in above example say loadChild is being called on a button click, I am able to load Child1Component, but how to pass var1 Input of child? Also How to subscribe to close EventEmitter decorated with @Output

Sangwin Gawande
  • 7,658
  • 8
  • 48
  • 66
Madhu Ranjan
  • 17,334
  • 7
  • 60
  • 69

2 Answers2

60

You have to pass it imperatively like:

loadChild(): void {
  this._cr.resolveComponent(Child1Component).then(cmpFactory => {               
    let cmpRef = this.childContainer.createComponent(cmpFactory);
     cmpRef.instance.var1 = someValue;  
   });
 }

also similar with registering handlers for outputs.

loadChild(): void {
  this._cr.resolveComponent(Child1Component).then(cmpFactory => {                
    let instance: any = this.childContainer.createComponent(cmpFactory).instance;
    if (!!instance.close) {
      // close is eventemitter decorated with @output 
      instance.close.subscribe(this.close);
    }
  });
}

close = (): void => {
  // do cleanup stuff..
  this.childContainer.clear();
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Thanks for this, I am not getting a callback however I am getting instance directly on createComponent, Can you please update the solution and also how to use @Output function? – Madhu Ranjan May 27 '16 at 17:22
  • @Gunter, tried the same way suggested by you, but getting some exceptions in browser console and it is not working as expected. can you please help by responding to this post? http://stackoverflow.com/questions/38360904/typeerror-cannot-read-property-createcomponent-of-undefined – Krishnan Jul 14 '16 at 16:35
  • @GünterZöchbauer: How to update data of input? Should i create a new question? – Nguyen Mar 27 '17 at 10:31
  • Do you know why it only works with the fat arrow function on close()? Regular annonymous doesnt work – L1ghtk3ira May 29 '18 at 17:42
  • @L1ghtk3ira because otherwise `this` points to where the callback is called from instead of our component class. See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions (you can also use `.bind(this)` for the same effect, but I find the arrow function syntax easier to read. – Günter Zöchbauer May 30 '18 at 05:27
6

This is how I did it with Angular 2.2.3

let nodeElement = document.getElementById("test");
let compFactory = this.componentFactoryResolver.resolveComponentFactory(AnyCustomComponent);
let component = compFactory.create(this.viewContainerRef.injector, null, nodeElement);
// This is where you pass the @Input
component.instance.anyInput = anyInput;
S.Galarneau
  • 2,194
  • 1
  • 24
  • 26