0

The website I am building has multiple components. On the "Home" page component, when a user clicks a button to add something to the page, I want to append the component of their choice to the "home" component. I have researched and found 3 ways of doing this, but I want to know what is the best practice considering for such a feature. The page will be initialized with some components already added by other users. I want users to be able to add to the components on there.

Angular says I should do it this way: https://angular.io/guide/dynamic-component-loader

Angular Material says I should do it this way: https://material.angular.io/cdk/portal/overview

And I personally was expecting to it this way (which works) but I do not think it is a "best practice":

So when a user clicks on this tag it would fire the append method:

<a class="dropdown-item" href="#!" (click)="prependMedia('elementOne')">ElementOne</a>

That method calls this in the TS file of the component, effectively pushing a new element to an array. Then the HTML listed will watch for a new element added and put the component into the ngFor:

TypeScript File:

mediaElements: any[] = [];
  element: any = {
    name: 'elementOne',
    data: 'data'
};

constructor() { }

ngOnInit() {}

prependMedia(type: string) {
 console.log(type);
 this.mediaElements.push(this.element);
}

HTML File that would add the component once a new model for it has been added to the mediaElements array:

<div *ngFor="let element of mediaElements" [(ngModel)]="mediaElements">
  <div *ngIf="element.name == 'elementOne'">
    <app-elementone></app-elementone>
  </div>
</div>

My question: What is wrong about the method I use with the ngFor? Not in an opinionated way but technically. What is the "best practice" way of adding a component to the DOM when a user clicks a button to add it to the page? And why? Not in an opinionated way but technically or withe evidence from Angular team.

I originally was going to go with how I showed in code, but now I have found both the Angular way and the Angular Material way and I am looking for facts about the best way.

Please advise? As some people feel this is too open for opinions, keeping it technical will help.

Let me know if you need any further description.

Thank you, Eric

  • 2
    that for loop is in my opinion easiest way to do this only case when it wouldn't work is probably if you wouldn't know what components can be used by user then you would need to use dynamic component solution – Xesenix Aug 18 '19 at 20:19
  • @Xesenix I think part of my hang up is that, I agree it is the easiest, but is there some reason that makes it a bad practice? There must be a reason Angular and Angular Material have created their methods as opposed to what I have written. I do know the types of every element they would be adding (as in I have a model for every dynamic component and there is only set types they can add) – Eric Belisle Giddings Aug 18 '19 at 20:23
  • 1
    I can tell from my experience after about 1,5-2 years of using that simple solution I didn't see anything that would make it antipattern outside of coupling rendered components with the module that renders them. So it depends on how extensively you need to use that pattern. If only in a few places I would say additional work for handling dynamic components is not worth it in other case when you are for example creating reusable component that may use components that are not know in context of rendering loop go with dynamic components. – Xesenix Aug 18 '19 at 20:30

1 Answers1

1

The *ngFor syntax you're using is the standard approach

Dynamic components are more suited to situations where you don't know what components you might require. An example of this is a Modal Dialog, where the modal might have to display components of any type - or if you have a third party library that displays components passed in by the library user

If you don't need the outter div, you could use:

<ng-container *ngFor="let element of mediaElements">

</ng-container>

Where you have <div *ngIf="element.name == 'elementOne'">, you could use an *ngSwitch, where the switch case would be the element name - this would suit if there were a few different components you need to render. There's a good ngSwitch example on the Angular Docs site, that's pretty close to what you're doing (as far as I can tell)

Note: I'm not sure why you need the ngModel binding. It's better practice to dispatch events from the template, and update bindings in the component

Drenai
  • 11,315
  • 9
  • 48
  • 82
  • Thank you for the detailed answer! Also for pointing out the switch would be a much better way of dealing with it...also for the ngModel information...I have marked this as the correct answer because it details exactly what I need to know...I do wonder why now there is a difference between Angular Material Portals and Angular's Dynamic Component docs. That's just curiosity though this has helped a lot. Thank you! – Eric Belisle Giddings Aug 18 '19 at 21:48
  • 1
    The difference between CDK Portal and Dynamic Components docs is because CDK is an optional library. It adds wrappers around Angular functionality like Dynamic Components to make them easier to use – Drenai Aug 19 '19 at 10:23
  • thanks for that distinction. I knew the CDK is optional as it's part of Angular Material. I didn't know Portals were a wrapper around the Dynamic Components functionality though. – Eric Belisle Giddings Aug 20 '19 at 01:44