Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 - Pass parameters to a dynamically generated component?

I have a simple demo app which I'm simulating manually insert / fetch data from DB and injecting new components - according to the num entered.

Plunker

enter image description here

So if I click the "manual " button twice :

enter image description here

And if I set "3" in the text and click "fetch from db" - I get the expected delay(simulate db) and then :

enter image description here

This all works as expected.

The "parent" component is :

//src/MainPage.ts
@Component({
  selector: 'my-app',
  template: `
    <button (click)="putInMyHtml()">Insert component manually</button>
    <p> # Items to fetch  : <input type="text" style='width:40px'  [(ngModel)]="dbNumItems" name="dbNumItems"/> <input type='button' value='fetch from db' (click)='fetchItems($event)'/></p>
    <div #myDiv>
       <template #target></template> 
    </div>
  `
})
export class MainPage {
  @ViewChild('target', { read: ViewContainerRef }) target: ViewContainerRef;
  dbNumItems: string;
  constructor(private cfr: ComponentFactoryResolver) {}

  fetchItems(){ 
        var p= new Promise((resolve, reject) => { //simulate db
        setTimeout(()=>resolve(this.dbNumItems),2000)
    });

  p.then(v=>{

    for (let i =0;i<v;i++)
    {
         this.putInMyHtml() ;// inject "v" times
    }
  })

}

  putInMyHtml() {
   // this.target.clear();
   let compFactory = this.cfr.resolveComponentFactory(TestPage);
    this.target.createComponent(compFactory);
  }
}

This is the Injected component :

//src/TestPage.ts
@Component({
  selector: 'test-component',
  template: '<b>Content  : Name={{user.Name}} Age={{user.Age}}</b><br/>',
})
export class TestPage {
    @Input
     User:Person;

}

So where is the problem ?

As you can see , in the injected component I have :

@Input
User:Person;

which means that I want the parent component to pass a Person object to each injection.

In other words :

Question

Looking at the "after db stage" , How can I pass a customized person to each injection ?

  p.then(v=>{

    for (let i =0;i<v;i++)
    {
         let p = new Person();
         p.Name = "name"+i;
         p.Age = i;

         this.putInMyHtml() ; //how to I pass `p` ???

    }
  })

}

Expected output :

enter image description here

NB I don't want to use ngFor because I don't need to hold an Array at the back end. this is an app which injects new articles periodically.and I will be glad to know if there's a better way of doing it.

like image 756
Royi Namir Avatar asked Apr 01 '17 18:04

Royi Namir


2 Answers

You can do it with the instance property of component ref like this:

putInMyHtml(p) {
   // this.target.clear();

    let compFactory = this.cfr.resolveComponentFactory(TestPage);

    let ref = this.target.createComponent(compFactory);
    ref.instance.user = p;
}

-Fixed the @Input() binding, syntax was wrong.

-Added a safe-navigation operator (?) for the template to do the null checks for the async input.

Fixed plunker: https://plnkr.co/edit/WgWFZQLxt9RFoZLR46HH?p=preview

like image 138
eko Avatar answered Oct 08 '22 09:10

eko


use *ngFor and iterate through an array of Person, that way you can use the @Input. You probably want something like

<ul>
  <li *ngFor="let person of people">
    <test-component [User]=person></test-component>
  </li>
</ul>

add people: Person[] to your main component and when you fetch items

p.then(v=>{

for (let i =0;i<v;i++)
{
   let p = new Person();
   p.Name = "name"+i;
   p.Age = i;  
   people.push(p)
}
})
like image 44
Phil DiMarco Avatar answered Oct 08 '22 11:10

Phil DiMarco