Angular - dynamické zobrazování komponent rubrika: Programování: JavaScript

1 petr.jajtner
položil/-a 2.10. 8:14
 
upravil/-a 2.10. 8:16

Ahojte vývojáři,
jsem v angularu 6 nováček, proto bych potřeboval radu od vás - odborníků. Mám několik podobných komponent reprezentující „dialogy“ na stránce. Ty potom v šablonách procházím takto:

wrapper.component.html:

<div *ngFor="let dialog of myService.dialogs"
     [dialog]="dialog">
</div>

dialog.component.html:

<h1 class="dialog__caption noselect draggable">
  {dialog.Caption}}
  <span class="dialog__toggle"
        (click)="dialog.toggle()">{{dialog.Minimized ? 'o' : 'n'}}</span>
</h1>
<div *ngIf="!dialog.Minimized"
      class="dialog__wrapper"
      [ngSwitch]="dialog.Id">
  <app-dialog-a *ngSwitchCase="'DialogA'"></app-dialog-a>
  <app-dialog-b *ngSwitchCase="'DialogB'"></app-dialog-b>
  <app-dialog-c *ngSwitchCase="'DialogC'"></app-dialog-c>
  <app-dialog-d *ngSwitchCase="'DialogD'"></app-dialog-d>
  <app-dialog-e *ngSwitchCase="'DialogE'"></app-dialog-e>
</div>

Zajímalo by mě, zdali by blok ngSwitch šel nahradit nějakým elegatnějším řešením, např. tak, že v dialogu bude už informace o komponentě a tu jednoduše „zobrazit“ místo těch app-dialog-*. Moje představa by byla přibližně tato:

...
<div *ngIf="!dialog.Minimized"
      class="dialog__wrapper">
  <{{dialog.Component}}></{{dialog.Component}}>
</div>
...

Doporučili byste mi něco, prosím, jak toto vyřešit?
Předem děkuji

Petr Jajtner

odkaz
6 dominios
odpověděl/-a 2.10. 9:28

Ja som nieco podobne teraz riesil, v Anugulari 6 to sice ide a funguje, ale rataj aj s tym, ze to nebude uplne bez problemov.

Tak ako ty, aj ja som si urobil nejaky wrapper komponent. Ten vo vnutri mal (okrem ineho) aj <ng-template appDynamicHost></ng-template>, cize template spolu s vlastnou direktivou appDynamicHost. Tato direktiva je sama o sebe prazdna, neobsahuje ziadnu logiku, len v kostruktore je definovana zavislost na ViewContainerRef aby sa s obsahom dalo neskor pracovat.

Cize vyzera takto:

import { Directive, ViewContainerRef } from '@angular/core';
 
@Directive({
    selector: '[appDynamicHost]'
})
export class DynamicHostDirective {
    constructor (public viewContainerRef: ViewContainerRef) { }
}

Naspat ku wrapperu. Ten ma idealne v ngOnInit kod, ktory s pomocou ComponentFactoryResolver dokaze dynamicky vytvorit akykolvek komponent a v podstate ho "vlozi" na potrebne miesto. Uryvok kodu moze vyzerat nejako takto:

const factory = this.componentFactoryResolver.resolveComponentFactory(TargetComponentClass);
const viewContainerRef = this.dynamicHost.viewContainerRef; // toto je referencia na nasu direktivu vo wrapperi
viewContainerRef.clear(); // v pripade ak sa to v case updatuje, radsej vycistit
 
const componentRef = viewContainerRef.createComponent(factory);
(<any>componentRef.instance).foo = 'bar'; // takto sa binduju @Input() premenne
 
this.templateInstance = <any>componentRef.instance; // ulozenie referencie na dynamicky komponent; takto s nim ak treba vieme dalej robit cokolvek

Miesto typu any odporucam pouzit nejaky vlastny interface, ktory budu mat vsetky takto dynamicky vytvarane komponenty spolocny, ale to nieje v tomto priklade relevantne.

Takto nejako (ak som na nic podstatne nezabudol) to bude fungovat. Predom vsak zdoraznim aj par problemov, ktore takyto pristup ma. Najdolezitejsie je, ze takto dynamicky vytvoreny komponent je akoby vytrhnuty z kontextu angularu a teda nebude dostavat ngOnChanges updaty a pod.. Ak je teda potrebne reagovat/zobrazovat v case ine data, tak sa to velmi vyrazne skomplikuje. Ja som to riesil tak, ze tu inicializaciu som prehodil do ngOnChanges uz vo wrapperi a ukladal si instanciu a tu dalej pouzival (nieco na sposob singletonu, aby som nemal ten isty komponent n krat zbytocne) a nasledne vsetky zmeny rucne posielam dalej do komponentu (cez this.templateInstance).

Odporucam si to vyskusat, ci ti tento postup bude fungovat. Ak to ale bude robit viac problemov, radsej to nepouzi. Ak naopak niekto vidi v tomto rieseni nejake pripomienky alebo sa to da urobit este lepsie, sem s tym, samemu by mi to velmi pomohlo :)

PS sorry ak tam je nejake typo alebo syntax chyba, kod pisem z pamati a anonymizujem :)

Pro zobrazení všech 2 odpovědí se prosím přihlaste:

Rychlé přihlášení přes sociální sítě:

Nebo se přihlaste jménem a heslem:

Zadejte prosím svou e-mailovou adresu.
Zadejte své heslo.