Architecture
package.json: - le fichier bien connu qui vous permet de déclarer toutes les dépendances de votre projet. tsconfig.json – Configuration du complilateur TypeScript. package-lock.json : il s'agit d'un fichier propre au fonctionnement du Node Package Manager, qui a à voir avec les dépendances de vos dépendances. angular.json : la configuration pour Angular-CLI. Vous pouvez définir plusieurs valeurs par défaut et configurer également les fichiers inclus lors de la création de votre projet. Par exemple, c'est dans ce fichier que se trouve le préfixe que l'on a défini précédemment en ligne de commande. polyfill.ts : les différents navigateurs existants n'ont pas le même niveau de prise en charge des normes du Web. Les polyfills servent à lisser ces différences en uniformisant le comportement de votre code entre tous les navigateurs. node_modules : il s'agit du dossier qui contient toutes les dépendances dont notre application a besoin pour fonctionner. karma.conf.js : c'est le fichier de configuration de Karma, qui est un outil permettant d'exécuter des tests unitaires dans votre application. main.ts : c'est le point d'entrée principale de notre application. Il s'occupe de compiler notre application, et lance le module racine de celle-ci. e2e : ce dossier contient les tests end-2-end de votre application. Il s'agit de tests qui permettent de simuler des parcours dans votre application du point de vue de l'utilisateur, et s'assurer que tout va bien. Par exemple, « je me connecte », « je modifie mon identifiant », « je me déconnecte », etc. Si vous n'avez jamais entendu parler de ce genre de tests, vous pouvez ignorer ce dossier. Conservez-le quand même, au cas où vous souhaiteriez développer des tests plus tard pour votre application.
Élément clés
Angular est composé des éléments clés suivants :
- Composant : Ce sont les blocs de construction de base d'une application Angular pour contrĂ´ler les vues HTML.
- Modules : Un module Angular est un ensemble de blocs de construction angulaires de base comme les composants, les directives, les services, etc. Une application est divisée en morceaux logiques et chaque morceau de code est appelé "module" qui exécute une seule tâche.
- Templates : Ils représentent les vues d'une application Angular.
- Services : Ils sont utilisés pour créer des composants qui peuvent être partagés dans l'ensemble de l'application.
- Métadonnées : Elles peuvent être utilisées pour ajouter des données supplémentaires à une classe Angular.
DOM
Angular utilise le vrai DOM pour la manipulation et la mise à jour des éléments de la page web. Cela signifie que chaque fois qu'un élément de la page est modifié, le navigateur recalcule et réaffiche la structure complète du DOM.
Cependant, Angular utilise également des techniques de détection de changement pour minimiser le nombre de mises à jour du DOM. La détection de changement est un mécanisme interne d'Angular qui surveille les propriétés des composants pour détecter les changements et mettre à jour les éléments associés dans le DOM.
En utilisant des techniques de détection de changement efficaces, Angular peut réduire considérablement le nombre de mises à jour du DOM, ce qui permet d'améliorer les performances de l'application.
Change Detection
Le Change Detection (détection des changements) est le mécanisme d'Angular qui vérifie quand et comment l'interface utilisateur (template) doit être mise à jour suite à un changement de données dans le composant. Quand une variable change, Angular vérifie si cela impacte le DOM – et le met à jour automatiquement.
Comment ça fonctionne (classique avec [Zone.js](http://Zone.js))
Jusqu'à Angular v15 : Angular utilise Zone.js pour intercepter toute opération asynchrone (clicks, timeouts, HTTP, etc.) À chaque événement, Angular relance tout le cycle de vérification du DOM (change detection tree). Par défaut, Angular scanne tous les composants, même ceux non modifiés → moins performant.
A partir d'Angular 16 introduit les signals, qui permettent une détection ultra ciblée, sans scanner toute l'arborescence. Angular peut maintenant fonctionner sans Zone.js, grâce à une approche plus réactive avec signals.
Il y a deux grandes stratégies de Change Detection:
Default (Stratégie par défaut) Angular vérifie tous les composants DOM, à chaque événement asynchrone (click, HTTP, timer, etc.) en utilisant Zone.js. Fonctionnement : Chaque fois qu'un changement se produit (clic, saisie, requête...), Angular scanne tout le DOM lié aux composants. Même si rien n'a changé, Angular vérifie quand même → peut affecter les performances.
OnPush Angular ne vérifie un composant que si : Une entrée @Input() change (par référence), Un événement du composant se déclenche (click, output, etc.), Ou si on force manuellement la vérification (markForCheck(), detectChanges()).
Avantage : Angular ignore les composants qui n'ont pas changé → meilleures performances. Utile surtout pour des composants purs, qui ne modifient pas leur état tout seuls.
Component
Il est constitué par classe: c'est là où on trouve les données à afficher et la logiques applicative,Template: c'est la partie qu'on affiche à l'utilisateur et Les styles qui contiennent les styles CSS qui sont appliqués au template du component. et définit par le décorateur @Component. Depuis Angular 14+, les composants peuvent être autonomes (standalone) sans module
Template
Un template est un morceau de HTML qui détermine ce que le composant va afficher. (Imaginez qu'on construise une maison : le template serait comme le plan de la maison, indiquant où chaque pièce et chaque meuble doivent aller). De la même manière, un template Angular définit où les éléments de notre interface utilisateur doivent apparaître.
Selector
La propriété selector permet d'afficher le composant à l'emplacement d'une balise HTML spécifique \<app-root>
Data binding
La data binding (liaison de données) permet de synchroniser les données entre la vue (template) et le modèle de données associé, sans avoir besoin de manipuler manuellement le DOM (Document Object Model).
La data binding permet de créer une communication bidirectionnelle entre la vue et le modèle de données, de sorte que les modifications apportées à l'un sont automatiquement reflétées dans l'autre. Cela permet de simplifier la gestion de l'état de l'application et d'améliorer l'expérience utilisateur.
Il existe plusieurs types de data binding:
Interpolation
C'est un mécanisme qui permet de modifier le DOM à partir du modèle. "{{}}"
Property Binding
Property Binding: qui permet de valoriser la propriété du composant ou une directive à partir du modèle."[]"
Event binding
Event binding: qui permet d'executer la fonction portée du composant suite à un évenement emis à partir d'un élément du DOM. "(click)".
Two way data binding
Two way data binding: c'est la combinaison de Property Binding et Event Binding.Dans le cas, le composant se charge s'impacter le DOM en cas de chargement du modèle et le DOM avertit le composant d'un chargement via une émission d'événement. Two-Way Data Binding est réalisé en utilisant la syntaxe des parenthèses et des crochets [( )].
NgModel
NgModel est un directive qui permet de réaliser la liaison bi-directionnelle entre le composant et le template.
Cette liaison permet de synchroniser automatiquement les valeurs saisies par l'utilisateur avec la propriété de la classe de component, et inversement.
Il permet aussi de gérer l'état des champs du formulaire en temps réel.
Pour utiliser la directive ngModel, il faut d'abord importer le module FormsModule dans le module de l'application
Control flow
Permet de contrôler l'affichage des éléments du template en fonction de conditions ou boucles.
Standalone
Depuis Angular 15, Un composant standalone est une manière plus simple de construire des applications avec Angular. Il permet de créer des éléments réutilisables et indépendants, sans avoir besoin de configurer des modules complexes.
Démarrer sur un composant standalone Si le composant racine est souvent appelé AppComponent. On peut démarrer l' application directement à partir d'un composant sans avoir besoin d'un module spécifique.
- Tout d'abord, on assure d'avoir un fichier main.ts dans le projet Angular.
- Dans ce fichier main.ts, importez bootstrapApplication depuis @angular/platform-browser :
bootstrapApplication est une fonction qui prend un composant en argument et démarre l'application Angular à partir de ce composant.
Standalone Vs NgModule
NgModule organise l'application autour de modules, alors que standalone organise l'application directement autour des composants.
Avantage principal
- Standalone supprime le boilerplate des NgModules et rend les dépendances explicites au niveau du composant.
- Lisibilité: Avec standalone, un composant contient tout ce dont il dépend, ce qui rend le code plus lisible et plus prévisible.
- Architecture: Standalone favorise une architecture orientée composants, plus naturelle et plus découplée que l'approche modulaire des NgModules.
- Performance / lazy loading: Standalone simplifie le lazy loading en permettant de charger directement des composants sans passer par un module.
bootstrapApplication
BootstrapApplication est une fonction qui prend un composant en argument et démarre l'application Angular à partir de ce composant.
NgModule
Un module Angular est un mécanisme permettant de :
- regrouper des composants (mais aussi des services, directives, pipes etc...),
- définir leurs dépendances,
- et définir leur visibilité.
Un module Angular est défini simplement avec une classe (généralement vide) et le décorateur NgModule.
@NgModule prend en paramètre un unique objet possédant diverses propriétés dont les principales sont :
- declarations – Dans cette propriété, vous devez y déclarer les classes de vue de votre module. Par classes de vue, il faut comprendre components, directives et pipes;
- exports – Cette propriété permet de déclarer le sous-ensemble de déclaration qui sera visible par les autres modules;
- imports – Cette propriété permet de déclarer les modules dont dépend notre module;
- providers - Dans cette propriété, vous devez déclarer les services que vous allez créer dans le cadre de ce module. Ces services contribuent à alimenter la collection globale des services accessibles par tous les composants de l'application;
- define their dependencies,
Lifecycle de composant
Un composant Angular passe par plusieurs phases :
- création
- initialisation
- rendu
- vérification
- destruction.
Angular expose ces étapes via des hooks du cycle de vie qui permettent d'intervenir à des moments précis, par exemple pour initialiser des données, réagir aux changements, accéder au DOM ou nettoyer des ressources.
Les cycles de vie sont divisés en plusieurs étapes distinctes, chacune étant exécutée à un moment spécifique du cycle de vie
- ngOnChanges : cette méthode est appelée chaque fois qu'une propriété d'entrée (@Input) du component change. Elle permet de détecter les modifications de données et de réagir en conséquence.
- ngOnInit : cette méthode est appelée une fois que le component a été initialisé et que ses propriétés d'entrée ont été initialisées. Elle est généralement utilisée pour initialiser des données, appeler des services ou mettre en place des abonnements.
- ngDoCheck : cette méthode est appelée à chaque cycle de détection de changement (change detection) de l'application. Elle permet de vérifier si des changements ont été apportés aux données du component et d'agir en conséquence.
- ngAfterContentInit : cette méthode est appelée une fois que le contenu du component a été initialisé. Elle est utilisée pour effectuer des opérations sur le contenu du component, comme la manipulation du DOM.
- ngAfterContentChecked : cette méthode est appelée à chaque cycle de détection de changement après l'initialisation du contenu du component. Elle permet de vérifier si le contenu du component a été modifié et d'agir en conséquence.
- ngAfterViewInit : cette méthode est appelée une fois que la vue du component a été initialisée. Elle est utilisée pour effectuer des opérations sur la vue, comme la manipulation du DOM.
- ngAfterViewChecked : cette méthode est appelée à chaque cycle de détection de changement après l'initialisation de la vue du component. Elle permet de vérifier si la vue du component a été modifiée et d'agir en conséquence.
- ngOnDestroy : cette méthode est appelée juste avant que le component ne soit détruit. Elle est utilisée pour effectuer des opérations de nettoyage, comme la désinscription des abonnements ou la libération de ressources .
Hooks les plus utilisés en pratique
En réalité, sur un projet propre, on utilises souvent :
- ngOnInit
- ngOnChanges (si inputs)
- ngAfterViewInit (DOM / lib externe)
- ngOnDestroy
Les autres sont très rarement nécessaires
OnInit Vs Constructeur
En Angular, le constructeur d'un composant est appelé lorsqu'une instance du composant est créée. Le constructeur est principalement utilisé pour l'initialisation des variables et des dépendances du composant, mais il ne doit pas être utilisé pour effectuer des opérations lourdes, telles que des appels HTTP ou des manipulations du DOM, car le DOM n'est pas encore initialisé à ce stade.
C'est là qu'intervient la méthode ngOnInit. Elle est appelée après le constructeur, une fois que toutes les dépendances du composant ont été injectées et que le composant a été initialisé. La méthode ngOnInit est principalement utilisée pour effectuer des opérations qui doivent être exécutées après l'initialisation du composant, telles que les appels HTTP, la récupération des données, ou les abonnements aux observables.
Directive
Directive est un composant la seule différence qu'elle ne possède pas la fonctionnalité templating et définit par le décorateur @Directive.
Il y a deux type de directive:
- Structurelle: a pour but de modifier le DOM en ajoutant ou remplaçant un élément du DOM. Exemple : Les "Structural Directives" natives les plus utilisées sont *ngIf et *ngFor, *ngSwitchCase.
- Attribut: a pour but de modifier l'apparence ou comportement d'un élément du composant ou directive. Exemple : [ngStyle] qui permet d'appliquer des styles de manière dynamique à un élément du DOM.
[NgClass] permet d'attribuer dynamiquement des classes aux objets du DOM
Créer une directive structurelle personnalisée consiste à utiliser TemplateRef et ViewContainerRef pour contrôler dynamiquement l'affichage d'un bloc HTML en fonction d'une condition ou logique définie.
Directive Vs Component
Les Components sont des éléments autonomes qui peuvent être affichés sur la page et ont leur propre logique métier, tandis que les Directives sont des éléments qui sont appliqués à des éléments HTML existants pour modifier leur comportement.
ElementRef
ElementRef est une classe qui permet d'accéder à l'élément HTML auquel une directive est appliquée. Elle est utilisée pour manipuler le DOM et appliquer des styles ou des attributs à l'élément HTML.
La classe ElementRef est injectable dans une directive à l'aide de l'injection de dépendances.
@HostListener
@HostListener() est un décorateur permettant d'ajouter un "listener" sur l'élément sur lequel la directive est appliquée. Il est utilisé pour définir le comportement d'une directive en réponse à des événements sur l'élément hôte ou ses enfants.
@HostBinding
@HostBinding est lié à la propriété à l'élément hôte. Si une liaison change, HostBinding met à jour l'élément hôte.
input
Input() est introduit à la V17 qui permet à un composant parent de transmettre des données à son composant enfant. Lorsque on utilise input(), n'oubliez pas que :
- La valeur est accessible via une fonction (exemple: user())
- Les inputs sont en lecture seule
- Les inputs doivent être déclarés au niveau de la classe
Les différentes façons d'utiliser input()
- Input avec valeur par défaut
- Input requis
- Input avec transformation
Bonnes pratiques
- Utilisez les décorateurs de classe @Input() et @Output() au lieu des propriétés inputs et outputs des métadonnées @Directive et @Component
- Évitez les alias d'entrée et de sortie, sauf s'ils servent un objectif important (Exemple: @Input('aliasMessage') message: string \= '';). Les alias peuvent rendre le code plus difficile à lire et à comprendre.
- Initialisez les propriétés d'entrée avec une valeur par défaut (Exemple: @Input() message: string \= '';). Cela permet d'éviter les erreurs si la valeur n'est pas définie dans le composant parent.
output
Output() c'est une fonction qui permet au composant enfant d'envoyer des informations Ă son parent
comment ça fonctionne :
- output\<User>() crée un point de sortie qui peut émettre des valeurs de type User
- .emit() est utilisé pour envoyer une valeur au composant parent
- Dans le parent, on utilise les parenthèses () pour écouter l'output
- $event est une variable spéciale qui contient la valeur émise par l'output
outputFromObservable
outputFormObservable est une fonction introduite avec les nouvelles APIs Angular V17.3 (signals / interop RxJS) pour transformer un Observable en Output d'un composant.
Pourquoi c'est utile
- moins de code
- pas de subscribe() manuel
- gestion automatique du lifecycle (unsubscribe)
Output Vs outputFromObservable
- output() sert à émettre un événement que le composant décide lui-même(je crée et contrôle l'événement), tandis que
- outputFromObservable() sert à exposer un événement provenant d'un flux RxJS déjà existant(je relaie un flux existant).
OutputRef
OutputRef est l'objet retourné par output(), C'est lui qui représente ton output Angular
EventEmitter
EventEmitter est une classe d'Angular utilisée pour émettre des événements depuis un composant vers son parent.
Ce que fait EventEmitter
- permet de déclarer un @Output
- permet d'émettre des valeurs avec .emit()
- permet au parent d'écouter avec (event)
Output Vs EventEmitter
EventEmitter: C'est l'ancienne implémentation, c'est une classe basée sur RxJS, elle agit comme un stream + événement. elle expose : .emit() et .subscribe() problème tu manipules un output comme un Observable: confusion entre event UI et flux de données
output(): C'est la nouvelle manière de déclarer un output, ce n'est pas l'output lui-même, c'est une fonction de création son rôle : définir clairement qu'on crée un événement Angular, pas un stream
Angular remplace EventEmitter par output pour supprimer la confusion avec RxJS et offrir une abstraction d'événement plus claire, plus simple et mieux structurée.
viewChild
ViewChild permet d' accéder à une référence de composant enfant ou d'élément du template.
@Input Vs @Output
@Input permet de passer les données du composant parent vers son fils. Il permet d'établir une liaison de données unidirectionnelle entre le composant parent et le composant enfant, ce qui permet de transmettre des valeurs, des objets ou des tableaux entre les composants.
@Output permet de passer les données du fils vers le composant parent. Pour utiliser @Output, il est nécessaire de définir une propriété événementielle dans le composant enfant avec le décorateur @Output()
Shadow DOM
Shadow DOM permet de définir du comportement interne à notre DOM sans qu'il interfère sur les autres parties de notre application. Il permet aussi de définir du style spécifique. L'intérêt du shadowDom permet de séparer les styles et le javascript et de ne pas risquer d'impacter d'autre éléments de l'application
Les avantages du Shadow DOM sont :
- Encapsulation des styles et du code JavaScript des composants, pour éviter les conflits avec les autres parties de l'application.
- Création de composants Web réutilisables, avec un style et un comportement interne spécifiques.
- Possibilité de définir un style spécifique pour chaque composant, qui ne sera appliqué qu'à l'intérieur du Shadow DOM, sans affecter les autres éléments de la page.
Types d'encapsulation
Angular fournit trois mode d'encapsulations:
- None: où comme on le devine, le Shadow DOM ne sera pas utilisé. Le CSS ne sera pas encapsulé non plus.
- Emulated (par défaut): le Shadow DOM ne sera pas utilisé non plus mais une encapsulation du CSS sera faite par Angular.
- Native: Angular créera un Web Component complet avec l'utilisation du Shadow DOM et du CSS scopé au component.
Angular provides three modes of encapsulations:
Pipe
Pipe est un filtre utilisable depuis la vue afin de transformer les valeurs lors du binding.
Les pipes sont utilisés dans la syntaxe de liaison de données (interpolation, property binding, event binding) pour modifier le résultat affiché.
Pour créer un pipe personnalisés, il faut créer une classe implémentant l'interface PipeTransform. Cette interface ne contient qu'une seule méthode à implémenter avec la signature suivante: transform(value: any, ...args: any[]): any;
Pipes sont purs (Stateless). C'est à dire que leur méthode transform est une fonction pure, la valeur retournée ne dépend que des paramètres reçus et les appels n'ont aucun effet de bord. Autrement dit, le Pipe est "stateless".cela signifie que le pipe ne conserve aucun état interne entre les appels à la méthode transform(). Cela signifie que le pipe n'a pas de mémoire interne et que la valeur de sortie ne dépend que des données d'entrée et des arguments de la méthode transform().
Pipes impurs sont évalués à chaque [Change Detection](file:///angular/change-detection) même quand les paramètres ne changent pas. Ils sont déclarés en passant la valeur false au paramètre pure du décorateur Pipe.
Nativement, seuls les Pipes suivant sont impurs:
- async : Il est impur car les valeurs retournées par ce Pipe peuvent changer à n'importe quel moment étant donné qu'elles proviennent d'une source asynchrone (Observable ou Promise).
- json : Etant donné qu'il est principalement utilisé pour le "debug", ce Pipe est impur car il est préférable que la valeur retournée soit mise à jour même si l'immutabilité n'est pas respectée.
- slice : Bien que ce Pipe fonctionnerait parfaitement en étant pur, il est probablement impur pour faciliter l'adoption d'Angular par la communauté car malheureusement l'immutabilité des Arrays est rarement respectée par les développeurs Angular.
Async est une Pipe capable de consommer les valeurs observables ou promises en appelant implicitement la méthode subscribe ou then afin de binder les valeurs contenus des observables ou promises.
Métadonnées
Les métadonnées sont utilisées pour décorer une classe afin qu'elle puisse configurer le comportement attendu de la classe. Les métadonnées sont représentées par des décorateurs :
- Décorateurs de classe par exemple @Component et @NgModule
- Décorateurs de propriétés par exemple @Input et @Output
- Décorateurs de paramètres exemple @Inject,
Forms
Il existe différentes façon d'implémenter les formulaires:
- [Template-driven Forms](file:///angular/formulaires/template-driven-forms) (à éviter) : inspirée du "two-way binding" utilisé dans AngularJS, cette approche a de nombreuses limitations et s'avère rapidement fastidieuse à implémenter, peu extensible et peu efficace.
- [Reactive Forms](file:///angular/formulaires/reactive-forms) (à adopter) : cette approche vient appuyer le paradigme "Reactive Programming" qui fait parti des fondements d'Angular avec : une meilleure séparation de la logique du formulaire et de la vue, une meilleure testabilité, des Observables, la génération dynamique de formulaires etc..
Template driven forms Méthode template : créer le formulaire dans le template, angular l'analyse puis vous met à disposition les différents inputs
La [Directive](file:///angular/directives) ngModel est au coeur des "Template-driven Forms".
- Elle permet principalement de "binder" dans les deux sens le "model" avec la "view". C'est ce que l'on appelle le "Two-way Binding". La syntaxe [(property)]="data" n'est que du "syntactic sugar" pour signifier [Input](file:///angular/interaction-entre-composants/input) + [Output](file:///angular/interaction-entre-composants/output). Pour profiter de la directive NgModel, il faut importer le module FormsModule dans les modules contenant des composants qui en dépendent
L'un des principaux problème des "Template-driven Forms" est le non respect de l'immutabilité.
Reactive Forms
Methode reactive : on crée le formulaire dans le template et dans le code Type script puis brancher les deux ensembles
Avantage:
Les "Reactive Forms" utilisent des Observables pour faciliter et encourager le "Reactive Programming". La logique des formulaires se fait dans le code TypeScript. Le formulaire devient alors plus facile à tester et à générer dynamiquement.
FormControl La classe FormControl est une indirection permettant de contrôler et d'accéder à l'état des "controls" de la vue (e.g. \<input>).Pour profiter de la directive FormGroupDirective, il faut importer le module ReactiveFormsModule dans les modules contenant des composants qui en dépendent.
Pour profiter de la directive FormGroupDirective, il faut importer le module ReactiveFormsModule dans les modules contenant des composants qui en dépendent.
FormControl fournit différentes propriétés et méthodes permettant de piloter le "control":
- value: permet d'accéder à la valeur actuellement contenu dans le "control".
- valueChanges: est un Observable permettant d'observer les changements de valeur du "control".
- reset, setValue et patchValue permettent de modifier l'état et la valeur du "control".
FormGroup permet de regrouper des "controls" afin de faciliter l'implémentation, récupérer la valeur groupée des "controls" ou encore appliquer des validateurs au groupe.
Le FormGroup est construit avec un "plain object" associant chaque propriété à un FormControl. Il est alors possible d'accéder aux valeurs et aux "controls" via la propriété associée.
Pour éviter d'accéder aux controls via la propriété FormGroup.controls, les "Reactive Forms" implémentent également une directive FormGroupDirective qui permet d'associer un FormGroup à un élément du DOM (form en général mais on peut utiliser d'autres éléments). Cela permet alors d'associer les "controls" via leur nom à l'aide de l'Input formControlName.
La classe FormGroup propose de nombreuses propriétés et méthodes similaires à celles rencontrées précédemment avec FormControl. En effet, FormControl et FormGroup héritent de la classe abstraite AbstractControl.
Un FormGroup est donc également un "control" et il est donc possible de construire une arborescence de FormGroups afin de regrouper différentes parties d'un formulaire de taille importante ou dont des parties sont réutilisables dans d'autres contextes.
FormArray héritant également de la classe abstraite AbstractControl, permet de créer un "control" contenant une liste de "controls" (e.g. FormControl ou FormGroup) ordonnés (contrairement à FormGroup qui permet de créer un groupe de "controls" accessibles avec des clés sur un "plain object").
La valeur contenu dans le FormArray est de type Array.
FormBuilder
Le module ReactiveFormsModule implémente également un service FormBuilder qui permet d'ajouter un peu de "syntactic sugar" afin de simplifier la création des FormGroup, FormArray ou FormControl.
Injection de dépendance
La "Dependency Injection" est un "design pattern" qui consiste à séparer l'instanciation (et donc l'implémentation) d'une dépendance et son utilisation. Ce "design pattern" permet :
- d'inverser les dépendances,
- d'éviter le couplage fort avec les dépendances,
- de factoriser l'instanciation d'une dépendance,
- de faciliter le remplacement d'une dépendance par une autre implémentation à des fins fonctionnelles ou de "testing".
- avoid strong coupling with dependencies,
Service
Service est une classe, qui peut contenir du code réutilisable, ou des données qu'on veut partager entre plusieurs composants et de la décorer avec le décorateur @Injectable().
Le décorateur @Injectable() permet à Angular de créer une instance du service et de le rendre disponible pour l'injection dans d'autres classes qui en ont besoin. Cela facilite la création de services réutilisables et permet d'éviter la duplication de code et de données entre différents composants.
Il a Trois type pour déclarer le service
- via Module: Pour déclarer un service dans un module en utilisant la propriété "providers" du décorateur @NgModule(). Cela permet de rendre le service disponible pour l'injection dans tous les composants du module.
- service portée (dans le composant): Pour déclarer un service directement dans un composant en utilisant la propriété "providers" du décorateur @Component(). Cela permet de rendre le service disponible uniquement pour ce composant et ses enfants.
- Tree-Shakable Services (providedIn): Cette méthode permet de déclarer le service directement dans le module racine de l'application, ce qui le rend disponible pour l'injection dans tous les composants de l'application. Pour cela, on utilise le décorateur @Injectable() avec l'option "providedIn: 'root'".
- Tree-Shakable Services (providedIn): This method declares the service directly in the root module of the application, making it available for injection into all components of the application. To do this, we use the @Injectable() decorator with the "providedIn: 'root'" option.
ProvidedIn: 'root'
ProvidedIn permet d'enregistrer le service au niveau global de l'application avec un singleton.
provider root Vs provider local
- root : une seule instance partagée
- local (dans composant/module) : nouvelle instance dans ce scope
HttpClient
Le service HttpClient a pour avantages de :
- simplifier l'implémentation d'échanges HTTP,
- fournir les outils nécessaires pour faciliter l'implémentation des "tests unitaires",
- se baser sur les Observables permettant ainsi : — d'annuler les requêtes si nécessaire,
- de suivre la progression d'"upload" et de "download",
Utilisation de HttpClient
HttpClient est un service Angular ; on peut donc le récupérer avec la [Dependency Injection](file:///angular/dependency-injection).
Etant donné que le service HttpClient est stateless, nous pouvons importer le module HttpClientModule directement dans notre [Feature Module](file:///angular/project-structure-and-modules/feature-module) BookModule.
En effet, les méthodes get, delete, patch, post, put, request etc... retournent toujours un Observable.
Router-outlet
Pour indiquer l'emplacement d'insertion du composant, il faut utiliser la directive \<router-outlet> directement dans le "root ou child component"
Accès aux paramètres
Le service ActivatedRoute décrit l'état actuel du "router". Il permet au composant associé à la "route" de récupérer les paramètres via les propriétés paramMap et queryParamMap.
Les propriétés paramMap et queryParamMap sont des Observables car par optimisation, en naviguant vers la même route mais avec des paramètres différents
Pour simplifier la récupération des paramètres, il est également possible d'utiliser la propriété snapshot qui contient l'état actuel de la route (e.g. : snapshot.paramMap.get('bookId')).
Le risque dans ce cas est de ne pas mettre à jour la vue en cas de navigation vers la même route avec des paramètres différents.
To simplify the retrieval of parameters, it is also possible to use the snapshot property which contains the current state of the route (e.g. : snapshot.paramMap.get('bookId')).
Lazy Loading
Lazy loading consiste à charger des objets uniquement lorsque l'on y accède explicitement.
Le chargement tardif permet de réduire le temps de chargement initial de l'application, car seuls les modules et les composants nécessaires sont chargés. Cela permet également d'améliorer les performances globales de l'application, car les ressources sont chargées de manière asynchrone en arrière-plan.
ForRoot Vs ForChild
les méthodes forRoot() et forChild() sont utilisées pour fournir des services et des configurations à un module et à ses enfants respectivement.
La méthode forRoot() est utilisée dans le module principal de l'application (app.module.ts) pour fournir des services et des configurations qui seront utilisés dans toute l'application. Elle est appelée une seule fois et permet de configurer des services globaux qui seront partagés entre tous les composants et modules de l'application.
La méthode forChild() est utilisée dans des sous-modules pour fournir des services et des configurations spécifiques à ce module et à ses enfants. Elle est appelée chaque fois qu'un module enfant est chargé avec le chargement tardif (lazy loading) et permet de configurer des services et des configurations spécifiques à ce module.
Preloading strategy
Preloading strategy consiste à charger ces modules de manière asynchrone après le chargement initial de l'application, de manière à ce qu'ils soient disponibles immédiatement lorsque l'utilisateur accède à la route correspondante. Cela permet d'éviter la latence de chargement des modules "Lazy Loaded Routes" et d'améliorer la performance globale de l'application.
Route Guards
Guards permettent de protéger l'accès à certaines routes en fonction de certaines conditions. Les Route Guards sont exécutés avant que la route ne soit activée et permettent de prendre une décision quant à la suite de l'exécution de la navigation.
Les "Guards" sont ajoutés au niveau de la configuration du "Routing"
ll existe trois types de Route Guards en Angular :
- CanActivate : Permet de protéger l'accès à une route en fonction de certaines conditions, comme la présence d'un token d'authentification ou l'appartenance à un groupe d'utilisateurs.
- CanActivateChild : Permet de protéger l'accès à toutes les routes enfants d'une route protégée par CanActivate.
- CanDeactivate : Permet de protéger la sortie d'une route en fonction de certaines conditions, comme la présence de données non sauvegardées ou la confirmation de l'utilisateur.
CanActivate
CanActivate est un service qui implémente l'interface CanActivate.
Cette méthode est appelée à chaque demande d'accès à la "route" ; elle doit alors retourner une valeur de type boolean ou Promise\<boolean> ou Observable\<boolean> indiquant si l'accès à la "route" est autorisé ou non.
Il est donc possible d'attendre le résultat d'un traitement asynchrone pour décider d'autoriser l'accès ou non.
En cas de refus d'accès, il est possible de rediriger l'utilisateur vers une autre "route" "manuellement" en injectant le service "Router"
CanDeactivate
CanDeactivate sont couplées aux composants car elles doivent communiquer avec le composant pour établir leur décision d'accès. Cette méthode est appelée à chaque fois que l'utilisateur souhaite quitter la route (clic sur un lien ou déclenchement automatique) ; elle doit alors retourner une valeur de type boolean ou Promise\<boolean> ou Observable\<boolean> indiquant si l'accès à la "route" est autorisé ou non. Contrairement au "Guards" d'activation, cette "Guard" prend en premier paramètre l'instance du composant. C'est pour cette raison que l'interface CanDeactivate est générique.
CanLoad
canLoad permet de gérer le chargement (ou non) des modules en lazy loading.
La méthode CanLoad est exécutée avant le chargement d'un module "Lazy Loaded Route" et permet de décider si le module doit être chargé ou non en fonction de certaines conditions. Si la méthode renvoie true, le module est chargé de manière asynchrone et est rendu disponible pour l'utilisation. Si la méthode renvoie false, le chargement du module est annulé et la navigation est bloquée.
Resolvers
Les Resolvers permettent d'attendre le retour d'un observable avant d'initialiser / mettre à jour un composant après une mise à jour de l'url. Le Resolver est une classe que l'on associe à la route du composant. Leur utilisation classique est la récupération de données.
Les Resolvers sont associés à une route spécifique dans le fichier de configuration du routage (app-routing.module.ts). Lorsque la route est activée, le Resolver est exécuté et récupère les données nécessaires à l'affichage du composant. Une fois que les données sont récupérées, le Resolver renvoie un Observable qui permet de s'assurer que les données sont disponibles avant l'affichage du composant.
APP_INITIALIZER
Les APP_INITIALIZER permettent de reporter l'initialisation d'un module Angular à la résolution d'une promesse. Nous pouvons nous en servir pour forcer Angular à attendre que certaines données soient récupérées avant de commencer le rendu.
APP_INITIALIZER est une clé d'injection (injection token) du coeur d'Angular. Vous pouvez déclarer pour cette clé d'injection un ou plusieurs providers.
Conclusion
Les Resolver, Guard et App initalizer permettent d'éviter à vos composants de passer par des états intermédiaires rapidement obsolètes. Ce faisant, ils permettent d'améliorer la qualité du rendu. En les utilisant, vous améliorez la modularité et la testabilité de votre code.
Observable
En programmation réactive, un Observable est un objet qui représente une source de données asynchrones et qui peut émettre plusieurs valeurs dans le temps.
Un Observable peut émettre des données de manière asynchrone à différents moments et permet de traiter ces données de manière réactive à mesure qu'elles arrivent.
En Angular, les Observables sont souvent utilisés pour gérer les données asynchrones telles que les réponses de requêtes HTTP, les événements de l'interface utilisateur ou de flux de données externes.
Un Observable peut émettre plusieurs types de valeurs, y compris des erreurs, des événements de complétion et des valeurs de données. Il permet également de combiner, filtrer, transformer ou concaténer des flux de données.
Lorsqu'un Observable est créé, il ne commence pas immédiatement à émettre des données. Au lieu de cela, il doit être souscrit (subscribed) à partir d'un composant ou d'un service pour démarrer le flux de données et recevoir les valeurs émises par l'Observable.
Subscribe
Subcribe est une méthode qui permet de souscrire à un observable (d'observer un obbservable) et de réagir à ce changement.
Il peut prendre 3 arguments :
- next: la fonction qui sera appelée à chaque fois qu'une nouvelle valeur sera émise.
- erreur: la fonction qui sera appelée si une erreur se produit pendant l'observation. Cette fonction prend un seul argument, qui est l'erreur
- complète: la fonction qui sera appelée lorsque l'observable est terminé et qu'il n'émettra plus de valeurs. Cette fonction ne prend aucun argument
- error
- complete
Of
Of est la méthode la plus simple et permet de créer un observable n'envoyant qu'une seule valeur.
Unsubscribe
unsubscribe() détruit la souscription à la fin de la vie du component et évite toutes les comportements infinies.
Subject
Subject() est un type d'observable qui permet de recevoir les informations et de choisir les informations émises.
Eviter memoire leak
Pour éviter la memoire leak, il faut se désabonner correctement :
- async pipe
- takeUntil
- take(1)
- first()
- destruction automatique via patterns Angular récents
Subject et BehaviorSubject
Subject : n'émet que les nouvelles valeurs BehaviorSubject : conserve et émet la dernière valeur connue aux nouveaux abonnés
TakeUntil
TakeUntil est un opérateur de RxJS qui permet de désabonner automatiquement un observateur (ou "subscriber") d'un observable lorsqu'un autre observable émet une valeur. Il permet de simplifier la gestion des abonnements en évitant les fuites de mémoire et en garantissant que l'observateur est toujours correctement désabonné.
Type Observable
Les observables sont généralement classés comme Hot ou Cold , selon leur comportement en matière d'émission.
Un objet Cold Observable est celui qui commence à émettre sur demande (abonnement), tandis qu'un objet Hot Observable est celui qui émet indépendamment des abonnements.
- Observable Froid : Une source qui va démarrer pour chaque souscription
- Observable Chaud: Une seule source qui est diffusée simultanément toutes les souscriptions
Cold observables peut ĂŞtre convertie en une Hot Observable avec une simple publish
Multicast Observable
L'observable multicasting (multicast observables) est une technique dans la programmation réactive qui permet à un seul flux de données (observable) d'être partagé et consommé par plusieurs observateurs (subscribers) en même temps. Cela permet d'éviter de dupliquer le flux de données pour chaque observateur, ce qui peut entraîner des problèmes de performance et de consommation de mémoire.
Dans un observable multicasting, l'observable est partagé en amont (upstream) en utilisant un opérateur de multicasting tel que share, publish, ou multicast. Cet opérateur crée un connectable observable, qui peut être connecté ou déconnecté à volonté. Les observateurs peuvent alors se connecter au connectable observable en utilisant la méthode connect.
Promise Vs Observable
- Promise produces a single value, observable produces a value stream
Rxjs
Rxjs est une bibliothèque javascript pour la composition de programmes asynchrones et basés sur des événements en utilisant des séquences d'observables A partir de cette séquence :
- On peut collecter des différences sources des données
- On peut modifier dans un pipeline grâce aux opérateurs
- On peut aussi combiner les données.
Avec Rjxs, On distingue trois éléments ou événements qui peuvent arriver
- Valeur next() qui fait appel Ă une prochaine valeur si la valeur existe
- Erreur error() qui peut apparaître pendants que nous appelons les éléments qui ne sont pas arrivés
- Signal complete() qui nous explique que l'ensemble des éléments que nous attendons à travers de notre séquence sont arrivé
Opérateur Rxjs
Opérateur Rxjs se place entre l'observable et la souscription. Il peut éditer ou filtrer les données émises par l'observable avant qu'elles n'arrivent à la souscription.
- map(): modifie chaque valeur émise par une observable selon la fonction déclarée et ensuite envoyé à la souscription
- filter() : qui va filtrer les données émises avant d'envoyer à la souscription.
- throttleTime() : va imposer un délai minimum entre deux valeurs reçues
- scan() et reduce() : qui permet de réunir toutes les valeurs émises par une observable selon une fonction définie(Reduce rends simplement le résultat.Scan va toutes les étapes avant le résultat)
- switchMap : Cette fonction est utilisée pour transformer un flux de données en un autre flux de données, en fonction d'une fonction de transformation fournie en argument. Cette fonction est souvent utilisée pour effectuer des appels HTTP asynchrones et récupérer des données à partir d'une API.
- concatAll est une méthode RxJS qui est utilisée pour concaténer des observables en un seul observable. Cette méthode est utile lorsque vous avez besoin d'observer des séquences d'événements en séquence, c'est-à -dire d'attendre qu'un observable se termine avant de commencer à observer le suivant.
- exhaustAll convertit un observable d'ordre supérieur en un observable de premier ordre en abandonnant les observables internes alors que l'observable interne précédent n'est pas encore terminé.
Combiner Observable
- forkJoin: combiner la sortie de plusieurs observables
- combineLatest \=> combineLatestWith[link](https://rxjs.dev/api/operators/combineLatestWith#combinelatestwith): écouter un groupe d'observables L'opérateur combineLatest est similaire au forkJoin à une distinction près. Il ne se termine pas une fois qu'il a reçu les résultats de chaque observable, mais continue d'écouter, et émet de nouveau à chaque fois qu'un des observables sources a émis de nouveau. Il réémet alors la liste de toutes les dernières valeurs émises par chaque observable
- mergeMap: utiliser la sortie d'un observable en entrée d'un autre,mergeMap crée un observable à partir d'un observable source qui envoie sa sortie en entrée d'un autre observable
Pipe Rxjs
En RxJS, un pipe est une fonction qui permet de composer une chaîne d'opérateurs pour transformer un flux de données asynchrones, tel qu'un Observable. Le pipe permet de chaîner des opérateurs ensemble pour appliquer des transformations successives sur les données émises par l'Observable.
Le pipe peut être utilisé pour transformer les données émises par un Observable en utilisant des opérateurs RxJS tels que map, filter, reduce, merge, switchMap, catchError,
JIT
JiT : Just-in-Time
Par défaut, la compilation des templates d'une application va être effectuée pendant l'exécution de l'application, c'est à dire que Angular va compiler à la volée les templates. C'est ce qu'on appelle la compilation JiT qui est utilisée via la commande ng build ou ng build --prod --no-aot.
Dans cette configuration, le bundle JavaScript généré va intégrer les templates de l'application sous une syntaxe HTML.
Ce mécanisme a deux effets négatifs. Le premier est que le bundle Javascript va être plus gros puisque les sources de l'application devront intégrer le compilateur de templates (dans le fichier vendor.bundle.js). Le second point est que l'application va devoir compiler les templates lors de son exécution ce qui aura nécessairement un impact négatif sur le temps d'affichage.
AOT
AoT : Ahead-of-Time
La compilation des templates est invariante, c'est-à -dire qu'un même template engendrera toujours la même fonction (de la même manière que la compilation TypeScript par exemple). Cela veut dire que la compilation des templates peut faire partie du process de build, puisqu'elle n'est pas dépendante de son contexte d'utilisation.
Les avantages de cette compilation sont clairs : on gagne en taille de bundle et on gagne en performance (étant donné que la phase de compilation des templates n'a pas à être effectuée). L'autre avantage important est que l'on va être capable de détecter les erreurs de syntaxe dans les templates (appel à une méthode du component inexistante, utilisation d'une variable non déclarée, etc.) lors de la phase de build, puisque les templates vont être compilés, plutôt qu'à l'exécution.
NgRx
Ngrx (Angular+RxJs+Redux)
NgRx est une bibliothèque open-source qui permet de gérer l'état (state) d'une application en utilisant le pattern Redux.
Le pattern Redux est une architecture de flux de données qui propose une façon unidirectionnelle de gérer l'état d'une application en utilisant le store qui contient l'état global de l'application. Les changements d'état sont effectués à travers des actions qui sont envoyées au store, puis traitées par des reducers qui décrivent comment l'état doit être mis à jour en réponse à une action donnée.
NgRx implémente le pattern Redux en fournissant des modules pour créer des stores, des actions et des reducers en utilisant la librairie RxJS pour gérer les flux de données asynchrones.
Fonctionnement du Redux( How does Redux work)
Redux génère l'objet javascript appelé le store Store contient le globale State de composant Actions qui représentent les différentes interactions que l'utilisateur peut faire, il s'agit de déclencheur. Enfin les actions se transmissent dans les Reducers qui définissent les actions du State
3 règles ou principaux du redux
- Le global state est unique
- Le state est en lecture seule (si on modifie le state, on doit passer par l'Actions. Sinon redux ne sera pas que ça n'a pas changé, il y a une forte chance que le Framework ne redésigne pas la vue et c'est pas traçable)
- Reducers sont des fonctions pures ( car le reducers sont des fonctions qui collent les changements et ne doivent pas prendre en compte des effects de bord, pas des requêtes ajax dépendante du temps, le Reducers ne fait que du synchrone)
Avantage redux
- Base de donnée centrale
- Amélioration de la perfomance : onPush
- Simple communication entre composants
createAction
La fonction createAction() est une fonction utilitaire fournie par la bibliothèque NgRx qui permet de créer des actions pour le store Redux.
Lorsqu'un événement se produit dans une application, cela peut nécessiter une mise à jour de l'état global de l'application, stocké dans le store Redux. Une action est un objet qui décrit l'événement qui s'est produit et qui est envoyé au store Redux pour déclencher la mise à jour de l'état.
La fonction createAction() prend en argument un type d'action, qui est une chaîne de caractères qui décrit l'événement qui s'est produit, ainsi que des paramètres qui seront utilisés pour construire l'objet d'action.
createReducer
La fonction createReducer() est une fonction utilitaire fournie par la bibliothèque NgRx qui permet de créer des réducteurs pour le store Redux.
La fonction createReducer() prend en argument un état initial et un ensemble de fonctions de réduction qui prennent en charge la mise à jour de l'état global en fonction de l'action reçue. Chaque fonction de réduction reçoit en argument l'état actuel de l'application et l'action qui a été envoyée au store Redux.
on
La fonction on() est une fonction utilitaire fournie par la bibliothèque NgRx qui permet de définir les fonctions de réduction qui doivent être exécutées en réponse aux actions spécifiques.
La fonction on() prend en argument une action et une fonction de réduction qui doit être exécutée en réponse à cette action. La fonction de réduction prend en argument l'état actuel de l'application et l'action qui a été envoyée au store Redux, et retourne un nouvel état de l'application.
Selector
Selectors sont des fonctions qui permettent de sélectionner une partie de l'état global de l'application stocké dans le store Redux.
Les selectors sont créés en utilisant la fonction createSelector() fournie par la bibliothèque NgRx. La fonction createSelector() prend en argument un ou plusieurs sélecteurs et une fonction qui combine les résultats des sélecteurs pour produire le résultat final.
Les sélecteurs peuvent être des fonctions simples qui sélectionnent une propriété spécifique de l'état global, ou des fonctions plus complexes qui combinent plusieurs propriétés pour produire un résultat plus complexe.
Les selectors peuvent être utilisés dans les composants Angular pour récupérer des données de l'état global stocké dans le store Redux. Les selectors permettent de séparer la logique de récupération des données de la logique d'affichage dans les composants, ce qui facilite la maintenance du code.
Select
Select est un service fourni par la bibliothèque NgRx qui permet de sélectionner une partie de l'état global de l'application stocké dans le store Redux.
La classe Select permet de créer des observables qui émettent des valeurs chaque fois que l'état global de l'application stocké dans le store Redux est mis à jour. Les observables émis par la classe Select sont appelés des "selectors observables".
Les selectors observables permettent de récupérer des données de l'état global stocké dans le store Redux dans les composants Angular de manière réactive. Les selectors observables peuvent être utilisés avec les pipes async dans les templates Angular pour afficher les données dans les composants.
RunTimeCheck
RuntimeChecks sont des options de configuration qui permettent de détecter les erreurs courantes dans l'utilisation de la bibliothèque NgRx.
Les runtimeChecks peuvent être utilisés pour détecter des erreurs telles que :
- L'utilisation d'une action qui n'est pas définie dans le reducer.
- L'utilisation d'un reducer qui ne retourne pas un état de l'application valide.
- L'utilisation d'un effet qui ne retourne pas une action valide.
- La mise à jour de l'état de l'application à l'extérieur du reducer.
- Les runtimeChecks peuvent être configurés au niveau de l'application en utilisant la fonction StoreModule.forRoot() fournie par la bibliothèque NgRx.
NgEffects
Effects sont des classes qui permettent de gérer les effets de bord dans une application Angular qui utilise le store Redux.
Les effets permettent de gérer les opérations asynchrones telles que les appels à des API, la gestion des websockets ou encore les notifications, tout en maintenant une séparation claire entre la logique métier et la gestion des effets de bord.
Les effets sont créés en utilisant le décorateur @Effect() fourni par la bibliothèque NgRx. Le décorateur @Effect() prend en argument une fonction qui retourne un observable contenant les actions à traiter. Cette fonction peut être asynchrone et effectuer des opérations d'E/S.
Real DOM Vs Virtuel
Le DOM (Document Object Model) est une représentation de la structure HTML d'une page web, où chaque élément HTML est un nœud dans un arbre de nœuds. Le DOM est utilisé par les navigateurs pour manipuler le contenu d'une page web en JavaScript.
Le vrai DOM (ou "DOM réel") est la représentation complète de l'état actuel de la page, telle que générée par le navigateur. Le vrai DOM est mis à jour chaque fois qu'un élément de la page est modifié.
En revanche, le virtuel DOM est une représentation alternative du DOM, créée et manipulée par un framework comme React ou Vue.js. Cette représentation est stockée en mémoire sous forme d'un arbre de nœuds virtuels. Lorsqu'un élément de la page doit être mis à jour, le framework crée une nouvelle version de l'arbre virtuel qui représente l'état mis à jour de la page. Le framework compare ensuite la version précédente de l'arbre virtuel avec la nouvelle version, et met à jour seulement les éléments qui ont été modifiés, plutôt que de reconstruire complètement le DOM réel.
Cette approche permet d'améliorer considérablement les performances de l'application, car la mise à jour du DOM réel est coûteuse en termes de temps de traitement. En utilisant le virtuel DOM, le framework peut minimiser les mises à jour du DOM réel, ce qui réduit les coûts de traitement et améliore la réactivité de l'application.
En résumé, la principale différence entre le vrai DOM et le virtuel DOM est que le vrai DOM représente l'état actuel de la page, tandis que le virtuel DOM est une représentation alternative stockée en mémoire qui permet de minimiser les mises à jour du DOM réel pour améliorer les performances de l'application.
Angular Ivy
Angular Ivy est un nouveau compilateur et moteur de rendu de template qui a été introduit dans Angular version 8. Il remplace le compilateur et le moteur de rendu de template précédents, appelés View Engine.
Ivy a été conçu pour améliorer les performances, la taille du bundle et la maintenabilité des applications Angular. Il offre plusieurs avantages par rapport à View Engine:
- Ivy offre une meilleure compilation et une plus grande efficacité de rendu de template, ce qui améliore les performances de l'application.
- Ivy permet une réduction significative de la taille du bundle de l'application, grâce à son architecture plus modulaire et à son système de tree-shaking plus efficace.
- Ivy offre une meilleure prise en charge des interfaces utilisateur dynamiques, telles que la création de composants à la volée ou l'ajout de directives personnalisées.
- Ivy simplifie la syntaxe de template et offre une meilleure gestion des erreurs de template.
- Ivy est compatible avec les versions précédentes d'Angular, ce qui facilite la mise à jour des applications existantes.
Angular Vs React
Angular et React sont deux frameworks JavaScript populaires pour le développement d'applications web. Chaque framework a ses propres avantages et inconvénients, et le choix entre les deux dépend des besoins et des préférences de l'équipe de développement.
Voici quelques raisons pour lesquelles on pourrait choisir Angular plutĂ´t que React :
- Angular offre une structure et une organisation claires pour le développement d'applications web. Il fournit un ensemble complet de fonctionnalités et de bibliothèques pour gérer la plupart des tâches courantes, telles que la gestion de la navigation, la gestion de formulaires, la gestion de la sécurité, etc.
- Angular est un framework complet avec une architecture claire et cohérente, ce qui facilite la mise en place de projets complexes avec de nombreuses fonctionnalités.
- Angular offre une expérience de développement uniforme et structurée, ce qui facilite la collaboration entre les membres de l'équipe de développement.
- Angular utilise TypeScript, qui est un langage de programmation à typage fort, offrant une meilleure sécurité, une meilleure lisibilité du code et une meilleure prise en charge des grandes bases de code.
- Angular est souvent utilisé pour le développement d'applications d'entreprise et d'applications à grande échelle.
D'un autre côté, voici quelques raisons pour lesquelles on pourrait choisir React plutôt que Angular :
- React offre une plus grande flexibilité et liberté aux développeurs pour créer des applications web personnalisées et hautement performantes.
- React est facile Ă apprendre et Ă utiliser, ce qui facilite la mise en place de projets plus petits et moins complexes.
- React est souvent utilisé pour le développement d'applications plus orientées client, telles que les applications de commerce électronique, les applications de médias sociaux et les applications de streaming vidéo.
- React est souvent utilisé pour la création d'applications mobiles hybrides, grâce à sa capacité à fonctionner avec des frameworks tels que React Native.
TrackBy
La directive trackBy est utilisée pour améliorer les performances lors de la manipulation de listes ou de tableaux d'objets dans la vue. Elle permet de fournir une fonction de suivi personnalisée pour identifier les éléments de la liste, afin de minimiser le nombre d'opérations de rendu inutiles et de réduire la charge de travail du navigateur.
Lorsqu'une liste ou un tableau d'objets est affiché dans la vue à l'aide de la directive ngFor, Angular utilise par défaut une stratégie de suivi des changements basée sur la comparaison des références d'objets. Cela signifie que chaque fois qu'un élément de la liste est ajouté, supprimé ou déplacé, Angular doit recalculer le DOM pour tous les éléments de la liste, même si seuls quelques éléments ont été modifiés. Cela peut entraîner une perte de performances importante pour les grandes listes.
Signals
Le signal est une nouvelle façon de gérer la réactivité des données introduite depuis Angular 16. Un signal est une valeur observable qui peut être lue et modifiée facilement. Lorsqu'un signal change, Angular met automatiquement à jour tout ce qui en dépend dans l'interface utilisateur, sans avoir besoin d'abonnement ou de ChangeDetectorRef. Ils remplacent les besoins classiques de @Input(), ngOnChanges() ou même de RxJS dans certains cas, en rendant le code plus simple, réactif et performant. Grâce aux fonctions comme signal(), computed() et effect(), Angular sait exactement quoi mettre à jour, ce qui améliore considérablement les performances.
Les avantages des signals sont leur facilité d'utilisation, absence d'abonnement manuel, et leur compatibilité avec le mode "zone-less", rendant Angular plus moderne. En revanche, ils ne sont pas encore compatibles avec toutes les parties d'Angular (comme HttpClient, Forms, Router) et peuvent demander un temps d'adaptation.
Inconvénients
- Trop de effect() non contrôlés \= mauvaise perf ou logique dure à suivre
- Pour les développeurs venant d'Angular classique ou RxJS, il faut s'adapter
Signals et RxJS peuvent fonctionner ensemble avec Ă toSignal() et fromSignal().
Signals Vs RxJS
Signals simplifient la gestion d'état synchronisée, et RxJS reste indispensable pour gérer des flux asynchrones complexes. ils ne remplacent pas complètement l'un l'autre, ils sont complémentaires
Combinaison
Combine-les en laissant RxJS gérer l'async et les opérateurs, puis convertis le résultat utile en Signal pour obtenir un état simple, synchronisé et lisible dans la vue.
Computed
Computed() sert à calculer une valeur automatiquement à partir d'un ou plusieurs signals. Quand un signal utilisé dans le calcul change, la valeur recalculée se met à jour toute seule.
Effect
Effect permet d'exécuter automatiquement une action quand un signal change. Il est utilisé pour réagir à un changement
toSignal
toSignal() est une fonction introduite dans Angular v16 qui permet de convertir un Observable RxJS en Signal. Elle fait partie du package @angular/core/rxjs-interop. Grâce à cette conversion, tu peux exploiter des données asynchrones issues d'un Observable (comme un appel HTTP, un champ de formulaire réactif ou un intervalle de temps) sous forme de valeur réactive et simple à utiliser, comme un signal.
Pourquoi utiliser toSignal()?
toSignal() est utile pour plusieurs raisons :
- Il permet d'utiliser un Observable directement dans un template Angular sans passer par le | async pipe.
- Il facilite la combinaison entre RxJS et les Signals dans une mĂŞme logique applicative.
- Il rend les données observables accessibles dans des computed() ou effect(), pour créer des comportements ou des valeurs dérivées de manière réactive.
- Il simplifie le code, car il n'y a plus besoin de s'abonner manuellement à un observable via .subscribe() ni de gérer les désabonnements (unsubscribe()).
Comment fonctionne toSignal() ?
Lorsqu'on utilise toSignal(), Angular commence à écouter le flux de l'observable. À chaque émission de l'observable, la valeur du signal est automatiquement mise à jour. Ce signal généré est en lecture seule : on ne peut pas le modifier avec .set() ou .update() comme un signal normal. Si on veut effectuer des transformations ou des calculs sur cette valeur, on peut le faire avec la fonction computed() pour créer un signal dérivé.
fromSignal
fromSignal() est une fonction (introduite dans Angular v16 dans @angular/core/rxjs-interop) qui permet de convertir un Signal en Observable. C'est l'inverse de toSignal(). Elle est utile lorsqu' on veut utiliser une valeur signal dans un contexte où RxJS est attendu, comme avec des opérateurs (map, switchMap, etc.) ou dans une API basée sur les observables (ex. : combineLatest, merge, etc.).
Pourquoi utiliser fromSignal()? On peut utiliser fromSignal() pour :
- Combiner un signal avec d'autres observables dans un pipeline RxJS.
- Appliquer des opérateurs RxJS à une valeur qui était d'abord un signal.
- Intégrer des signaux dans une logique déjà construite avec RxJS, sans tout réécrire.
Comment fonctionne fromSignal() ?
Lorsqu' on passe un signal à fromSignal(), Angular crée un observable qui émet chaque fois que le signal change. Chaque nouvelle valeur du signal sera automatiquement émise par l'observable généré. L'observable reste synchronisé avec le signal d'origine, ce qui permet de profiter des opérateurs RxJS tout en gardant la source signal . L'observable généré est "cold" : il n'émet que quand on s'abonne. Contrairement au signal, un observable ne garde pas forcément la dernière valeur si personne n'est abonné. Tu dois toujours te désabonner manuellement si nécessaire
defer
@defer Permet de lazy loader une partie du template, il permet d'améliore le Time To Interactive et de réduire le bundle initial
[afterNextRender](https://v16.angular.io/api/core/afterNextRender)/ [afterRender](https://v16.angular.io/api/core/afterRender) [DestroyRef](https://v16.angular.io/api/core/DestroyRef) [takeUntilDestroyed](https://v16.angular.io/api/core/rxjs-interop/takeUntilDestroyed)
Inject et injectionToken
Inject() est une fonction introduite dans Angular v14 et généralisée en Angular v15+, qui permet de récupérer un service ou une dépendance en dehors du constructeur d'un composant, service ou directive.
Pourquoi utiliser inject()?
Traditionnellement, on injectait des services Angular via le constructeur. Cela rend le code plus flexible, surtout dans les composants standalone, les signals, ou des fichiers utilitaires oĂą le constructeur n'est pas disponible.
Comment fonctionne inject() ?
Inject() va chercher dans l'arborescence d'injection Angular (le "dependency injection tree") la valeur ou l'instance du service demandé. Il fonctionne à l'intérieur du contexte d'injection Angular, c'est-à -dire dans les composants, directives, pipes, services, etc.
InjectionToken
Un InjectionToken est un jeton (clé) d'injection personnalisée. Il permet de fournir ou d'injecter des valeurs non-classées, comme : des chaînes de caractères, des valeurs de configuration, des fonctions, etc.
Pourquoi utiliser InjectionToken ?
Angular ne peut injecter que des classes ou types référencés. Si tu veux injecter une valeur simple (par exemple une URL, un objet de config ou un tableau), tu dois créer un InjectionToken comme identifiant unique.
Zone.js
Zone.js qui permet à Angular de détecter les changements dans l'application de manière plus efficace et de mettre à jour l'interface utilisateur de manière plus rapide, ce qui peut améliorer la réactivité de l'application. Cependant, Zone.js pose essentiellement quatre (04) problèmes qui sont :
- Zone.js a un surcoût d'environ 100 Ko. Bien que négligeable pour les applications de grande taille, cette surcharge est rédhibitoire pour le déploiement de composants web légers.
- Avec Zone.js, les objets du navigateur sont modifiés et les erreurs sont difficiles à diagnostiquer.
- Zone.js n'arrive pas à interprèter async et await puisqu'il s'agit de mots-clés. Par conséquent, lors de la compilation, Angular convertit toujours ces déclarations en promesses, même si tous les navigateurs pris en charge supportent déjà async et await en mode natif.
- Lorsque des modifications sont apportées, des composants entiers, y compris leurs prédécesseurs, sont toujours vérifiés dans l'arborescence des composants. Il n'est actuellement pas possible d'identifier directement les composants modifiés ou de mettre à jour les parties modifiées d'un composant.
zoneless
Angular peut fonctionner sans zone.js. Avant : Angular interceptait tous les événements Maintenant : Signals déclenchent explicitement les updates Avantages : meilleures performances et contrôle total
Pourquoi Angular évolue vers un modèle zoneless ?
Zone.js est coûteux en perf et difficile à debugger et Angular veut de rendre le framework plus prévisible et de s'aligner avec les standards modernes
compileComponents
Toujours utiliser compileComponents() dans les tests de composants avec templates, sauf si tu es sûr que :
- Tu fais du test unitaire pur (pas de DOM),
- Le composant n'a aucune dépendance,
- Le template est inline et très simple.
Angular 2026
Angular continue d'évoluer et de s'imposer comme l'un des frameworks JavaScript les plus robustes du marché. on a choisi Angular plutôt qu'un autre framework car:
- Un framework qui a mûri sans vieillir
Depuis sa version 3 en 2016, Angular a conservé un cycle de 6 mois entre versions majeures, offrant à la fois stabilité – grâce aux releases Long-Term Support (LTS) – et innovation régulière.
- Une ergonomie de code repensée
La "renaissance" du framework v17 a introduit des améliorations majeures dans l'expérience développeur.
- Signals : La réactivité simplifiée
Les signaux offrent une réactivité déclarative et prédictive, sans la complexité des pipes asynchrones. Ils permettent à Angular de savoir exactement quelles parties de l'interface doivent être mises à jour, évitant les vérifications inutiles et améliorant drastiquement les performances.
- Standalone API : Simplicité architecturale
Plus besoin de créer des modules pour chaque composant. L'API standalone simplifie la structure de vos applications en permettant l'import direct des dépendances, rendant le code plus lisible et plus maintenable.
- Built-in Control Flow : Syntaxe intuitive
Les nouvelles directives de contrôle de flux (@for, @if, @switch) sont plus proches de JavaScript natif, offrant une lisibilité accrue et réduisant la verbosité du code. Cette syntaxe est plus intuitive pour les développeurs venant d'autres langages.
- TypeScript au cœur du framework
Angular a été conçu dès le départ avec TypeScript, offrant :
- Sécurité de type native
Détection d'erreurs à la compilation Autocomplétion intelligente dans l'IDE Refactoring sécurisé à grande échelle Documentation vivante via les types
- Strict mode par défaut
Depuis la v16, Angular active le mode strict de TypeScript par défaut, garantissant une qualité de code élevée et réduisant les bugs en production.
God object
God Object (ou composant God) est un composant qui fait trop de choses et centralise trop de responsabilités.il gère à la fois la logique métier, l'état, les appels API, la transformation de données et l'UI, au lieu de déléguer.
Comment éviter
- extraire la logique API dans des services
- extraire l'état dans une facade/store
- découper l'UI en sous-composants
- déplacer les transformations lourdes hors template
- clarifier les responsabilités
Angular 16
Signals (grosse feature): Nouveau modèle de réactivité simple et synchrone remplace partiellement RxJS pour le state, moins de dépendance à [Zone.js](http://Zone.js) Insight senior : Angular passe d'un modèle "stream-based" à "state-based"
Hydration (SSR amélioré): Hydration non destructive réutilise le DOM côté client évite le flickering, améliore les Core Web Vitals. Impact : meilleures performances + UX
Build plus rapide (esbuild + Vite): Nouveau système de build, compilation beaucoup plus rapide
dev server plus performant
Interop Signals ↔ RxJS: Bridge officiel entre Signals et RxJS, toSignal(), toObservable() permet de combiner les deux proprement
Angular 17
Nouveau control flow: (grosse feature)Nouvelle syntaxe plus simple dans les templates
@if (condition) { ... }
@switch (...) { ... }
âś” remplace ngIf, ngFor, *ngSwitch
âś” plus lisible, proche de JS
âś” meilleures perfs
👉 Insight senior : Angular simplifie enfin le templating
⚡ 2. @for ultra performant
👉 Nouvelle boucle optimisée jusqu'à +90% de performance vs *ngFor gestion native du track
Deferrable views (@defer): Lazy loading directement dans le template
Angular 18
Angular 18 — nouveautés principales
Zoneless (grosse évolution): Angular peut fonctionner sans zone.js (expérimental), change detection pilotée par Signals, moins de surcharge
Coalescing activé par défaut: Réduction automatique des cycles de change detection, moins de rerender inutiles, amélioration perf globale
Native async/await (zoneless): Plus de transformation en Promise, code plus clean
bundle plus léger
Event Replay (nouveau): Les interactions utilisateur ne sont plus perdues
clics capturés avant hydration, rejoués après chargement