0

I want to have a parent view loop through the html of different child components. I'm using Angular8. How can I do this?

I have tried various attempts using ViewChildren, like suggested here and here.

But these and other examples of ViewChildren seem to be going for something different than what I have in mind. Or it could just be my lack of understanding of ViewChildren.

Here is some psuedo-code to express what I have in mind (I know it doesn't work, but hopefully it clarifies the goal here):

*happy-parent-component:

  html:
   <div *ngFor="let childComponent of childComponents" >
        <{{childComponent}} ></{{childComponent}}> <!--I understand this doesn't work--> 
    </div>

  ts:
    import { ChildComponent1} from '../child-component1/child-component1.component"
    import { ChildComponent2} from '../child-component2/child-component2.component"
    ...
    export class HappyParentComponent implements OnInit {
       @ViewChild(ChildComponent1, {static: false})
       @ViewChild(ChildComponent2, {static: false})

       public childComponents = [ChildComponent1, ChildComponent2]  //(I understand this doesn't work...)

*child-component1:

     html:
        <div><!--fancy html from childcomponent1 --></div>

*child-component2:

     html:
        <div><!--fancy html from childcomponent2 --></div>

The result on the HappyParentComponent view would be:

  <!--fancy html from childcomponent1 -->
  <!--fancy html from childcomponent2 -->  

Is there a way to do this?

EDIT: Just for more info, the reason I want to add the html of the child components dynamically, instead of just putting childComponen1, childComponent2, etc.. directly into the parent view html, is that which components will be displayed depends on user selections.

So users will make certain selections, and that will change which child components are displayed. And over time, there could end up being a lot of child components.

SeanRtS
  • 1,005
  • 1
  • 13
  • 31
  • So you basically want to insert components dynamically in your html? what is the problem you trying to solve that lead you to think that this might be the solution? why not put ` ` directly there? – Mauro Aguilar Nov 15 '19 at 21:32
  • Thanks and yes I want to insert components dynamically. The reason to not just put the app-child-1 and app-child 2, etc., directly into the html is that which components will be added actually relies on user selections. Depending on which options they select, different child components will be displayed. – SeanRtS Nov 15 '19 at 22:26

2 Answers2

0

does your child components have similar things to display??

the reason why am asking is, if you have similar data to display, you can create one child component and pass different data to display inside the loop.

like

<child-component *ngFor="let childObj of childArr" [data]="childObj">
    // your html here
</child-component>

as you said dynamically changing views and there would be lot of child components in the future, you shouldn't create multiple components, instead you can follow this.

Shankarlal D
  • 169
  • 1
  • 7
  • The key is that the components have totally different html structures. It's not just an issue of different data--they have an entirely different appearance. So it probably has to be in different components. – SeanRtS Nov 16 '19 at 14:23
0

You are probably looking for to dynamically creating components. You say components should be rendered based on user selection, so this could be one way:

gather all available components into an object:

components = {
  hello: HelloComponent,
  hello1: Hello1Component
};

Don't know how you will gather the components, but for my example I have a form, and the form result could look like:

{
  hello: false,
  hello1: true
}

which would allow to do:

// val would be the above form object
createSelected(val) {
  // this removes all previously created components, if exist
  this.removeComponents();
  const selected = {};
  for (let v in val) {
    if (val[v]) {
      let comp = this.components[v];
      let factory = this.componentFactoryResolver.resolveComponentFactory(
        comp
      );
      // this is the array that gets cleared 
      // when all previous components should be destroyed
      this.componentRefs.push(this.container.createComponent(factory));
    }
  }
}

removeComponents() {
  if (this.componentRefs.length) {
    this.componentRefs.map(x => x.destroy());
    this.componentRefs = [];
  }
}

The above createSelected() is call when form is submitted.

Your parent template would then simply look like:

<div #container></div>

And we access this in parent with:

@ViewChild("container", { read: ViewContainerRef, static: false })
container: ViewContainerRef;

Then remember to add these child components as entryComponents in your ngModule.

STACKBLITZ

AT82
  • 71,416
  • 24
  • 140
  • 167
  • Thank you for this suggestion. In this code, is the parent component having to fully load every child component, even if only a few are displayed? So that, even if the user selects, say, just one child component to view, the parent page is the size of all possible components? – SeanRtS Nov 16 '19 at 14:30
  • That might end up being the case for any solution, but I'm trying to figure out how this goal will make the parent component view larger and slow down the loading of the parent component, even if only a one or two child components show up in the view. – SeanRtS Nov 16 '19 at 14:31
  • this does just create the components that user has chosen, not all. As you see I am looping through `val` and only create those that user has chosen. – AT82 Nov 16 '19 at 14:44
  • this is just a sample on how it can be done, you need to apply it to your usecase, i.e how and when you want to create certain components. – AT82 Nov 16 '19 at 14:52
  • Thanks--I guess this is a basic Angular question. In the parent component, I'll have to import all the child components. Does that bloat the parent component? Or is it instead that the parent component is only bloated by what child components are actually displayed in the view? – SeanRtS Nov 16 '19 at 15:25