11 aprilie 2020

Angular - ziua 10 - Componente dinamice

Componente dinamice - sunt create la runtime, momentul cand vor trebui sa fie afisate este controlat programatic, cu *ngIf sau cu Dynamic Component Loader

Dynamic Component Loader: componenta este adaugata la DOM prin cod (imperativ) (vs. declarativ, printr-un selector, la *ngIf)
Totusi, varianta prin *ngIf este mai usoara si cea recomandata.

Exemplu cu *ngIf pentru afisarea unei ferestre de eroare:

1. se creeaza o componenta noua, numita de exemplu AlertComponent, cu satelitii aferenti, si se importa in app.module.ts langa celelalte componente.
@Component({
  selector: 'app-alert',
  templateUrl: './alert.component.html',
  styleUrls: ['./alert.component.css']
})

2. In componenta se adauga:
@Input() message: string; // string settable from outside
@Output() closeEvent = new EventEmitter<void>(); // emits on onClose() for the outside

3. In Html, fereastra de alerta are un buton cu textul "Close", se adauga: (click)="onClose()" pentru a declansa emitter-ul din Typescript.

4. Componenta de alert este folosita in componenta din care vrem s-o chemam, cu *ngIf, trimitandu-i drept input [message] o variabila, iar pe output-ul (closeEvent) devenit input in acea componenta, se merge pe o metoda onHandleError din componenta curenta:
<app-alert [message]="error" *ngIf="error" (closeEvent)="onHandleError()"></app-alert>

5. In onHandleError() avem grija sa schimbam conditiile pentru care *ngIf este true, deci putem seta this.error = null acolo.

----

Varianta programatica ( mai grea, pentru acelasi scenariu )

1. Se creeaza o componenta noua, adnotata cu @Directive, care va reprezenta definitia unui obiect in DOM (de fapt gata sa fie introdus in DOM). ViewContainerRef reprezinta "un container unde se pot adauga una sau mai multe view-uri la componenta"

@Directive({
  selector: '[appPlaceholder]'})
export class PlaceholderDirective {
  constructor(public viewContainerRef: ViewContainerRef) {
  }
}

2. In Html-ul componentei unde vrem sa atasam componenta dinamica, declaram (este doar o declaratie, nu apare in DOM):
<ng-template appPlaceholder></ng-template> 

3. In TypeScript-ul componentei de mai sus vom realiza adaugarea programatica. Dar intai declaram variabilele clasei:
@ViewChild(PlaceholderDirective) alertHost: PlaceholderDirective;
private closeSub: Subscription;

4. Se injecteaza in constructor: private componentFactoryResolver: ComponentFactoryResolver

Intr-o metoda de showErrorAlert():
5. Se obtine din alertHost proprietatea de tipul ViewContainerRef declarata, se face clear() pe ea

6. Din "fabrica" de componente se obtine sub-fabrica de tipul AlertComponent:
const alertCompFactory = this.componentFactoryResolver.resolveComponentFactory(AlertComponent);

7. Se creeaza componenta dinamica cu ajutorul ei si se populeaza campul message cu ceva ce ar trebui sa primim ca parametru in metoda:
const componentRef = hostViewContainerRef.createComponent(alertCompFactory);
componentRef.instance.message = errorResponse;

8. Se face subscribe pe pe proprietatea closeEvent a componentei, care se salveaza intr-o subscriptie:
this.closeSub = componentRef.instance.closeEvent.subscribe(() => {
  this.closeSub.unsubscribe();
  hostViewContainerRef.clear();
});

Asadar, cand se apasa pe "Close", subscriptia in curs se termina, iar componenta se "curata" astfel disparand de pe ecran.

9. Metoda de mai sus este chemata pe ramura de tratare a erorii dupa login/logout.

Niciun comentariu: