Render Component Dynamically
Asked Answered
K

5

16

I have this array:

this.dashboard = [
     {name: '<app-incassi-provvigioni></app-incassi-provvigioni>'},
     {name: '<app-scheda-punti-vendita></app-scheda-punti-vendita>'}
]

I populate this array on the ngOnInit cycle. I was wondering how can I render the components when I read my array in the html like that:

<gridster [options]="gridsterOptions">
    <gridster-item [item]="item" *ngFor="let item of dashboard">
        <!-- your content here -->
        {{item.name}}
    </gridster-item>
</gridster>

Of course right now it returns the string contained in the object but I'm trying to find a solution to render the component. It is possible to do that?

More details: I am developing a dashboard type app where I retrieve the list of the user dashlet from the DB and I'm trying to render those components dynamically once the main app component is ready. using Angular 7 & Gridster2.

Kobold answered 28/3, 2019 at 8:27 Comment(5)
what about <div [innerHTML]="item.name"></div>....?Chirlin
@Edison it creates an empty div inside the dashlet container. it's not rendering the component.Kobold
Angular has a very good guide on using dynamic components. I think you should refer this: angular.io/guide/dynamic-component-loaderNevsa
So all the string (component-selectors) are a part of the same module ?Clop
why using tag ?! why you don't use CompnentClass as element in your array. this will make it easier in the component loader factory.Milson
I
9

You can inject component dynamically

Just create component with @Input() component;

export class DynamicContentComponent implements OnInit {

  @Input() component: any;
  @Input() data: any;

  constructor(public viewContainerRef: ViewContainerRef,
              private componentFactoryResolver: ComponentFactoryResolver) { }

  ngOnInit() {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.component);
    this.viewContainerRef.clear();

    const componentRef = this.viewContainerRef.createComponent(componentFactory);
    (<DynamicComponentRef>componentRef.instance).data = this.data;
  }
}

And use it in HTML

<app-dynamic-content [component]="someComponent" [data]="data"></app-dynamic-content>

Data example

someComponent = SchedaPuntiVendita;

Dynamic components should be added to entryComponents in your module

Also you can create some kind of factory which will receive some string and depends on it returns you component class

Factory example

@Injectable()
export class DynamicContentComponentFactory {

  constructor() { }

  get(type: ContentType): any {
    switch (type) {
      case 'scheda':
        return SchedaPuntiVendita;
    }
  }

And modify DynamicContentComponent a little

@Input() contentType: string;

constructor(..., private factory: DynamicContentComponentFactory) { }
...

const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.factory.get(this.contentType));

...
Introductory answered 28/3, 2019 at 9:42 Comment(1)
The ComponentFactoryResolver is no longer needed. The Angular team has streamlined it so you can directly create the component like this: this.viewContainerRef.createComponent(this.component);Seat
G
7

rather passing component tag name("app-incassi-provvigioni" in your case), pass the component name (TestComponenet), then call the function from your view and render it as dynamically with Directive.

eg:

<gridster [options]="gridsterOptions">
  <gridster-item [item]="item" *ngFor="let item of dashboard">
    <div class="inner">
      <ng-template dynamic-template> </ng-template>
    </div>
  </gridster-item>
</gridster>

dynamic-template is a directive, that help us to load the component.

Gunfire answered 28/3, 2019 at 8:43 Comment(3)
this sounds interesting, can you provide some code example please?Kobold
@LucaAlberto, i just did the sample code check the link: [link ] (stackblitz.com/edit/angular-yz8w1u). please mark as answer if this is suite for you. thanksGunfire
Man, you just saved my day. This answer should be the accepted answer.Render
N
4

Maybe you can use an angular switch for this:

<div [ngSwitch]="item.name">
    <app-incassi-provvigioni *ngSwitchCase="incassi"></app-incassi-provvigioni>
    <app-scheda-punti-vendita *ngSwitchCase="scheda"></app-scheda-punti-vendita>
</div>
Nibbs answered 28/3, 2019 at 8:32 Comment(0)
C
0

if you are using gridster2, actually there is reference so you could use npm package as https://www.npmjs.com/package/ng-dynamic-component .

Here is an example of my code:

 <gridster-item
      class="gridster-item"
      [item]="item.gridItem"
      *ngFor="let item of dashboard; let i = index"
      (itemResize)="getItemSize($event, i)"
    >
      <button class="button-remove-item" (click)="removeItem(item)">
        <img src="assets/remove-item.svg" />
      </button>
      <div class="gridster-item-inner">
        <ndc-dynamic
          [ndcDynamicInputs]="item.size"
          [ndcDynamicComponent]="item.component.data"
        ></ndc-dynamic>
      </div>
    </gridster-item>

Let me know if you have any issues.

Caution answered 30/12, 2022 at 14:27 Comment(0)
I
-1
this.dashboard = [
         {name: 'dashboard1'},
         {name: 'dashboard2'}
    ]

    <gridster [options]="gridsterOptions">
        <gridster-item [item]="item" *ngFor="let item of dashboard">
            <ng-container *ngIf="item.name === 'dashboard1'" *ngTemplateOutlet="dashboard1">
            </ng-container>
            <ng-container *ngIf="item.name === 'dashboard1'" *ngTemplateOutlet="dashboard2">
            </ng-container>
        </gridster-item>
    </gridster>


    <ng-template #dashboard1>
        <app-incassi-provvigioni></app-incassi-provvigioni>
    </ng-template>
    <ng-template #dashboard2>
        <app-scheda-punti-vendita></app-scheda-punti-vendita>
    </ng-template>
Imp answered 28/3, 2019 at 9:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.