Aller au contenu principal

State

Aujourd'hui les états sont gérés par des services dédiés sans utiliser de solution de type "Store" (NgXS / NgRxStore).

Service as store

Des instances dédiées sont instanciées et injectées lors de la création du composant (i.e: ce ne sont pas des singletons).

Ex:

//Injection d'un service gérant la page de détail
@Component({
//...
providers: [TypologiesDetailService],
})
export class TypologiesDetailPageComponent {}
info

Dans le cas d'une page de détail avec onglets, on a découpé pour avoir un sous service pour chaque onglet. Les données communes sont dans le service parent au niveau de la page de détail et chaque onglets peut avoir son propre service dédié.
Lors de la mise à jour de données communes, on doit faire la mise à jour le plus haut possible dans la hierarchie de service pour notifier tous les observateurs.

Chaque donnée est gérée dans un BehaviorSubject. La majorité de ces services utilisent les données d'un resolver qui a pré-chargé les données avant de naviguer sur la page affichant le composant. La donnée du resolver permet d'instancier la valeur initiale de ce Subject.

Exemple de gestion d'une donnée par un service :

@Injectable()
export class TypologiesDetailService {
// les données du resolver utilisent un typage fort avec des constantes (ici : TYPOLOGIE_DETAIL_RESOLVER_KEY) et des types (ici : TypologieDetailResolverData)
//1. initialisation du behavior subject
private _typologie$ = new BehaviorSubject<Typologie>(
(
this.activatedRoute.snapshot.data[
TYPOLOGIE_DETAIL_RESOLVER_KEY
] as TypologieDetailResolverData
).typologie,
);
//2. exposition d'un observable
readonly typologie$ = this._typologie$.asObservable();

//3. getter
get typologie(): Typologie {
return this._typologie$.getValue();
}

//4. setter
set typologie(typologie: Typologie) {
this._typologie$.next(typologie);
}

constructor(
private activatedRoute: ActivatedRoute,
) {
//5. écoute des nouvelles valeurs résolus par un changement du router
this.activatedRoute.data.pipe(skip(1)).subscribe((data) => {
const resolvedData = data[
TYPOLOGIE_DETAIL_RESOLVER_KEY
] as TypologieDetailResolverData;
this.typologie = resolvedData.typologie;
});
}
}
  1. On initialise le behavior subject une valeur résolu statiquement (non observable)
  2. On expose un observable -> il sera utilisé lorsque l'on doit réagir à l'émission de nouvelles valeurs
  3. On expose un getter -> il sera utilisé lors d'un accès synchrone à la valeur (ex: appel à une API pour la typologie actuelle - on récupère l'id de la typologie en cours)
  4. On expose un setter -> si on autorise la donnée a être mise à jour depuis l'extérieur (ex: sous services)
  5. On écoute les nouvelles valeurs des changements de routes -> notamment pour les navigations vers une autre typologie depuis la même page (sans rafraichissement de l'app Angular)
remarque

Un sous service devrait être la façade du service parent mais parfois les 2 services ont été injectés séparement.
Ex:
Facade :
TypologieDetailService -> VisaService (redéfinit et délègue au service TypologieDetailService certaines méthodes) -> Composants onglet visa

Sans façade :
TypologieDetailService -> Composants onglet visa
+
VisaService -> Composants onglet visa