3

I am looping through a list of metrics and inside each loop there is a button that would be clicked to render a graph at a specific div within the HTML of the loop. The graph is a separate component, however I cannot seem to figure out how to target the correct HTML element to load the graph component into. I started by creating a component rendering function and call it like this:

loadMetricGraph(metric) {
  let selector = "[id=metricGraph-" + metric.id + "]";
  this.renderComponent(LineGraphHorizontalComponent, selector, metric.data);
}    

renderComponent(componentClass, selector, data) {
  return this.compiler
    .compileComponentAsync(componentClass)
    .then((factory:ComponentFactory<any>) => {
      let cmpRef:ComponentRef<any> =
        factory.create(this.injector, null, selector);
      cmpRef.instance.inputData = data;
      (<any>this.appRef)._loadComponent(cmpRef);
      return cmpRef;
    });
}

In the loop I have a div that would be the target for the loaded component, however I am stuck on how to pass the correct selector to the renderComponent() function. I have attempted to use id="graphData-{{ metric.id }}" And then a selector of "[id=graphData-" + metric.id + "]". I know this is not the correct way to do this, but I am not sure how to proceed.

somecallmemike
  • 441
  • 1
  • 8
  • 19

1 Answers1

4

You can't dynamically add HTML and get components or directives created for this HTML. If you want to dynamically add components then you need to use ViewContainerRef.createComonent.

See Angular 2 dynamic tabs with user-click chosen components shows how to do this declaratively using a helper component.

Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • I am not attempting to dynamically add HTML, the HTML is added in the *ngFor when the base component is loaded. I am attempting to target the existing HTML elements in the loop by an id or something dynamic to load the graph component. The example you linked shows how to do this when you already know the attribute of the target, in this case `#target`. I need to do this dynamically for each instance of the loop where the target is unknown until a click occurs. – somecallmemike Sep 14 '16 at 15:46
  • No, `#target` is inside the helper component. `*ngFor` adds the helper component (``) and configures it with the type `` should add inside itself. I think it's the only sane way. With your approach you would need to sync the HTML elements created by `*ngFor` with the dynamically created components. That seems really complicated, while my approach is quite simple. – Günter Zöchbauer Sep 14 '16 at 15:48
  • I guess I am not understanding your approach. I am trying to reuse the LineGraphHorizontalComponent class multiple times and inject an instance of it into a specific element location within the structure of the looped HTML. I do not have a list of pre-defined child components that need to be picked from, rather a reusable graph component that is passed data and renders a graph that should be placed in a specific location. – somecallmemike Sep 14 '16 at 15:59
  • I guess it would be helpful if you would add the code to your question that demonstrates how you generate the HTML and where you want to add the component. I have no idea what "specific element location within the structure of the looped HTML" means. – Günter Zöchbauer Sep 14 '16 at 16:09
  • I documented it in the question, I have a div element with an id tag like `id="metricGraph-{{ metric.id }}"`, and I simply want to target that div to load the component into. I cannot figure out a better way to give that div element a dynamic target than using and id, or if there is an alternative way to target a div that I am unaware of. `factory.create(this.injector, null, selector)` requires a valid selector and I am trying to figure out how to pass it a valid selector that I can generate dynamically when the DOM is created. – somecallmemike Sep 14 '16 at 16:26
  • I haven't seen working examples with CSS selectors yet (perhaps I missed something I didn't need it myself so far). *"And then a selector of "[id=graphData-" + metric.id + "]". I know this is not the correct way to do this, but I am not sure how to proceed."* What do you mean with this? Why do you think this is not the correct way? – Günter Zöchbauer Sep 14 '16 at 16:29
  • With the `` approach you could use `` and when you set `graphId` to the matching `matrix.id`, then `*ngIf` and `` will add the component. – Günter Zöchbauer Sep 14 '16 at 16:31