2

Suppose I have a "ParentComponent" that has a list of items (let's say taken as an input) and needs to render the elements in the list. I need this parent component to be reusable however and one of the customizations it would support would be choosing the template (or component) it uses for rendering the list items. The component used for the child list items could be anything as long as it can render the list item given to it as an input (the template can correctly reference properties of the item). This is possible in frameworks such as React, so my question would be if it is possible as well in Angular and how one would do it?

I imagine the parent component taking the custom component as an input, along with the items

@Input()
items: Array<ItemType>;

@Input()
componentForRenderingStuff: ?;

And the template being something like this

<div class="wrapper">
  <!-- ...stuff -->
  <div class="list-item" *ngFor="let i of items">
    <component-for-rendering-stuff [item]="i"></component-for-rendering-stuff>
  </div>
</div>

Which would mean that using the parent component in another template would be like

<app-parent-component [items]="items" [componentForRenderingStuff]="SomeComponent"></app-parent-component>

Is something like this possible? I looked at ng-content but I couldn't make it work with *ngFor.

7
  • I think this is too complicated an approach. It could be easier. That component object pass to a child, this is a anti-pattern. Define a component what you import into your module. And this component can be reuse anywhere. If you pass data to parent there exists a @Output annotation. Did you think of this? Commented Nov 4, 2020 at 16:38
  • 1
    If I was faced with this problem I would use ng-switch to load the correct component and simply have one child component that would hold the other components to be switched Commented Nov 4, 2020 at 18:28
  • @OwenKelvin yeah, I did this once in my app already and I'm kind of worried of those bindings being active all the time, waiting for changes in the input. Besides that the idea is that this list is might be used once let's say in a search bar dropdown, at another time in some completely other place, etc. with the goal is it being a light dependency for the different angular modules within the app. Commented Nov 4, 2020 at 18:47
  • @Numichi I'm at a loss, not sure I understand the "@Output" part within this context, as for this being an antipattern it is used heavily in React and with great success. Commented Nov 4, 2020 at 18:48
  • 1
    there are two options that come to mind, one is using the template based solution mentioned by @OwenKelvin. the other is dynamic component loading. both have their pros and cons. however, if performance is a real concern for you, and you are looking to avoid the change detection that having so many if's in your template may cause, then you are left with dynamic component loading. Commented Nov 5, 2020 at 15:36

1 Answer 1

0

As Edward suggested in his comment the behavior can be achieved using dynamic component loading.

I found a few third party libraries on npm which solved that and due to time and simplicity I used the one which had the most appealing user statistics: https://www.npmjs.com/package/ng-dynamic-component

In short a generic list component would look like this

@Component({
  selector: 'app-list',
  template: `
    <div *ngFor="let item of items">
      <ndc-dynamic
        [ndcDynamicComponent]="listItemComponent"
        [ndcDynamicInputs]="{ input: item.data }"
      ></ndc-dynamic>
    </div>
  `,
  styleUrls: ['...'],
})
export class ClickableVerticalListComponent {
  @Input()
  listItemComponent: any;

  @Input()
  items: Array<any>;

  constructor() {}
}

If we want to use the list with a specific custom component for a list item we would do it in this way:

@Component({
  selector: '...',
  template: `
    <app-list 
      [items]="items" 
      [listItemComponent]="ExampleComponent"
    ></app-list>
  `,
  styleUrls: ['...'],
})
export class ParentComponent {
  ExampleComponent = ExampleComponent;

  items: [{ data: ... }, { data: ... }];

  constructor() {}
}
Sign up to request clarification or add additional context in comments.

1 Comment

glad to hear you found a solution. just as an FYI for future use, angular native has documentation on dynamic component loading here. good luck.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.