26

TL;DR:

Explanation:

I'm dynamically creating Angular 2 components using DynamicComponentLoader's loadIntoLocation() function.

The place to insert the component generated by this function is determined by its anchorName parameter, which excepts you to pass a Template Variable Name (as a string).

So in the example they use <div #child></div> in the template, then pass 'child'. Which works fine.

However, rather than having to link generated components to an element with variable name hard-coded into the template, I'd like to be able to, say, append them to a variable-sized list.

Now, the NgFor page shows you have access to an index variable: <li *ng-for="#item of items; #i = index">...</li>. So it'd work if I could assign the list items such template variable names used on this index or the like, i.e. #child1, #child2, etc.

So I'm inclined to try <div #{{foo}}></div> with an app variable foo as "child". I'm having trouble debugging this since the front-end doesn't really show these template variable names in the DOM, but it seems this dynamic assignment is failing, resulting in an error "Could not find variable ...".

Might there be any way to do what I want? Or even to view assigned template variable names from the browser for debugging?

Kiara Grouwstra
  • 5,723
  • 4
  • 21
  • 36

2 Answers2

14

The problem with that approach is you can't dynamically generate variable names.

Another possible approach is using loadAsRoot which instead of using a variable name, uses an id which can contain a dynamic name.

// This will generate dynamically the id value
template: `
  <div *ng-for="#idx of data">
    <div id="dynamicid_{{idx}}">Dynamic</div>
  </div>`

Then you set the list you want to iterate over

this.data = [1,2,3,4,5,6];

for(var i = 0; i < this.data.length; i++) {
  // Third argument is the injector that I'm not using, so I just nulled it
  dynamicComponentLoader.loadAsRoot(DynamicComponent, '#dynamicid_'+this.data[i], null);
}

Here's the plnkr with an example working.

I hope it helps.

Eric Martinez
  • 31,277
  • 9
  • 92
  • 91
  • Hah, that's great. I was already starting to fear assigning the variables dynamically wasn't going to work out, but I'm glad there's still a solution. Thank you! :) – Kiara Grouwstra Nov 29 '15 at 12:45
  • 1
    You're welcome! There's another [solution](http://stackoverflow.com/a/33000250/4933038), but it's a *little bit* more complicated. – Eric Martinez Nov 29 '15 at 12:46
  • Hah, I hadn't even seen that thread yet. I'm pretty sure I'll stick with your solution though, much neater. :D – Kiara Grouwstra Nov 29 '15 at 12:57
  • 1
    Follow-up: creating the components itself works fine, but I've had trouble loading them with useful templates (involving NgFor, in the child now) due to dependency injection exceptions. I've created a [new question](http://stackoverflow.com/questions/33986633/angular-2-dependency-injection-error-on-ngfor-in-dynamically-created-component) for that issue. – Kiara Grouwstra Nov 29 '15 at 18:54
  • 1
    this works in this simple example but what I'm seeing is that if the item has additional stuff like click handlers or routerLinks those do not render with loadAsRoot. I'm only able to get them to load with loadIntoLocation. – Honorable Chow Jan 26 '16 at 02:29
  • this should be `[attr.id]="dynamicid_{{idx}}`, no? – Cardano Jun 03 '16 at 04:59
  • Angular 2 rc4: DynamicComponentLoader deprecated. http://stackoverflow.com/questions/38332249/angular-2-rc4-dynamiccomponentloader-deprecated – Avisek Chakraborty Apr 27 '17 at 07:56
3

After looking around for similar answers, it dawned on me that the default solution is actually very standard: extract a component, where you can hardcode your template variable, and generate as many instances of this component as you need with the *ngFor directive.

I understand there may be concerns about performance, and I don't know enough about that to comment either way (who knows, it may end up being faster), but IMO it should definitely be the first solution to be envisioned.

And the DynamicComponentLoader mentionned in Eric Martinez answer seems to be gone from Angular 5 anyway (couldn't find it in the docs).

Arnaud P
  • 12,022
  • 7
  • 56
  • 67