15 iulie 2022

Angular Architecture & Best Practices - ziua 4

State Management

Opțiuni: servicii, NgRx, ngrx-data, observable store

Altele neabordate: Akita, Ngxs, MobX

Tipuri de stare: specifică aplicației / sesiunii / de business


Servicii Angular

Este cea mai simplă metodă. 

Mai multe servicii pot avea legătură la un storage comun. 


NgRx 

  • reactive state management inspired by Redux; unify the events & derive state using RxJS
    • Redux + RxJS = NgRx 
    • Redux - popularizat de Facebook și React
  • o singură sursă de adevăr în privința stării datelor
  • furnizează date immutable
  • cost: complexitatea codului



// în componentă:

constructor (private store: Store<EntityState>) {} 

this.store.dispatch (new CustomerAction.GetCustomers()); // 1 

// customer.reducers.ts  cheamă servicii 

// customer.effects.ts  este un @Injectable și are metode adnotate cu @Effect care ret. un Observable

// customer.selectors.ts  face subscribe la Store, furnizează rezultatul în componentă


ngrx-data = NgRx simplificat, un wrapper (mai puțin cod - o linie de cod/entitate)


Observable Store

== pachet: @codewithdan/observable-store

== sursă unică a datelor, read only, notificări la schimbare de stare către cei care s-au abonat, păstrează un istoric al schimbării de stare (onBefore, onAfter), cod puțin & funcționează cu orice framework

== inclus într-un serviciu care extinde ObservableStore<TipulDeDate>

== include starea (TipulDeDate) prin this.getState()

Inițializare prin this.setState(data, actionName); // acțiunea se salvează pt logging

== componenta face subscribe la Observable store

12 iulie 2022

Angular Architecture & Best Practices - ziua 3

Comunicarea între componente

Dacă ierarhia componentelor e prea mare (prea multe @Input și @Output), există alternative - ambele se bazează pe subject/observable

  • event bus - injectat în ambele componente care doresc să comunice; componentele fac subscribe la evenimente (nu vor ști de unde vin datele); cuplare slabă;
  • observable service - un serviciu furnizează observables către componente, care pot face subscribe; vor ști de unde vin datele; cuplarea nu e la fel de slabă.


RxJS subjects:

4 feluri principale:

  • Subject (generic) - componentele fac subscribe și pot primi date - doar datele care se emit de la momentul abonării
  • BehaviorSubject - componentele care se abonează mai târziu pot obține date emise anterior (doar la ultima emitere)
  • ReplaySubject - componentele care se abonează mai târziu pot obține toate datele emise în trecut; se poate configura cât să se poată obțină
  • AsyncSubject - trimite cea mai nouă valoare (ultima) atunci când se încheie de tot emiterea.

Convenție: subject - private & observable - public

private subject: Subject<Type>;

observable: Observable<Type>;

// init

this.subject = new Subject();

this.observable = this.subject.asObservable();

// emitere eveniment

this.subject.next(obj);

// abonare

this.observable = this.subject.subscribe (data => { ... });


Event bus: folosește RxJS subjects - o componentă face subscribe la un eveniment, alta emite un eveniment (fără să știe cine va primi datele).

// ambele componente:

constructor (eventBus: EventBusService) {}

// în componenta care emite:

this.eventBus.emit (objOfChoice);

// în componenta care primește:

this.eventBusSubs = this.eventBus.on (EventType, data => {});

Dezavantaje: nu se știe cine declanșează evenimentul; mantenanța este mai dificilă; trebuie făcut și unsubscribe


Observable service

// în componenta care emite (DataService):

this.subject = new Subject();

this.observable = this.subject.asObservable();

this.subject.next (objToSend);

// în componenta care primește:

constructor (dataService: DataService) {}

this.subs = this.dataService.observable.subscribe (data => {});

Avantaje: se cunoaște de unde vin datele

Dezavantaje: cuplarea între observator (componenta care se abonează) și observat (care emite); trebuie făcut unsubscribe


unsubscribe ---> unsubscribe() în ngOnDestroy();

Alte variante: 

1. AutoUnsubscribe decorator - cu npm install 

  @AutoUnsubscribe()

  export class MyComponent implements OnInit, OnDestroy() { }

 // nu este nevoie de nimic în ngOnDestroy (), dar trebuie menționată metoda

2. SubSink - cu npm install 

  subs = new SubSink();

  this.subs = .... // trebuie inițializat cu fiecare subscripție nouă în ngOnInit, se vor adăuga subscripțiile

  this.subs.unsubscribe(); // trebuie apelat în ngOnDestroy

08 iulie 2022

Angular Architecture & Best Practices - ziua 2

 Structuring Components









Container-Presentation pattern: obtine date intr-un singur loc (container) si trimite la componentele de prezentare doar ce este necesar. Comunicare prin @Input / @Output

Child routes: ex. /customers/1/edit - o idee buna pt ca poti face bookmarking


@Output - cu EventEmitter

Complicatie: prea multe niveluri in care trebuiesc pasate datele





  • OnPush: Use the CheckOnce strategy, meaning that automatic change detection is deactivated until reactivated by setting the strategy to Default (CheckAlways). Change detection can still be explicitly invoked. This strategy applies to all child directives and cannot be overridden.
  • Default: Use the default CheckAlways strategy, in which change detection is automatic until explicitly deactivated.
Efectul OnPush: datele primite prin @Input nu se mai pot modifica in componenta dincolo de initializare; in general: cand se schimba proprietatile de @Input si cand se emite un eveniment. 

Recomandare: OnPush pe toate componentele de prezentare. Avantaje: performanta (mai ales cand sunt f multe componente), impiedicare modificare stare.


Clonare obiecte (pt refresh in aplicatie):

1) const obj2 = JSON.parse ( JSON.stringify (obj) ); // deep copy, dar are probleme cu tipul Date

2) serviciu pt clonare, in core:









3) Immutable.js:

> npm install immutable

import * from 'immutable';

immList = List<Obj> (this.objList);

return this.immList.toArray();

SAU

return fromJS (objList).toJS(); 


Mostenire componente: export class ComponentX extends BaseComponent 

07 iulie 2022

Angular Architecture & Best Practices - ziua 1

Arhitectura aplicatiilor

Aspecte de luat in considerare: 

* App overview * App features * Domain security * Domain rules * Logging * Services & Communication * Data models * Feature components * Shared functionality










i18n = internationalization


Organizing Features and Modules

LIFT = Localizare rapida, Identificare tipul de cod (sufixe component/service/dto), Flat structure, Dry (fara repetare cod -> reutilizare)

Organizare in functie de Features vs. Conventionala

- se prefera in functie de Features (sunt de sine-statatoare)

- conventionala este ok in interiorul unui feature, daca exista prea multe subfeatures -> ex. folder components (adauga un nivel ierarhic in plus)

Minim 1 modul per Feature (se poate importa lejer din afara)

Flat structure: evitare prea multe ierarhii (neplacut la refactorizari) -> chiar daca un Feature e inclus in altul, sa fie pe acelasi nivel cu el; ajuta si la localizare


Lazy loading of modules = nu sunt declarate in app.module.ts in imports

Core modules: servicii singleton folosite in mai multe locuri in aplicatie; se pot redenumi si "common". Se importa doar in Root Module.

Shared modules: componente, pipe-uri si directive reutilizabile. Se importa oriunde este nevoie in Feature Modules.


Creare custom library:

> ng generate library my-lib     # actualizeaza "paths" in tsconfig.json

> ng build my-lib

Publicare:

> cd dist/my-lib

> npm publish