<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[angulardev.fr]]></title><description><![CDATA[Je suis Ezéchiel Amen AGBLA, Angular Developer Expert &amp; Ionic Developer Expert, passionné par le développement web et mobile.]]></description><link>https://angulardev.fr</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1681395645590/shC49s0nA.png</url><title>angulardev.fr</title><link>https://angulardev.fr</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 08 Sep 2026 06:17:14 GMT</lastBuildDate><atom:link href="https://angulardev.fr/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[A la découverte de rxMethod]]></title><description><![CDATA[Depuis l'arrivée des Signals dans Angular, une question revient sans cesse dans les équipes : comment continuer à profiter de la puissance de RxJS, de ses opérateurs, de sa gestion fine des flux async]]></description><link>https://angulardev.fr/a-la-d-couverte-de-rxmethod</link><guid isPermaLink="true">https://angulardev.fr/a-la-d-couverte-de-rxmethod</guid><category><![CDATA[signals]]></category><category><![CDATA[Angular]]></category><category><![CDATA[RxJS]]></category><category><![CDATA[NgRx]]></category><category><![CDATA[resources]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Tue, 28 Jul 2026 05:35:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/5f29b3abe180c5164cdecc5b/8a8a5917-a730-4f86-963c-5725566dd6d7.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Depuis l'arrivée des Signals dans Angular, une question revient sans cesse dans les équipes : comment continuer à profiter de la puissance de RxJS, de ses opérateurs, de sa gestion fine des flux asynchrones, de son <code>switchMap</code> par exemple qui annule les requêtes obsolètes et le tout en adoptant un modèle d'état basé sur les signaux ?</p>
<p>C'est exactement le problème que résout <code>rxMethod</code>, une fonction fournie par le package <code>@ngrx/signals/rxjs-interop</code>.</p>
<p>Elle ne remplace pas RxJS, elle le fait cohabiter proprement avec les signaux.</p>
<h2>Le problème qu'elle résout</h2>
<p>Avant <code>rxMethod</code>, connecter un flux RxJS à un signal demandait souvent du bricolage : un <code>effect()</code> qui souscrit manuellement à un Observable, une gestion artisanale du désabonnement, et le risque bien réel de fuites mémoire ou de traitements en double si l'on n'y prête pas attention.</p>
<p><em>Voici un exemple représentatif de ce qu'on écrivait "avant" :</em></p>
<pre><code class="language-typescript">import { Component, effect, inject, signal, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';

@Component({ /* ... */ })
export class TodoList implements OnDestroy {
  #todosService = inject(TodosService);
  #subscription?: Subscription;

  userId = signal(1);
  todos = signal&lt;Todo[]&gt;([]);

  constructor() {
    effect(() =&gt; {
      const id = this.userId(); // lecture du signal → dépendance déclarée

      // ⚠️ On souscrit manuellement à chaque exécution de l'effect
      this.#subscription = this.#todosService.getByUserId(id).subscribe({
        next: (todos) =&gt; this.todos.set(todos),
        error: (err) =&gt; console.error(err),
      });
    });
  }

  ngOnDestroy() {
    this.#subscription?.unsubscribe();
  }
}
</code></pre>
<p>Ce code a deux défauts typiques :</p>
<ul>
<li><p><strong>fuite mémoire / traitements en double</strong> : à chaque changement de <code>userId</code>, l'<code>effect</code> relance une souscription, mais n'annule jamais la précédente. Si l'utilisateur change rapidement d'identifiant, plusieurs requêtes HTTP restent actives en parallèle, et rien ne garantit que les réponses arrivent dans l'ordre. Ainsi, la plus ancienne peut très bien écraser la plus récente.</p>
</li>
<li><p><strong>nettoyage fragile</strong> : on ne désabonne qu'à la destruction du composant (<code>ngOnDestroy</code>), pas entre deux exécutions de l'<code>effect</code>. Pour corriger ça "à la main", il faudrait gérer soi-même l'ancienne souscription via le <code>onCleanup</code> de l'<code>effect</code> :</p>
</li>
</ul>
<pre><code class="language-typescript">effect((onCleanup) =&gt; {
  const id = this.userId();

  const sub = this.#todosService.getByUserId(id).subscribe({
    next: (todos) =&gt; this.todos.set(todos),
  });

  onCleanup(() =&gt; sub.unsubscribe());
});
</code></pre>
<h2>rxResource, la réponse native d'Angular à ce problème</h2>
<p>Face à ce genre de bricolage, Angular propose désormais sa propre solution native pour la lecture de données asynchrones : <code>rxResource</code>, dans <code>@angular/core/rxjs-interop</code>.</p>
<p>Elle résout exactement le problème illustré ci-dessus, sans <code>effect()</code>, sans <code>Subscription</code> manuelle, sans <code>onCleanup</code> :</p>
<pre><code class="language-typescript">import { Component, signal, inject } from '@angular/core';
import { rxResource } from '@angular/core/rxjs-interop';

@Component({ /* ... */ })
export class TodoList {
  #todosService = inject(TodosService);
  userId = signal(1);

  todosResource = rxResource({
    params: () =&gt; ({ userId: this.userId() }),
    stream: ({ params }) =&gt; this.#todosService.getByUserId(params.userId),
  });
}
</code></pre>
<p><code>rxResource</code> gère lui-même l'état complet de la requête :</p>
<ul>
<li><p>valeur : <code>todosResource.value()</code> ;</p>
</li>
<li><p>chargement : <code>todosResource.isLoading()</code> ;</p>
</li>
<li><p>erreur : <code>todosResource.error()</code> ;</p>
</li>
<li><p>l'annulation automatique de la requête précédente à chaque changement de <code>params</code> sans qu'on ait à écrire le moindre <code>switchMap</code> ni à se soucier du désabonnement.</p>
</li>
</ul>
<p>Sur ce cas d'usage précis, une lecture pilotée par un paramètre réactif est bien évidemment plus simple qu'un <code>rxMethod</code>.</p>
<blockquote>
<p>Alors, <code>rxResource</code> rend-il <code>rxMethod</code> inutile ?  </p>
<p>Pas tout à fait, et c'est ce que la suite de cet article va démontrer.</p>
</blockquote>
<h2>Anatomie de rxMethod</h2>
<pre><code class="language-typescript">import { rxMethod } from '@ngrx/signals/rxjs-interop';

function rxMethod&lt;Input&gt;(
  generator: (source$: Observable&lt;Input&gt;) =&gt; Observable&lt;unknown&gt;,
  config?: { injector?: Injector }
): RxMethod&lt;Input&gt;
</code></pre>
<p><code>rxMethod</code> prend en paramètre un générateur qui n'est rien d'autre qu'une fonction qui reçoit un flux (<code>Observable&lt;Input&gt;</code>) et retourne un <code>Observable</code> transformé, typiquement via un <code>pipe()</code>.</p>
<p>Le paramètre de type <code>Input</code> fixe le type des données attendues en entrée, indépendamment de la forme sous laquelle l'appelant les fournit.</p>
<pre><code class="language-typescript">import { Component, inject, signal } from '@angular/core';
import { rxMethod } from '@ngrx/signals/rxjs-interop';
import { switchMap } from 'rxjs';
import { tapResponse } from '@ngrx/operators';

@Component({ /* ... */ })
export class TodoList {
  readonly #todosService = inject(TodosService);
  readonly userId = signal(1);
  readonly todos = signal&lt;Todo[]&gt;([]);

  readonly loadTodos = rxMethod&lt;number&gt;(
    switchMap((id) =&gt;
      this.#todosService.getByUserId(id).pipe(
        tapResponse({
          next: (todos) =&gt; this.todos.set(todos),
          error: console.error,
        })
      )
    )
  );

  constructor() {
    this.loadTodos(this.userId);   // un Signal&lt;number&gt;
    this.loadTodos(this.userId$);  // un Observable&lt;number&gt;
    this.loadTodos(1);            // une valeur brute, number
  }
}
</code></pre>
<p>Trois façons d'appeler <code>loadTodos</code> sont possibles :</p>
<ul>
<li><p>si c'est un <strong>Signal</strong>, il crée un <code>effect()</code> interne qui pousse la valeur courante du signal dans le flux source à chaque changement ;</p>
</li>
<li><p>si c'est un <strong>Observable</strong>, il s'y abonne directement et transmet chaque valeur émise dans le flux source ;</p>
</li>
<li><p>si c'est une <strong>valeur statique</strong>, il l'émet une seule fois dans le flux source, immédiatement.</p>
</li>
</ul>
<p>La logique métier ne change pas, seule la source d'entrée varie. Dans les trois cas, la valeur finit par arriver, sous forme de <code>number</code>.</p>
<h2>Pourquoi le choix de l'opérateur de flattening compte</h2>
<p><code>rxMethod</code> ne dicte aucun opérateur d'aplatissement particulier : c'est à vous de choisir entre <code>switchMap</code>, <code>mergeMap</code>, <code>concatMap</code> ou <code>exhaustMap</code> selon le comportement de concurrence souhaité.</p>
<ul>
<li><p><code>switchMap</code> : annule la requête précédente dès qu'une nouvelle arrive. C'est idéal pour une recherche ou un chargement de détail par identifiant.</p>
</li>
<li><p><code>concatMap</code> : traite les requêtes dans l'ordre, une par une. Il est utile pour des écritures séquentielles.</p>
</li>
<li><p><code>mergeMap</code> : lance tout en parallèle pour des opérations indépendantes.</p>
</li>
<li><p><code>exhaustMap</code> : ignore les nouvelles requêtes tant que la précédente n'est pas terminée. C'est très pratique pour éviter les doubles soumissions.</p>
</li>
</ul>
<blockquote>
<p>Ce choix a un impact direct sur la fiabilité de l'application, notamment pour éviter les fameuses "race conditions" où une réponse arrivée en retard écrase un résultat plus récent.</p>
</blockquote>
<h2>Le contexte d'injection</h2>
<p><code>rxMethod</code> doit, par défaut, être appelée dans un contexte d'injection Angular (constructeur, champ de classe, ou tout endroit où <code>inject()</code> est disponible). Si ce n'est pas possible, on peut fournir explicitement un <code>Injector</code> :</p>
<pre><code class="language-typescript">constructor() {
  this.loadTodos = rxMethod&lt;number&gt;(
    switchMap((id) =&gt; /* ... */),
    { injector: this.injector }
  );
}
</code></pre>
<p>Cette contrainte garantit que le cycle de vie de la souscription est correctement rattaché, et donc automatiquement nettoyé à la destruction du composant.</p>
<p>Plus besoin de gérer manuellement un <code>Subscription</code> et son <code>unsubscribe()</code>.</p>
<h2>Les mutations : là où rxResource montre ses limites</h2>
<p><code>rxResource</code> a un modèle mental très clair : une fonction <code>stream</code> qui se relance automatiquement chaque fois que <code>params()</code> produit une nouvelle valeur.</p>
<p>C'est un peu comme une requête qu'on rejoue à chaque changement de paramètre. C'est excellent pour du <code>GET</code>.</p>
<p>Cependant, voyons ce qui se passe si on essaie de le détourner pour une mutation.</p>
<pre><code class="language-typescript">// ⚠️ Détournement de rxResource pour une sauvegarde - à éviter
export class TodoEditor {
  #todosService = inject(TodosService);
  todoToSave = signal&lt;Todo | null&gt;(null);

  saveResource = rxResource({
    params: () =&gt; this.todoToSave(),
    stream: ({ params }) =&gt; {
      if (!params) return of(undefined);
      return this.#todosService.save(params);
    },
  });

  onSubmit(todo: Todo) {
    this.todoToSave.set(todo); // 👈 on force la mutation via un changement de "requête"
  }
}
</code></pre>
<p>Trois problèmes concrets apparaissent immédiatement :</p>
<ul>
<li><p><strong>la déduplication casse le renvoi :</strong> <code>rxResource</code> ne relance <code>stream</code> que lorsque <code>params()</code> change de valeur. Si l'utilisateur soumet deux fois le même formulaire (même objet <code>todo</code>), le second clic <strong>ne déclenche rien du tout</strong> : <code>params()</code> n'a pas changé, donc la mutation n'est jamais rejouée. C'est un comportement correct pour une lecture, mais un bug silencieux pour une écriture.</p>
</li>
<li><p><strong>l'annulation implicite est dangereuse :</strong> si <code>todoToSave()</code> change une nouvelle fois pendant qu'une sauvegarde précédente est encore en cours, le comportement de type <code>switchMap</code> de <code>rxResource</code> annule la requête HTTP en cours. Pour une lecture, perdre une réponse obsolète est sans conséquence. Pour une écriture, cela peut vouloir dire qu'une sauvegarde en base de données est interrompue au milieu de son exécution.</p>
</li>
<li><p><strong>aucun contrôle sur la concurrence : i</strong>mpossible de dire à <code>rxResource</code> "ignore les nouveaux appels tant que le précédent n'est pas terminé" (<code>exhaustMap</code>) ou "empile-les dans l'ordre" (<code>concatMap</code>). Le modèle est figé sur un comportement proche de <code>switchMap</code>, pensé pour représenter un état, pas une suite d'événements.</p>
</li>
</ul>
<p>Voyons maintenant la même mutation avec <code>rxMethod</code> :</p>
<pre><code class="language-typescript">export class TodoEditor {
  #todosService = inject(TodosService);
  saving = signal(false);

  saveTodo = rxMethod&lt;Todo&gt;(
    exhaustMap((todo) =&gt; {
      this.saving.set(true);
      return this.#todosService.save(todo).pipe(
        tapResponse({
          next: () =&gt; this.saving.set(false),
          error: (err) =&gt; {
            console.error(err);
            this.saving.set(false);
          },
        })
      );
    })
  );

  onSubmit(todo: Todo) {
    this.saveTodo(todo); // 👈 chaque appel est un événement, pas une valeur d'état
  }
}
</code></pre>
<p>Ici, tout fonctionne comme attendu :</p>
<ul>
<li><p><strong>chaque appel à</strong> <code>saveTodo(todo)</code> <strong>déclenche systématiquement un envoi</strong>, même avec un <code>todo</code> identique au précédent. <code>rxMethod</code> alimente en interne un flux d'événements (proche d'un <code>Subject</code>) : il ne compare pas les valeurs, il les pousse. Rien n'est dédupliqué.</p>
</li>
<li><p><code>exhaustMap</code> <strong>protège explicitement contre le double clic</strong> : tant qu'une sauvegarde est en cours, les appels suivants sont ignorés. Un choix assumé, contrairement au comportement implicite et non paramétrable de <code>rxResource</code>.</p>
</li>
<li><p><strong>aucune sauvegarde en cours n'est jamais annulée involontairement</strong>, puisque rien ne force <code>rxMethod</code> à réagir à un changement de "paramètre" : c'est l'appel explicite qui déclenche l'action.</p>
</li>
</ul>
<blockquote>
<p>La différence de fond tient en une phrase : <code>rxResource</code> <strong>modélise un état</strong> (une donnée dérivée d'un paramètre, qu'on relit), <code>rxMethod</code> <strong>modélise un flux d'événements</strong> (une action qu'on déclenche).</p>
<p>Vouloir faire des mutations avec le premier revient à forcer un modèle déclaratif à se comporter comme un modèle impératif. Ça fonctionne parfois en apparence, mais les cas limites (double soumission, annulation, ordre d'exécution) finissent toujours par se rappeler à vous.</p>
</blockquote>
<p>Une bonne règle de partage des rôles :</p>
<table>
<thead>
<tr>
<th>Besoin</th>
<th>Outil recommandé</th>
</tr>
</thead>
<tbody><tr>
<td>Charger une donnée en fonction d'un signal (recherche, détail par id, pagination)</td>
<td><code>rxResource</code></td>
</tr>
<tr>
<td>Déclencher une action ponctuelle (sauvegarde, suppression, envoi de formulaire)</td>
<td><code>rxMethod</code></td>
</tr>
<tr>
<td>Chaîner des opérateurs RxJS complexes sur un flux d'événements arbitraire</td>
<td><code>rxMethod</code></td>
</tr>
</tbody></table>
<p>Les deux ne s'excluent pas : il est courant de voir <code>rxResource</code> pour l'affichage et <code>rxMethod</code> pour les mutations coexister dans le même composant.</p>
<h2>Au-delà des cas de lecture</h2>
<p>Rien n'empêche non plus d'utiliser <code>rxMethod</code> pour des appels HTTP simples, sans structure d'état complexe autour :</p>
<pre><code class="language-typescript">export class UserListComponent {
  #http = inject(HttpClient);
  users = signal&lt;User[]&gt;([]);

  loadUsers = rxMethod&lt;void&gt;(() =&gt;
    this.#http.get&lt;User[]&gt;('/api/users').pipe(
      tap((data) =&gt; this.users.set(data))
    )
  );
}
</code></pre>
<p>C'est une alternative intéressante aux appels RxJS "à la main" dans un <code>effect()</code>, avec une API plus explicite sur ce qui déclenche réellement l'effet de bord.</p>
<p>En somme, <code>rxMethod</code> n'est pas un remplaçant de RxJS, ni un concurrent des signaux. Il joue un rôle de pont. Il permet de :</p>
<ul>
<li><p><strong>conserver</strong> la richesse des opérateurs RxJS (debounce, annulation, combinaison de flux) ;</p>
</li>
<li><p><strong>s'intégrer</strong> nativement aux signaux Angular, sans code de liaison superflu ;</p>
</li>
<li><p><strong>automatiser</strong> la gestion du cycle de vie des souscriptions ;</p>
</li>
<li><p><strong>accepter indifféremment</strong> une valeur, un signal ou un observable en entrée ;</p>
</li>
<li><p><strong>couvrir les mutations</strong> là où <code>rxResource</code>, pensé pour la lecture, montre ses limites.</p>
</li>
</ul>
<p>Pour toute équipe qui migre progressivement vers les signaux tout en gardant des flux de données complexes basés sur RxJS, <code>rxMethod</code> constitue aujourd'hui l'un des outils les plus pragmatiques de l'écosystème NgRx, particulièrement complémentaire de <code>rxResource</code> pour tout ce qui touche à l'écriture plutôt qu'à la lecture.</p>
<blockquote>
<p><em>Pour plus d'informations 👉🏽</em> <a href="https://ngrx.io/guide/signals/rxjs-integration">rxMethod</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Zoneless par-ci, Zoneless par-là... Mais qu'en est-il concrètement ?]]></title><description><![CDATA[Si vous suivez l'actualité d'Angular avec la sortie de la version 20 du framework, vous avez probablement entendu de plus en plus ce terme. Mais au-delà du jargon, qu'est-ce que le Zoneless concrètement pour vos applications Angular ? Pourquoi est-il...]]></description><link>https://angulardev.fr/zoneless-par-ci-zoneless-par-la-mais-quen-est-il-concretement</link><guid isPermaLink="true">https://angulardev.fr/zoneless-par-ci-zoneless-par-la-mais-quen-est-il-concretement</guid><category><![CDATA[zoneless]]></category><category><![CDATA[Angular]]></category><category><![CDATA[zonejs]]></category><category><![CDATA[signals]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Mon, 02 Jun 2025 07:14:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/bx6subJbaiU/upload/684e02551a64fc50e339dcbcb227c72f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Si vous suivez l'actualité d'Angular avec la sortie de la version 20 du framework, vous avez probablement entendu de plus en plus ce terme. Mais au-delà du jargon, <strong><em>qu'est-ce que le</em></strong> <code>Zoneless</code> <strong><em>concrètement pour vos applications Angular</em></strong> <em>?</em> <strong><em>Pourquoi est-il si important ?</em></strong><br />Bien que cette fonctionnalité soit actuellement en phase expérimentale, plongeons-nous quand-même ensemble dans ce concept qui promet de révolutionner la gestion des performances dans l'écosystème Angular.</p>
<h3 id="heading-le-probleme-la-magie-de-zonejs">👉 Le Problème : La Magie de <code>Zone.js</code></h3>
<p>Pour comprendre le <code>Zoneless</code>, il faut d'abord saisir le rôle de <code>Zone.js</code>, le compagnon silencieux d'Angular depuis ses débuts.</p>
<p><code>Zone.js</code> est une bibliothèque qui <strong><em>“patch” le moteur d'exécution JavaScript</em></strong> dans le navigateur. Son objectif principal est de <strong><em>détecter quand des changements pourraient se produire</em></strong> dans votre application. Comment fait-il ça ? En interceptant les événements asynchrones courants :</p>
<blockquote>
<ul>
<li><p><strong><em>Timers</em></strong> (<code>setTimeout</code>, <code>setInterval</code>) ;</p>
</li>
<li><p><strong><em>Requêtes HTTP</em></strong> (<code>fetch</code>, <code>XMLHttpRequest</code>) ;</p>
</li>
<li><p><strong><em>Événements du DOM</em></strong> (<em>clics</em>, <em>saisies</em>, <em>etc</em>.) ;</p>
</li>
<li><p><strong><em>Promesses</em></strong> (<code>Promise.then</code>, <code>async/await</code>).</p>
</li>
</ul>
</blockquote>
<p>Chaque fois qu'une de ces opérations se termine, <code>Zone.js</code> notifie Angular qu'un "<em>tick</em>" s'est produit. Angular, à son tour, déclenche son <strong><em>mécanisme de détection de changement</em></strong> pour s'assurer que l'interface utilisateur reflète le nouvel état de vos données.</p>
<blockquote>
<p>C'est ce qui donne à Angular sa réactivité "magique" : vous modifiez une variable, et l'affichage se met à jour sans que vous ayez à le dire explicitement.</p>
</blockquote>
<p>Cependant, cette magie a un coût : <strong><em>la performance</em></strong>. Chaque détection de changement initiée par <code>Zone.js</code> doit parcourir l'arbre des composants pour vérifier les potentielles mises à jour. Dans les grandes applications complexes, avec de nombreux événements asynchrones, cela peut entraîner des <strong><em>cycles de détection de changement inutiles et coûteux</em></strong>, ralentissant l'application et impactant l'expérience utilisateur.</p>
<h3 id="heading-la-solution-le-zoneless-ou-le-controle-manuel">👉 La Solution : Le <code>Zoneless</code> ou le <code>Contrôle Manuel</code></h3>
<p>Le terme <code>Zoneless</code> fait référence à la possibilité de faire fonctionner une application Angular sans <code>Zone.js</code>, ou du moins en réduisant considérablement sa portée. L'idée n'est pas de se débarrasser complètement de la détection de changement, mais de la <strong><em>rendre plus explicite et optimisée</em></strong>.</p>
<blockquote>
<p>Au lieu que <code>Zone.js</code> nous dise quand un changement a potentiellement eu lieu, le développeur prend le contrôle et <strong><em>signale explicitement à Angular quand il doit vérifier les changements</em></strong>.</p>
<p>Comment cela fonctionne-t-il concrètement ? Principalement grâce aux <code>Signals</code> <strong>(Signaux)</strong>.</p>
</blockquote>
<h3 id="heading-le-role-central-des-signals">👉 Le Rôle Central des <code>Signals</code></h3>
<p>Les <code>Signals</code> sont une nouvelle primitive de réactivité introduite dans Angular 16 (et améliorée dans les versions suivantes).</p>
<p>Lorsque vous utilisez des <code>Signals</code>, Angular sait exactement quels composants dépendent de quelles données. Si vous modifiez un <code>Signal</code>, Angular ne mettra à jour que les composants (et les parties de ces composants) qui sont directement affectés par ce <code>Signal</code>. Cela contraste fortement avec l'approche de <code>Zone.js</code> qui vérifie l'ensemble de l'application ou de grandes parties de celle-ci.</p>
<blockquote>
<p>En mode <code>Zoneless</code>, c'est la <strong>modification d'un</strong> <code>Signal</code> <strong>qui devient le déclencheur principal de la détection de changement</strong>, et non plus un événement asynchrone arbitraire intercepté par <code>Zone.js</code><em>.</em></p>
</blockquote>
<p>Angular aura besoin de déclencher une détection de changement en mode <code>Zoneless</code> :</p>
<blockquote>
<ul>
<li><p>Quand un <code>Signal</code> <strong>est mis à jour</strong>.</p>
</li>
<li><p>Quand un <strong><em>événement DOM</em> se produit</strong> (comme un clic), Angular peut toujours les détecter.</p>
</li>
<li><p>Quand vous utilisez <code>markForCheck()</code> sur un <code>ChangeDetectorRef</code> (pour les cas où vous ne travaillez pas avec des <code>Signals</code> ou avez des données asynchrones non basées sur des <code>Signals</code>).</p>
</li>
</ul>
</blockquote>
<p>En somme, le <code>Zoneless</code> n'est pas un mystère insaisissable, mais une <strong>évolution logique de la stratégie de détection de changement d'Angular</strong>. En s'appuyant sur les <code>Signals</code>, Angular nous offre la possibilité de reprendre le contrôle sur la réactivité de nos applications, menant à des performances accrues, un code plus prévisible et une expérience de développement améliorée.</p>
<blockquote>
<p>Pour plus d'informations 👉 <a target="_blank" href="https://angular.dev/guide/zoneless">Zoneless</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[formArray.patchValue(...) : attention à son utilisation !]]></title><description><![CDATA[Si vous avez l’habitude de manipuler des formulaires réactifs (Reactive Forms) dans un projet Angular, vous connaissez certainement la méthode patchValue(…). Cette méthode, permet de mettre à jour les valeurs de votre formulaire. Cependant, son utili...]]></description><link>https://angulardev.fr/formarraypatchvalue-attention-a-son-utilisation</link><guid isPermaLink="true">https://angulardev.fr/formarraypatchvalue-attention-a-son-utilisation</guid><category><![CDATA[Angular]]></category><category><![CDATA[reactive forms]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Tue, 28 Jan 2025 09:00:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/-Cmz06-0btw/upload/d87d41e7890b00cfabda35f011461b77.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Si vous avez l’habitude de manipuler des formulaires réactifs (<em>Reactive Forms</em>) dans un projet Angular, vous connaissez certainement la méthode <code>patchValue(…)</code>. Cette méthode, permet de mettre à jour les valeurs de votre formulaire. Cependant, son utilisation sur un formulaire ayant un contrôle de type <code>FormArray</code> qui n’est rien d’autre qu’un formulaire dynamique, dans lequel vous pouvez ajouter et supprimer des contrôles au moment de l'exécution est très délicate. Dans cet article, nous verrons ensemble, pourquoi faut t-il faire attention à la façon dont nous utilisons <code>patchValue(…)</code> lorsqu’il s’agit d’un formulaire ayant un contrôle de type <code>FormArray</code>.</p>
<p>Avant d’entrer dans le vif du sujet, nous rappellerons, l’utilisation classique de la méthode <code>patchValue(…)</code> sur un formulaire simple. Considérons, le bout de code suivant où nous avons un composant <code>ProfileEditorComponent</code> dans lequel est implémenté un formulaire simple composé de deux champs à savoir <code>firstName</code> et <code>lastName</code> :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {Component} <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
<span class="hljs-keyword">import</span> {FormGroup, FormControl} <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/forms'</span>;

<span class="hljs-meta">@Component</span>({
  selector: <span class="hljs-string">'app-profile-editor'</span>,
  templateUrl: <span class="hljs-string">'./profile-editor.component.html'</span>,
  styleUrls: [<span class="hljs-string">'./profile-editor.component.css'</span>],
  standalone: <span class="hljs-literal">true</span>,
})

<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ProfileEditorComponent {
  profileForm = <span class="hljs-keyword">new</span> FormGroup({
    firstName: <span class="hljs-keyword">new</span> FormControl(<span class="hljs-string">''</span>),
    lastName: <span class="hljs-keyword">new</span> FormControl(<span class="hljs-string">''</span>)
  });
}
</code></pre>
<p>Dans l’exemple susmentionné, pour mettre à jour les valeurs de notre formulaire en utilisant <code>patchValue(…)</code>, voici ce que nous faisons :</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">this</span>.profileForm.patchValue({
    firstName: <span class="hljs-string">'John'</span>,
    lastName: <span class="hljs-string">'Doe'</span>
});
</code></pre>
<p>Une fois, ce bout de code exécuté, notre formulaire sera mis à jour avec les valeurs <code>John</code> pour le <code>firstName</code> et <code>Doe</code> pour le <code>lastName</code>.</p>
<blockquote>
<p>La question qui vient à l’esprit est la suivante ?</p>
<p>Est-ce aussi le cas lorsque nous faisons face à un formulaire qui contient des contrôles de type <code>FormArray</code> ?</p>
<p>La meilleure façon de s’en assurer, est de faire l’expérience. Let’s go !</p>
</blockquote>
<p>Considérons le code ci-dessous :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Component } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
<span class="hljs-keyword">import</span> { FormBuilder, FormArray, FormGroup } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/forms'</span>;

<span class="hljs-meta">@Component</span>({
  selector: <span class="hljs-string">'app-addresses'</span>,
  template: <span class="hljs-string">`
    &lt;form [formGroup]="addressesForm"&gt;
      &lt;div formArrayName="addresses"&gt;
        &lt;div *ngFor="let address of addresses.controls; let i = index" [formGroupName]="i"&gt;
          &lt;input type="text" formControlName="road" placeholder="Road"&gt;
          &lt;input type="text" formControlName="city" placeholder="City"&gt;
        &lt;/div&gt;
        &lt;button type="button" (click)="addAddress()"&gt;Add an address&lt;/button&gt;
      &lt;/div&gt;
    &lt;/form&gt;
  `</span>
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> AddressesComponent {
  addressesForm = <span class="hljs-keyword">new</span> FormGroup({
   addresses: <span class="hljs-keyword">new</span> FormArray([
      <span class="hljs-keyword">new</span> FormGroup({
        road: <span class="hljs-keyword">new</span> FormControl(),
        city: <span class="hljs-keyword">new</span> FormControl()
      })
   ])
 });

  <span class="hljs-keyword">public</span> get addressesFormArray() {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.addressForm.get(<span class="hljs-string">'addresses'</span>);
 }
}
</code></pre>
<p>Dans le code ci-dessus, nous avons un formulaire qui contient un contrôle de type <code>FormArray</code>. Supposons, qu’après un traitement, nous voulons mettre à jour les adresses de notre formulaire en utilisant naturellement notre méthode <code>patchValue(…)</code>. Voici ce que nous écrirons :</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">this</span>.addressesFormArray.patchValue([
    {
        road: <span class="hljs-string">'1'</span>,
        city: <span class="hljs-string">'Paris'</span>
    },
    {
        road: <span class="hljs-string">'2'</span>,
        city: <span class="hljs-string">'Lyon'</span>
    },
    {
        road: <span class="hljs-string">'3'</span>,
        city: <span class="hljs-string">'Marseille'</span>
    }
]);
</code></pre>
<p>Dans notre exemple, si l’on se confère à la logique de la méthode <code>patchValue(…)</code>, normalement, notre formulaire doit être mis à jour avec les valeurs des adresses indiquées.</p>
<blockquote>
<p>Malheureusement, je ne suis navré de vous décevoir mais ce n’est pas le cas !</p>
<p>Lorsqu’on se rend sur l’interface web, on se rend compte que c’est uniquement le premier objet du tableau des adresses qui est conservé comme valeur.  </p>
<p>Notre méthode <code>patchValue(…)</code> n’a pas su itérer sur le reste des éléments et c’est là tout le piège !</p>
</blockquote>
<p>Pour résoudre ce problème, une gymnastique s’impose à nous lors de l’utilisation de notre méthode <code>patchValue(…)</code>. Voici donc comment procéder étape par étape :</p>
<p>Étape 1 : Nettoyer notre <code>FormArray</code></p>
<pre><code class="lang-typescript"><span class="hljs-built_in">this</span>.addressesFormArray.clear();
</code></pre>
<p>Étape 2 : Recréer une nouvelle instance de notre <code>FormArray</code></p>
<pre><code class="lang-typescript"><span class="hljs-comment">/*
    Considérons une variable `myAddresses` qui contient toutes les adresses récupérées 
    après un appel d'API par exemple
*/</span>

<span class="hljs-keyword">while</span> (<span class="hljs-built_in">this</span>.addressesFormArray.controls.length &lt; myAddresses.length) {
   <span class="hljs-built_in">this</span>.addressesFormArray.push(<span class="hljs-keyword">new</span> FormGroup({
        road: <span class="hljs-keyword">new</span> FormControl(),
        city: <span class="hljs-keyword">new</span> FormControl()
    }));
}
</code></pre>
<p>Étape 3 : Mettre à jour les valeurs avec <code>patchValue(…)</code></p>
<pre><code class="lang-typescript"><span class="hljs-built_in">this</span>.addressesFormArray.patchValue(myAddresses);
</code></pre>
<p>En somme, pour appliquer un <code>patchValue(…)</code> sur notre formulaire, il est crucial d’identifier la nature des contrôles. S’agit t-il d’un <code>FormControl</code> classique ou d’un <code>FormArray</code> conduisant à un formulaire complexe ? Suivant, le cas, nous savons aujourd’hui quoi faire.</p>
<blockquote>
<p>Pour plus d'informations 👉 <a target="_blank" href="https://angular.dev/guide/forms/reactive-forms">Reactive Forms</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Le cycle de vie d'un composant en Angular : que faut t-il retenir avec la renaissance du framework ?]]></title><description><![CDATA[Depuis la sortie de la version 17 du framework Angular, on parle de la renaissance de Angular. Il y a eu beaucoup de changements majeurs depuis la version 16 du framework pour préparer le terrain à cette renaissance. Ces changements majeurs ont rendu...]]></description><link>https://angulardev.fr/le-cycle-de-vie-dun-composant-en-angular-que-faut-t-il-retenir-avec-la-renaissance-du-framework</link><guid isPermaLink="true">https://angulardev.fr/le-cycle-de-vie-dun-composant-en-angular-que-faut-t-il-retenir-avec-la-renaissance-du-framework</guid><category><![CDATA[Angular]]></category><category><![CDATA[lifecycle]]></category><category><![CDATA[components]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Tue, 19 Nov 2024 06:14:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/aOC7TSLb1o8/upload/4adc466418af33a1b455249b912963b2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Depuis la sortie de la version 17 du framework Angular, on parle de la renaissance de Angular. Il y a eu beaucoup de changements majeurs depuis la version 16 du framework pour préparer le terrain à cette renaissance. Ces changements majeurs ont rendu le framework davantage attractif et performant. Dans cet article, nous nous intéresserons au cycle de vie d’un composant en Angular afin de mieux comprendre quelques subtilités d’une part et d’autre part, quels sont les changements qui y ont été apportés.</p>
<p>Avant d’entrer dans le vif du sujet, nous essaierons de comprendre ce qu’est le cycle de vie d’un composant.</p>
<p>Le cycle de vie d’un composant en général, peu importe la technologie utilisée représente les différentes étapes par lesquelles le composant passe depuis la création jusqu’à la destruction. En effet, ce processus est très similaire à la vie d’un être humain par exemple. L’homme naît, grandit, vieillit et meurt. C’est comme cela, il faut voir le cycle de vie d’un composant également. En Angular, ces différentes phases sont perceptibles à travers les différentes méthodes que le framework nous offre. Le cycle de vie d'un composant est étroitement lié à la façon dont Angular vérifie les modifications apportées à nos composants au fil du temps. Notre prochain objectif sera décortiquer une à une ces méthodes afin de rendre le plus accessible possible à la compréhension des uns et des autres.</p>
<ol>
<li><strong>La méthode</strong> <code>constructor()</code> <strong>:</strong></li>
</ol>
<p>Le framework Angular est basé sur le langage TypeScript. Chaque composant Angular n’est rien d’autre qu’une classe TypeScript. Lorsqu’on parle de classe, on parle de la programmation orientée objet. Ainsi, la première méthode qui s’exécute dans une classe c’est le constructeur de la classe qui est <code>constructor</code>.</p>
<p>Dès que le framework Angular crée une instance de notre composant à l’exécution de notre application, c’est la méthode <code>constructor</code> qui s’exécute en premier. On appelle cette étape du cycle de vie d’un composant Angular : <strong><em>création</em></strong>.</p>
<ol start="2">
<li><strong>La méthode</strong> <code>ngOnChanges()</code> <strong>:</strong></li>
</ol>
<p>Après la phase de création que nous venons de voir précédemment, la prochaine étape c’est la phase : <strong><em>détection de changements</em></strong> (<strong><em>change detection</em></strong> en anglais). La première méthode de cette phase est la méthode <code>ngOnChanges()</code>.</p>
<p>La méthode ngOnChanges s'exécute après que les entrées du composant ont été modifiées. En effet, pour jauger de la pertinence de cette méthode et comprendre comment elle fonctionne, il faut la tester sur un composant ayant des <code>inputs</code>.</p>
<p>En Angular, lorsqu’on décide de passer des informations d’un composant A parent vers un composant B enfant, on passe par des <code>inputs</code> au niveau du composant enfant. Lorsque le composant parent A met à jour les inputs du composant enfant B, la méthode <code>ngOnChanges()</code> se déclenche et ce à chaque nouvelle mise à jour des <code>inputs</code> .</p>
<ol start="3">
<li><strong>La méthode</strong> <code>ngOnInit()</code> <strong>:</strong></li>
</ol>
<p>La deuxième méthode de la phase de détection de changement du cycle de vie d’un composant Angular est la méthode <code>ngOnInit()</code>.</p>
<p>La méthode <code>ngOnInit()</code> s'exécute après qu'Angular ait initialisé toutes les entrées (<em>inputs</em>) des composants avec leurs valeurs initiales. La méthode <code>ngOnInit()</code> d'un composant s'exécute exactement une fois.</p>
<p>Cette étape se produit avant que le <em>template HTML</em> du composant ne soit initialisé. Cela signifie que vous pouvez mettre à jour l'état du composant en fonction de ses valeurs d'entrée (<em>input</em>) initiales.</p>
<ol start="4">
<li><strong>La méthode</strong> <code>ngDoCheck()</code></li>
</ol>
<p>Juste après l’exécution de la méthode <code>ngOnInit()</code>, notre composant demeure toujours dans la phase de détection de changement à travers l’exécution de la méthode <code>ngDoCheck()</code> .</p>
<p>La première fois que la méthode <code>ngDoCheck()</code> s’exécute c’est juste après l’exécution de la méthode <code>ngOnInit()</code>. Ensuite, <code>ngDoCheck()</code> s'exécute avant chaque vérification par Angular des changements apportés au niveau du <em>template HTML</em> de notre composant.</p>
<p>Cette méthode s'exécute très fréquemment et peut avoir un impact significatif sur les performances de votre page. Ainsi, évitons d’utiliser cette méthode autant que possible, ne l'utilisons que lorsque nous n’avons vraiment pas d'autre alternative.</p>
<ol start="5">
<li><strong>La méthode</strong> <code>ngAfterContentInit()</code></li>
</ol>
<p>Lorsque dans notre composant Angular, nous interagissons avec des <code>content queries</code> à savoir <code>@ContentChild()</code> ou <code>@ContentChildren()</code> , la méthode <code>ngAfterContentInit()</code> s'exécute une seule fois lorsque tous les enfants imbriqués (<code>content queries</code>) dans le composant ont été initialisés.</p>
<p>Nous pouvons donc accéder à l'état initialisé de nos queries mais toute tentative de modification de l'état dans cette méthode entraîne une erreur <code>ExpressionChangedAfterItHasBeenCheckedError</code>.</p>
<p>La méthode <code>ngAfertContentInit()</code> lorsqu’elle est définie, s’exécute juste après le <code>ngDoCheck()</code> .</p>
<p>Pour ceux qui n’ont aucune idée de ce qu’est une <code>content query</code>, voici un exemple :</p>
<pre><code class="lang-typescript"><span class="hljs-meta">@Component</span>({
  selector: <span class="hljs-string">'custom-toggle'</span>,
  ...
  template: <span class="hljs-string">`&lt;div&gt; 
             &lt;ng-content&gt;&lt;/ng-content&gt; 
             &lt;/div&gt;`</span>
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> CustomToggle {
  text: <span class="hljs-built_in">string</span>;
}
<span class="hljs-meta">@Component</span>({
  selector: <span class="hljs-string">'custom-expando'</span>,
  ...
  template: <span class="hljs-string">`&lt;div&gt; 
             &lt;ng-content&gt;&lt;/ng-content&gt; 
             &lt;/div&gt;`</span>
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> CustomExpando {
  <span class="hljs-meta">@ContentChild</span>(CustomToggle) toggle: CustomToggle;
  ngAfterContentInit() {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">this</span>.toggle.text);
  }
}
<span class="hljs-meta">@Component</span>({
  selector: <span class="hljs-string">'user-profile'</span>,
  template: <span class="hljs-string">`
    &lt;custom-expando&gt;
      &lt;custom-toggle&gt;Show&lt;/custom-toggle&gt;
    &lt;/custom-expando&gt;
  `</span>
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> UserProfile { }
</code></pre>
<blockquote>
<p>Le composant <code>custom-toggle</code> est projecté dans le composant <code>custom-expando</code> à travers la directive <code>ng-content</code>.</p>
<p>Le composant <code>custom-toggle</code> à son tour est capable de projecter du contenu d’où le texte <code>Show</code> à l’intérieur des balises <code>custom-toggle</code> lors de son utilisation dans le template du composant <code>user-profile</code>.</p>
<p>Lorsqu’on parle de <code>content query</code>, il faut penser à la <code>projection de contenue</code> avec la directive <code>ng-content</code>.</p>
</blockquote>
<ol start="6">
<li><strong>La méthode</strong> <code>ngAfterContentChecked()</code> <strong>:</strong></li>
</ol>
<p>La méthode <code>ngAfterContentChecked()</code> est étroitement liée à la méthode <code>ngAfterContentInit()</code>. En effet, lorsque toutes nos <code>content queries</code> sont initialisées, la méthode <code>ngAfterContentChecked()</code> s’exécute pour marquer ces <code>content queries</code> comme étant <code>checked</code>. Cela signifie tout simplement que ces <code>content queries</code> ont été vérifiées pour déterminer si des changements ont eu lieu à leurs différents niveaux.</p>
<p>Cette méthode s'exécute très fréquemment et peut avoir un impact significatif sur les performances de votre page. Ainsi, évitons d’utiliser cette méthode autant que possible, ne l'utilisons que lorsque nous n’avons vraiment pas d'autre alternative.</p>
<p>Bien que nous puissions accéder à l'état mis à jour des <code>content queries</code> ici, toute tentative de modification d'un état dans cette méthode entraîne une erreur <code>ExpressionChangedAfterItHasBeenCheckedError</code> (<code>ExpressionChangedAfterItBeenChecked</code>).</p>
<ol start="7">
<li><strong>La méthode</strong> <code>ngAfterViewInit()</code> <strong>:</strong></li>
</ol>
<p>Tout comme nous l’avons vu précédemment avec la méthode <code>ngAfterContentInit()</code> , la méthode <code>ngAferViewInit()</code> aussi s’exécute juste après la méthode <code>ngDoCheck()</code> .</p>
<p>En effet, pendant que la méthode <code>ngAfterContentInit()</code> est utilisée pour les <code>content queries</code> , la méthode <code>ngAfterViewInit()</code> est utilisée pour les view queries (<code>@ViewChild()</code> , <code>@ViewChildren()</code>.</p>
<p>La méthode <code>ngAfterViewInit()</code> s'exécute une seule fois lorsque tous les enfants imbriqués (<code>view queries</code>) dans le composant ont été initialisés.</p>
<p>Nous pouvons donc accéder à l'état initialisé de nos <code>view queries</code> mais toute tentative de modification de l'état dans cette méthode entraîne une erreur <code>ExpressionChangedAfterItHasBeenCheckedError</code>.</p>
<p>Pour ceux qui n’ont aucune idée de ce qu’est une view query, voici un exemple :</p>
<pre><code class="lang-typescript"><span class="hljs-meta">@Component</span>({
  selector: <span class="hljs-string">'custom-card-header'</span>,
  ...
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> CustomCardHeader {
  text: <span class="hljs-built_in">string</span>;
}
<span class="hljs-meta">@Component</span>({
  selector: <span class="hljs-string">'custom-card'</span>,
  template: <span class="hljs-string">'&lt;custom-card-header&gt;Visit sunny California!&lt;/custom-card-header&gt;'</span>,
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> CustomCard {
  <span class="hljs-meta">@ViewChild</span>(CustomCardHeader) header: CustomCardHeader;
  ngAfterViewInit() {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">this</span>.header.text);
  }
}
</code></pre>
<blockquote>
<p>Dans le composant parent <code>CustomCard</code>, on essaie de récupérer le composant enfant <code>CustomCardHeader</code>.</p>
<p>Lorsqu’on parle de <code>view query</code>, il faut penser à un <code>composant enfant</code> qu’on essaie de récupérer depuis un <code>composant parent</code> dynamiquement.</p>
</blockquote>
<ol start="8">
<li><strong>La méthode</strong> <code>ngAfterViewChecked()</code> <strong>:</strong></li>
</ol>
<p>La méthode <code>ngAfterViewChecked()</code> suit la même logique que la méthode <code>ngAfterContentChecked()</code> . Tout comme la méthode <code>ngAfterContentChecked()</code> est étroitement liée à la méthode <code>ngAfterContentInit()</code> , la méthode <code>ngAfterViewChecked()</code> est aussi liée à la méthode <code>ngAfterViewInit()</code> .</p>
<p>En effet, lorsque toutes nos <code>view queries</code> sont initialisées, la méthode <code>ngAfterViewChecked()</code> s’exécute pour marquer ces <code>view queries</code> comment étant <code>checked</code>. Cela signifie tout simplement que ces <code>view queries</code> ont été vérifiées pour déterminer si des changements ont eu lieu à leurs différents niveaux.</p>
<p>Bien que nous puissions accéder à l'état mis à jour des <code>view queries</code> ici, toute tentative de modification d'un état dans cette méthode entraîne une erreur <code>ExpressionChangedAfterItHasBeenCheckedError</code> (<code>ExpressionChangedAfterItBeenChecked</code>).</p>
<ol start="9">
<li><strong>Les méthodes</strong> <code>afterRender()</code> <strong>et</strong> <code>afterNextRender()</code><strong>:</strong></li>
</ol>
<p>Les méthodes <code>afterNextRender()</code> et <code>afterRender()</code> font parties des nouvelles méthodes ajoutées au cycle de vie d’un composant Angular depuis la version 16.2 du framework.</p>
<p>La méthode <code>afterRender()</code> se déclenche à chaque fois que <strong><em>tous les composants sont chargés dans le DOM</em></strong>*.*</p>
<p>Quant à la méthode <code>afterNextRender()</code>, elle se déclenchera la prochaine fois que <strong><em>tous les composants seront chargés dans le DOM</em></strong> mais une seule fois*.*</p>
<ol start="10">
<li><strong>La méthode</strong> <code>ngOnDestroy()</code> <strong>:</strong></li>
</ol>
<p>La méthode <code>ngOnDestroy</code> s'exécute une fois juste avant la destruction d'un composant. Angular détruit un composant lorsqu'il n'est plus affiché sur la page, par exemple lorsqu'il est masqué par un <code>NgIf</code> ou lorsqu'on navigue vers une autre page.</p>
<p>En somme, le cycle de vie d’un composant Angular se résume en trois phase à savoir : <strong><em>la phase de création</em></strong>, <strong><em>la phase de détection de changements</em></strong> et <strong><em>le rendering</em></strong>. La <strong><em>phase de rendering</em></strong> a été introduite dans le framework à la sortie de la version 16 du framework. Chacune de ces phases fait appel à des méthodes dans lesquelles nous pouvons gérer des traitements au besoin. Il est très important de maîtriser ces phases afin d’assurer la performance de nos composants Angular.</p>
<blockquote>
<p>Pour plus d'informations 👉 <a target="_blank" href="https://angular.dev/guide/components/lifecycle">Component Lifecycle</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Model Input : Un aperçu approfondi]]></title><description><![CDATA[Dans notre précédent article, nous avons vu ensemble ce qu'est un Signal Input et comment l'utiliser. Nous avons particulièrement remarqué sa similarité avec le décorateur @Input().
Dans ce nouvel article, nous verrons ensemble un nouveau type de inp...]]></description><link>https://angulardev.fr/model-input-un-apercu-approfondi</link><guid isPermaLink="true">https://angulardev.fr/model-input-un-apercu-approfondi</guid><category><![CDATA[model input]]></category><category><![CDATA[Signal Input]]></category><category><![CDATA[Angular]]></category><category><![CDATA[signals]]></category><category><![CDATA[Angular Signals]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Fri, 14 Jun 2024 08:19:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/IUY_3DvM__w/upload/b4e935466bfcb417735770af6c8ae6d4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Dans notre précédent <a target="_blank" href="https://angulardev.fr/signal-input-comment-ca-marche">article</a>, nous avons vu ensemble ce qu'est un <code>Signal Input</code> et comment l'utiliser. Nous avons particulièrement remarqué sa similarité avec le décorateur <code>@Input()</code>.</p>
<p>Dans ce nouvel article, nous verrons ensemble un nouveau type de input qui est toujours basé sur <code>Angular Signal</code>. Ce nouveau type de input a été conçu de façon très ingénieuse et est aussi très similaire à une fonctionnalité existante de Angular qui est très utilisée. Découvrons-le ensemble sans plus attendre !</p>
<h3 id="heading-quest-ce-quun-model-input-en-angular">Qu'est-ce qu'un <code>Model Input</code> en Angular ?</h3>
<p>En nous basant sur la documentation de Angular, un <code>Model Input</code> est <strong>un type spécial d'input qui permet à un composant A de propager de nouvelles valeurs vers un composant B.</strong> En effet, lorsqu'on analyse cette définition, ce qui nous vient à l'esprit c'est qu'un <code>Model Input</code> fait la même chose qu'un <code>Signal Input</code> dans le fonctionnement comme l'indique cet exemple :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {Component, model, input} <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
<span class="hljs-meta">@Component</span>({...})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> CustomCheckbox {
  <span class="hljs-comment">// This is a model input.</span>
  checked = model(<span class="hljs-literal">false</span>);

  <span class="hljs-comment">// This is a standard input.</span>
  disabled = input(<span class="hljs-literal">false</span>);
}

&lt;!--- Parent component side ---&gt;
&lt;custom-checkbox [checked]=<span class="hljs-string">"true"</span> [disabled]=<span class="hljs-string">"false"</span> /&gt;
</code></pre>
<p>Le code ci-dessus nous démontre que les deux types d'input à savoir <code>Signal Input</code> et <code>Model Input</code> permettent de faire le <code>one-way binding</code> c'est-à-dire dans notre cas ici <strong>la communication unidirectionnelle d'un composant parent vers un composant enfant</strong> et non l'inverse.</p>
<p>Cependant, le <code>Model Input</code> ne se limite pas à cela. Dans sa philosophie de propager de nouvelles valeurs, le <code>Model Input</code> est <strong>également accessible en écriture contrairement à un</strong> <code>Signal Input</code>. Nous pouvons donc modifier la valeur d'un <code>Model Input</code> depuis le composant enfant où il a été instancié ce qui n'est pas le cas du <code>Signal Input</code> qui est <code>read-only</code>. Voici une illustration :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {Component, model, input} <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
<span class="hljs-meta">@Component</span>({
  selector: <span class="hljs-string">'custom-checkbox'</span>,
  template: <span class="hljs-string">'&lt;div (click)="toggle()"&gt; ... &lt;/div&gt;'</span>,
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> CustomCheckbox {
  checked = model(<span class="hljs-literal">false</span>);
  disabled = input(<span class="hljs-literal">false</span>);

  toggle() {
    <span class="hljs-comment">// While standard inputs are read-only, you can write directly to model inputs.</span>
    <span class="hljs-built_in">this</span>.checked.set(!<span class="hljs-built_in">this</span>.checked());
  }
}
</code></pre>
<p>Dans l'illustration de code ci-dessus, nous remarquons l'utilisation de la méthode <code>set()</code> sur notre <code>Model Input</code>. Nous pouvons en déduire que la méthode <code>update()</code> aussi peut être utilisée et pour conclure que notre <code>Model Input</code> est un <code>WritableSignal</code>.</p>
<p>Pour finir, en nous basant sur les deux extraits de code précédent, on peut facilement émettre l'hypothèse suivante :</p>
<blockquote>
<p><strong><em>Lorsque le composant parent ou enfant (peu importe l'un des deux) écrit une nouvelle valeur dans le Model Input, Angular peut propager cette nouvelle valeur vers le composant qui lie cette valeur.</em></strong></p>
</blockquote>
<p>En effet, c'est typiquement le fonctionnement du <code>Model Input</code>. <strong>Il ne permet pas que la liaison unidirectionnelle (</strong><code>one-way binding</code><strong>) mais également la liaison bidirectionnelle (</strong><code>two-way binding</code><strong>)</strong>. Tout ceci nous fait penser à <code>ngModel</code> que nous utilisons déjà énormément dans Angular.</p>
<h3 id="heading-two-way-binding-avec-model-input"><code>Two-way binding</code> avec <code>Model Input</code></h3>
<p>La documentation de Angular nous présente deux situations différentes pour l'utilisation du <code>two-way binding</code> en se basant sur <code>Angular Signal</code> ou sur du <code>JS/TS</code> classique.</p>
<ul>
<li><code>Two-way binding</code> avec du <code>JS/TS</code> classique :</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-meta">@Component</span>({
  ...,
  <span class="hljs-comment">// `checked` is a model input.</span>
  <span class="hljs-comment">// The parenthesis-inside-square-brackets syntax (aka "banana-in-a-box") creates a two-way binding</span>
  template: <span class="hljs-string">'&lt;custom-checkbox [(checked)]="isAdmin" /&gt;'</span>,
})

<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> UserProfile {
  <span class="hljs-keyword">protected</span> isAdmin = <span class="hljs-literal">false</span>;
}
</code></pre>
<p>Dans l'exemple ci-dessus, le composant <code>CustomCheckbox</code> peut écrire des valeurs dans <code>checked</code>, qui propage ensuite ces valeurs vers la propriété <code>isAdmin</code> dans <code>UserProfile</code>. Cette liaison permet de synchroniser les valeurs de <code>checked</code> et de <code>isAdmin</code>.</p>
<ul>
<li><code>Two-way binding</code> avec <code>Angular Signal</code> :</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-meta">@Component</span>({
  ...,
  <span class="hljs-comment">// `checked` is a model input.</span>
  <span class="hljs-comment">// The parenthesis-inside-square-brackets syntax (aka "banana-in-a-box") creates a two-way binding</span>
  template: <span class="hljs-string">'&lt;custom-checkbox [(checked)]="isAdmin" /&gt;'</span>,
})

<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> UserProfile {
  <span class="hljs-keyword">protected</span> isAdmin = signal(<span class="hljs-literal">false</span>);
}
</code></pre>
<p>Dans cet exemple ci-dessus, le <code>CustomCheckbox</code> peut écrire des valeurs dans <code>checked</code>, qui propage ensuite ces valeurs vers <code>isAdmin</code> dans <code>UserProfile</code>. Cette liaison permet de synchroniser les valeurs de <code>checked</code> et de <code>isAdmin</code>. Cependant, il est très important de noter qu'ici, <strong><em>la liaison transmet le signal</em></strong> <code>isAdmin</code> <strong><em>lui-même, et non la valeur du signal.</em></strong></p>
<p>Pour finir, en nous basant sur le principe du <code>two-way binding</code>, lorsque nous déclarons un <code>Model Input</code> dans un composant ou une directive, Angular crée automatiquement un <code>event</code> correspondant. Voici un exemple :</p>
<pre><code class="lang-typescript"><span class="hljs-meta">@Directive</span>({...})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> CustomCheckbox {
  <span class="hljs-comment">// This automatically creates an output named "checkedChange".</span>
  <span class="hljs-comment">// Can be subscribed to using `(checkedChange)="handler()"` in the template.</span>
  checked = model(<span class="hljs-literal">false</span>);
}
</code></pre>
<p>Angular émet cet <code>event</code> de changement chaque fois que nous écrivons une nouvelle valeur dans le <code>Model Input</code> en appelant les méthodes <code>set()</code> ou <code>update()</code>.</p>
<h3 id="heading-differences-entre-model-et-input">Différences entre <code>model()</code> et <code>input()</code></h3>
<p>Nous avons commencé cet article en essayant de voir la similarité entre un <code>Signal Input</code> et un <code>Model Input</code>. A présent, nous verrons les éléments qui les différencient :</p>
<ul>
<li><p><code>model()</code> définit à la fois un <code>input</code> et un <code>output</code>. Le nom de l'<code>output</code> est toujours le nom de l'<code>input</code> suffixé par <code>Change</code> pour prendre en charge les liaisons bidirectionnelles. C'est au composant qui l'implémente de décider s'il veut utiliser seulement l'<code>input</code>, seulement l'<code>output</code>, ou les deux.</p>
</li>
<li><p><code>ModelSignal</code> est un <code>WritableSignal</code>, ce qui signifie que sa valeur peut être modifiée de n'importe où à l'aide des méthodes <code>set()</code> et <code>update()</code>. Lorsqu'une nouvelle valeur est attribuée, le <code>ModelSignal</code> émet un <code>output</code>. Il en va différemment de l'<code>InputSignal</code>, qui est en <code>read-only</code>.</p>
</li>
<li><p>Les <code>Model Inputs</code> ne prennent pas en charge <code>transform</code>, contrairement aux <code>Signal Inputs</code>.</p>
</li>
</ul>
<p>En somme, dans cet article, l'objectif a été de vulgariser le concept de <code>Model Input</code>. Bien qu'ayant une similarité avec le <code>Signal Input</code>, nous avons remarqué qu'il est beaucoup plus proche de la philosophie autour du <code>ngModel</code> à travers le <code>two-way binding</code> mais basé sur <code>Angular Signal</code>. Toutefois, il faut noter que <code>Model Input</code> est toujours <strong><em>Developer Preview</em></strong>.</p>
<blockquote>
<p>Pour plus d'informations 👉 <a target="_blank" href="https://angular.dev/guide/signals/model#two-way-binding-with-signals">Model Inputs</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Signal Input : comment ça marche ?]]></title><description><![CDATA[Dans notre précédent article Angular Signals : que faut-il retenir quand je débute ?, nous avons exploré ce qui se cache derrière les Signals en Angular. Aujourd'hui, nous aborderons l'un des concepts liés aux Angular Signals : les Signal Inputs, com...]]></description><link>https://angulardev.fr/signal-input-comment-ca-marche</link><guid isPermaLink="true">https://angulardev.fr/signal-input-comment-ca-marche</guid><category><![CDATA[Angular]]></category><category><![CDATA[signals]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Wed, 12 Jun 2024 05:28:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/wrkNQmhmdvY/upload/eb19b709178509e635fb0440ad2e41f8.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Dans notre précédent article <a target="_blank" href="https://angulardev.fr/angular-signals-que-faut-t-il-retenir-quand-je-debute">Angular Signals : que faut-il retenir quand je débute ?</a>, nous avons exploré ce qui se cache derrière les <code>Signals</code> en <code>Angular</code>. Aujourd'hui, nous aborderons l'un des concepts liés aux <code>Angular Signals</code> : les <code>Signal Inputs</code>, comme l'indique le titre de l'article. L'objectif est de comprendre ce que c'est, comment l'utiliser et pourquoi l'utiliser.</p>
<h3 id="heading-quest-ce-quun-signal-input-en-angular">Qu'est-ce qu'un <code>Signal Input</code> en Angular ?</h3>
<p>Avant de définir ce qu'est un <code>Signal Input</code>, faisons un peu d'histoire. En <code>Angular</code>, pour passer des propriétés d'un <em>composant parent A</em> à un <em>composant enfant B</em>, nous utilisons habituellement les <code>@Input()</code>. Voici un exemple :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {Component, input} <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
<span class="hljs-meta">@Component</span>({...})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ComponentB {
  <span class="hljs-meta">@Input</span>() age: <span class="hljs-built_in">number</span>;
}

&lt;!--- Parent component side (ComponentA) ---&gt;
&lt;component-b [age]=<span class="hljs-string">'10'</span> /&gt;
</code></pre>
<p>Avec l'arrivée de <code>Signal</code> en <code>Angular</code>, la core team de <code>Angular</code> a introduit une nouvelle méthode pour gérer cette communication, appelée <code>Signal Input</code>, afin d'optimiser les performances du framework.</p>
<p>Le <code>Signal Input</code> joue le même rôle que <code>@Input()</code><strong><em>c'est-à-dire permettre l'injection de données du composant parent A vers le composant enfant B.</em></strong></p>
<h3 id="heading-comment-utiliser-un-signal-input">Comment utiliser un Signal Input ?</h3>
<p>Angular prend en charge deux variantes de <code>Signal Input</code> à savoir :</p>
<ul>
<li><p><code>Optional input</code> : Il faut souligner que le <code>Signal Input</code> est <strong><em>optionnel par défaut</em></strong>. Tout comme le <code>@Input()</code>, si au niveau du <em>composant parent A</em>, nous ne faisons pas passer l'input en propriété au <em>composant enfant B</em>, ce n'est pas bloquant. Voici un exemple de composant utilisant le Optional Signal Input :</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">import</span> {Component, input} <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
  <span class="hljs-meta">@Component</span>({...})
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> MyComp {
    <span class="hljs-comment">// optional</span>
    firstName = input&lt;<span class="hljs-built_in">string</span>&gt;();         <span class="hljs-comment">// InputSignal&lt;string|undefined&gt;</span>
    age = input(<span class="hljs-number">0</span>);                      <span class="hljs-comment">// InputSignal&lt;number&gt;</span>
  }
</code></pre>
<p>  Pour le <code>Signal Input</code> nommé <code>firstName</code>, on note que le type est <code>string | undefined</code>. En effet, si l'on ne spécifie pas une valeur initiale à un <code>Signal Input</code> qui optionnel même après l'avoir typé, <code>Angular</code> utilisera le type <code>undefined</code> de façon implicite. <strong><em>Il faut donc penser à initialiser votre</em></strong><code>Signal Input</code><strong><em>avec une valeur quand il est optionnel.</em></strong></p>
</li>
<li><p><code>Required input</code> : Ici, le <code>Signal Input</code> est rendu obligatoire et il est nécessaire d'indiquer le type de la donnée à recevoir. En voici un exemple :</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">import</span> {Component, input} <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
  <span class="hljs-meta">@Component</span>({...})
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> MyComp {
    <span class="hljs-comment">// required</span>
    lastName = input.required&lt;<span class="hljs-built_in">string</span>&gt;(); <span class="hljs-comment">// InputSignal&lt;string&gt;</span>
  }
</code></pre>
</li>
</ul>
<p>Tout comme le <code>@Input()</code> classique que nous connaissons jusque-là, le <code>Signal Input</code> aussi peut être utilisé avec des <code>alias</code>. Le but des <code>alias</code> c'est d'éviter des conflits de noms d'attributs. Voici un exemple lié à l'utilisation d'un <code>alias</code> sur un <code>Signal Input</code> :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {Component, input} <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
<span class="hljs-meta">@Component</span>({...})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> MyComp {
  age = input(<span class="hljs-number">0</span>, {alias: <span class="hljs-string">'studentAge'</span>});
}

&lt;!--- Parent component side ---&gt;
&lt;my-comp [studentAge]=<span class="hljs-string">'10'</span> /&gt;
</code></pre>
<p>A présent, nous parlerons d'une spécifité propre au <code>Signal Input</code> que nous n'avons pas avec <code>@Input()</code>. Il s'agit de la <strong><em>transformation de valeur</em></strong> [<em>cette expression fait penser aux Pipes 😃].</em> En effet, tout comme la philosophie derrière un <code>Pipe</code>, nous sommes capables avec un <code>Signal Input</code><strong><em>de faire un traitement sur la valeur d'entrée sans en modifier le sens. La transformation convertira donc la valeur brute envoyée par le parent au type d'entrée attendu. La transformation doit être une fonction pure.</em></strong> En voici une illustration :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> MyComp {
  disabled = input(<span class="hljs-literal">false</span>, {
    transform: <span class="hljs-function">(<span class="hljs-params">value: <span class="hljs-built_in">boolean</span>|<span class="hljs-built_in">string</span></span>) =&gt;</span> <span class="hljs-keyword">typeof</span> value === <span class="hljs-string">'string'</span> ? value === <span class="hljs-string">''</span> : value,
  });
}
</code></pre>
<p>Dans l'exemple ci-dessus, nous déclarons un <code>Signal Input</code> nommé <code>disabled</code> qui accepte des valeurs de type <code>boolean</code> et <code>string</code>. <strong><em>Ces valeurs (peu importe quelles soient</em></strong><code>boolean</code><strong><em>ou</em></strong><code>string</code><strong><em>) sont ensuite traitées en</em></strong><code>boolean</code><strong><em>avec la transformation, ce qui donne des</em></strong><code>boolean</code><strong><em>.</em></strong></p>
<p>De cette façon, nous ne traitons que du <code>boolean</code> à l'intérieur de notre <em>composant enfant</em> lorsque nous appelons <code>this.disabled()</code>, tandis au niveau de notre <em>composant parent,</em> nous pouvons passer un <code>string</code> comme raccourci pour marquer notre composant <code>disabled</code>.</p>
<blockquote>
<p>Pour finir, tout ce qui a été abordé dans ce précédent <a target="_blank" href="https://angulardev.fr/angular-signals-que-faut-t-il-retenir-quand-je-debute">article</a> concernant les <code>Signals</code> est valable pour les <code>Signal Inputs</code> à savoir les méthodes <code>computed()</code> et <code>effect()</code>.</p>
</blockquote>
<h3 id="heading-pourquoi-et-quand-faut-t-il-choisir-signal-input-au-lieu-de-input">Pourquoi et quand faut t-il choisir Signal Input au lieu de <code>@Input()</code> ?</h3>
<p>Le <code>Signal Input</code> est une alternative réactive à la méthode <code>@Input()</code>. Voici les nombreux avantages qu'offrent un <code>Signal Input</code> en nous basant sur la documentation :</p>
<ul>
<li><p>Le <code>required input</code> de <code>Signal Input</code> ne nécessite pas de valeur initiale ou d'astuce pour indiquer à <code>TypeScript</code> qu'un <code>input</code> a toujours une valeur.</p>
</li>
<li><p>Les transformations (<code>transform</code>) sont automatiquement vérifiées pour correspondre aux valeurs acceptées.</p>
</li>
<li><p>Les <code>Signal Inputs</code>, lorsqu'ils sont utilisées dans des <em>templates</em>, marqueront automatiquement les composants <code>OnPush</code> en <code>dirty</code>. <strong><em>Avec</em></strong><code>@Input()</code><strong><em>, il fallait le faire soi-même pour permettre le changement de détection.</em></strong></p>
</li>
<li><p>L'utilisation de la méthode <code>effect()</code> au lieu de <code>ngOnChanges()</code> ou <code>setters</code> qui rend plus facile et aisé le contrôle du changement des valeurs.</p>
</li>
</ul>
<p>En somme, nous avons à présent une compréhension plus poussée de l'essentiel à connaître sur les <code>Signal Inputs.</code> Nous aborderons dans de prochains articles, d'autres concepts liés à <code>Angular Signal</code>.</p>
<blockquote>
<p>Pour plus d'informations 👉 <a target="_blank" href="https://angular.dev/guide/signals/inputs">Signal Inputs</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[RxJS Interop : de quoi s'agit t-il ?]]></title><description><![CDATA[Lorsque la core team de Angular a annoncé l'arrivée du concept de Signals, plusieurs interrogations se sont soulevées quant à l'utilisation de la bibliothèque RxJS qui a été jusque-là un atout très important pour le framework Angular.

Est-ce que Ang...]]></description><link>https://angulardev.fr/rxjs-interop-de-quoi-sagit-t-il</link><guid isPermaLink="true">https://angulardev.fr/rxjs-interop-de-quoi-sagit-t-il</guid><category><![CDATA[Angular]]></category><category><![CDATA[signals]]></category><category><![CDATA[RxJS]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Thu, 06 Jun 2024 08:25:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/8qEB0fTe9Vw/upload/7dfc8a03e3e7763bdfd1e598a37de4ba.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Lorsque la core team de Angular a annoncé l'arrivée du concept de <code>Signals</code>, plusieurs interrogations se sont soulevées quant à l'utilisation de la bibliothèque <code>RxJS</code> qui a été jusque-là un atout très important pour le framework Angular.</p>
<blockquote>
<p>Est-ce que <code>Angular Signals</code> remplacera <code>RxJS</code> ?<br />Est-ce que <code>Angular Signals</code> et <code>RxJS</code> sont complémentaires ?<br />Quel sera l'avenir de <code>RxJS</code> dans Angular ?</p>
</blockquote>
<p>Cet article vise à mettre la lumière sur ces interrogations ainsi que sur l'utilisation du package <code>RxJS Interop</code> que la core team de Angular a mis à disposition des développeurs.</p>
<blockquote>
<p>Est-ce que <code>Angular Signals</code> remplacera <code>RxJS</code> ?</p>
</blockquote>
<p><code>Angular Signals</code> n'a pas pour but de remplacer <code>RxJS</code>. Le concept de <code>Signals</code> a été introduit afin d'optimiser le système de détection des changements dans nos composants Angular lorsque nous mettons à jour une variable ou un objet. Ce système a été conçu autour la bibliothèque <code>Zone.js</code> qui a démontré ces limites que j'ai eues à évoquer précédemment dans cet article <a target="_blank" href="https://angulardev.fr/les-nouveautes-en-angular-16-partie-1">Les nouveautés en Angular 16 (Partie 1)</a><br />Nous comprenons donc que <code>Zone.js</code> n'a rien avoir techniquement avec <code>RxJS</code>. Ce sont deux bibliothèques bien distinctes qui ont des buts différents.</p>
<blockquote>
<p>Est-ce que <code>Angular Signals</code> et <code>RxJS</code> sont complémentaires ?</p>
</blockquote>
<p>Maintenant que nous savons que <code>Angular Signals</code> n'est pas là pour remplacer <code>RxJS</code>, nous aborderons leur complémentarité.</p>
<p>Après avoir suivi plusieurs discussions entre les membres de la core team et ceux de la communauté Angular, <strong><em>je peux affirmer que oui</em></strong> 😃</p>
<p>En effet, les <code>Signals</code><strong><em>permettent une gestion d'état synchrone</em></strong> tandis que la bibliothèque <code>RxJS</code> le fait de <strong><em>manière asynchrone</em></strong>. La décision de choisir entre l'utilisation de <code>RxJS</code> ou celle de <code>Signals</code> dépendra donc de la complexité de nos opérations asynchrones et de l'environnement de développement existant. Pour des scénarios asynchrones complexes et des architectures basées sur des événements, <code>RxJS</code> offre des outils puissants. En revanche, pour des cas simples d'événements typiquement synchrone ou lorsqu'une meilleure intégration avec les fonctionnalités spécifiques d'Angular est souhaitée, les <code>Signals</code> peuvent être plus appropriés.</p>
<blockquote>
<p>Quel sera l'avenir de <code>RxJS</code> dans Angular ?</p>
</blockquote>
<p>Après être penché sur les deux premières interrogations qui constituaient des points d'ombre, il est légitime de se demander ce que nous réserve la core team Angular par rapport à l'utilisation de <code>RxJS</code> dans le futur.</p>
<p>L'objectif de la core team de Angular est de rendre optionnel l'installation de bibliothèque <code>RxJS</code> dans un projet Angular c'est-à-dire qu'à la création d'un projet Angular, la bibliothèque <code>RxJS</code> ne sera plus une dépendance installée par défaut. Toutefois, la core team est bien consciente de la puissance des fonctionnalités de <code>RxJS</code>. Pour faciliter la transition, ils ont mis en place le package <code>RxJS Interop</code>.</p>
<p><code>RxJS Interop</code><strong><em>d'Angular fournit des utilitaires utiles pour intégrer</em></strong> <code>Angular Signals</code> <strong><em>avec les observables</em></strong> <code>RxJS</code><strong><em>.</em></strong> Voici quelques utilitaires ou helpers (en anglais) que nous offre cette nouvelle bibliothèque :</p>
<ul>
<li><code>toSignal</code></li>
</ul>
<p>La fonction <code>toSignal</code> permet de créer un signal qui écoute en permanence la valeur d'un <code>Observable</code>. Son comportement est similaire à celui du pipe <code>async</code> que nous utilisons dans nos templates HTML, mais il est plus flexible et peut être utilisé n'importe où dans une application. Voyons ensemble ce cas d'utilisation :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Component } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
<span class="hljs-keyword">import</span> { AsyncPipe } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/common'</span>;
<span class="hljs-keyword">import</span> { interval } <span class="hljs-keyword">from</span> <span class="hljs-string">'rxjs'</span>;
<span class="hljs-meta">@Component</span>({
  template: <span class="hljs-string">`{{ counter() }}`</span>,
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> Ticker {
  counterObservable = interval(<span class="hljs-number">1000</span>);
  <span class="hljs-comment">// Get a `Signal` representing the `counterObservable`'s value.</span>
  counter = toSignal(<span class="hljs-built_in">this</span>.counterObservable, {initialValue: <span class="hljs-number">0</span>});
}
</code></pre>
<p>Comme le pipe <code>async</code>, <code>toSignal</code> s'abonne immédiatement à l'<code>Observable</code>, ce qui peut entraîner des effets de bord. La souscription créée par <code>toSignal</code> se désabonne automatiquement de l'<code>Observable</code> donné lorsque le composant ou le service qui appelle <code>toSignal</code> est détruit. Ainsi, <strong><em>nous pouvons transformer un comportement asynchrone en synchrone en utilisant pleinement le potentiel des Signals.</em></strong></p>
<blockquote>
<p><em>Cependant,</em><code>toSignal</code><em>crée une souscription. Il faut donc éviter de l'appeler plusieurs fois pour le même</em><code>Observable</code><em>, et réutiliser le signal qu'il renvoie.</em></p>
</blockquote>
<ul>
<li><code>toObservable</code></li>
</ul>
<p>A l'opposé de l'utilitaire <code>toSignal</code>, utilisez <code>toObservable</code> pour créer un <code>Observable</code> qui écoute en permanence la valeur d'un signal. La valeur du signal est surveillée par un <code>effect</code> qui transmet la valeur à l'<code>Observable</code> lorsqu'elle change. Voici un cas d'utilisation :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Component, signal } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
<span class="hljs-meta">@Component</span>(...)
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> SearchResults {
  query: Signal&lt;<span class="hljs-built_in">string</span>&gt; = inject(QueryService).query;
  query$ = toObservable(<span class="hljs-built_in">this</span>.query);
  results$ = <span class="hljs-built_in">this</span>.query$.pipe(
    switchMap(<span class="hljs-function"><span class="hljs-params">query</span> =&gt;</span> <span class="hljs-built_in">this</span>.http.get(<span class="hljs-string">'/search?q='</span> + query ))
  );
}
</code></pre>
<p>Lorsque le signal <code>query</code> change, l'observable <code>query$</code> émet la dernière requête et déclenche une nouvelle demande HTTP. <em>Dans l'exemple, on remarque l'utilisation de</em><code>switchMap</code><em>qui est un opérateur</em><code>RxJS</code><em>ce qui est normal car comme nous l'avons précédemment annoncé, pour les tâches asynchronous,</em><code>RxJS</code><em>demeure très efficace.</em></p>
<p>Il est à noter qu'il reste deux autres utilitaires à savoir <code>outputFromObservable</code> et <code>outputToObservable</code> que nous verrons plus tard dans un nouvel article.</p>
<p>En somme, comme nous l'avons remarqué, pour comprendre l'utilité de <code>RxJS Interop</code>, il faut d'abord comprendre ce à quoi est destiné d'une part <code>Angular Signals</code> et d'autre part <code>RxJS</code>. Aujourd'hui, au travers de cet article, nous avons les idées claires de ce qu'il en sera du future de <code>RxJS</code> avec l'arrivée de <code>Angular Signals</code>.</p>
<blockquote>
<p>Pour plus d'informations 👉 <a target="_blank" href="https://angular.dev/guide/signals/rxjs-interop">RxJS Interop</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Angular Signals : que faut t-il retenir quand je débute ?]]></title><description><![CDATA[Depuis la sortie de Angular 16, une nouvelle fonctionnalité a été introduite. Il s'agit du concept de Signals. Cette fonctionnalité est à présent stable et constitue un atout majeur dans la conception des applications avec Angular. Dans cet article, ...]]></description><link>https://angulardev.fr/angular-signals-que-faut-t-il-retenir-quand-je-debute</link><guid isPermaLink="true">https://angulardev.fr/angular-signals-que-faut-t-il-retenir-quand-je-debute</guid><category><![CDATA[Angular]]></category><category><![CDATA[signals]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Tue, 04 Jun 2024 07:48:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/xXiKQ2AavlY/upload/5a804b41bf61bd15a204ad91ee04b5f0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Depuis la sortie de Angular 16, une nouvelle fonctionnalité a été introduite. Il s'agit du concept de <code>Signals</code>. Cette fonctionnalité est à présent stable et constitue un atout majeur dans la conception des applications avec Angular. Dans cet article, nous verrons ensemble ce dont il est question quand on parle de <code>Signals</code> en Angular et ce qu'il faut en retenir.</p>
<h3 id="heading-quest-ce-quun-signal-en-angular">Qu'est-ce qu'un <code>signal</code> en Angular ?</h3>
<p>Avant d'entrer dans le vif du sujet, faisons un peu d'histoire. Dans ce précédent article <a target="_blank" href="https://angulardev.fr/les-nouveautes-en-angular-16-partie-1">Les nouveautés en Angular 16 (Partie 1)</a>, j'ai parlé de <code>Signal</code> et de pourquoi il est innovant. Je vous conseille de le lire pour mieux comprendre la suite.</p>
<p>En nous basant sur la documentation de Angular, voici comment il a été défini :</p>
<blockquote>
<p><code>Angular Signals</code> est un système qui suit de manière granulaire comment et où votre état est utilisé dans une application, permettant au framework d'optimiser les mises à jour de rendu.</p>
</blockquote>
<p>Ce qui captive l'attention dans cette définition c'est l'expression <strong><em>suivre de manière granulaire</em></strong>. Cela signifie tout simplement que le système mis en place écoute en permance point à point les changements des états des variables utilisées dans votre application qui sont basées sur le concept de <code>Signals</code>. Ainsi, le temps de réponse lié au rendu de ces variables ou objets dans le <code>DOM</code> est considérablement réduit ce qui rend notre application plus performante.</p>
<p>En allant plus loin dans la définition, nous apprenons que les <code>Signals</code> peuvent contenir n'importe quelle valeur c'est-à-dire des primitives ou des données complexes. Quand on parle de primitives, il s'agit des types de base à savoir string, number, etc...</p>
<p>Pour finir, il y a un élément crucial à retenir : <strong><em>les</em></strong> <code>Signals</code> <strong><em>peuvent être en écriture ou en lecture seule.</em></strong></p>
<h3 id="heading-comment-creer-un-signal-et-mettre-a-jour-sa-valeur">Comment créer un signal et mettre à jour sa valeur ?</h3>
<p>Dans cette section, nous verrons comment utiliser un <code>signal</code> en mode écriture c'est-à-dire créer un <code>signal</code> et modifier sa valeur :</p>
<ul>
<li>Création d'un <code>signal</code></li>
</ul>
<p>Pour créer un <code>signal</code>, voici comment procéder :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> count = signal(<span class="hljs-number">0</span>);
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'The count is: '</span> + count());
</code></pre>
<p>Comme nous le remarquons sur le code ci-dessus, la création d'un <code>signal</code> se fait au travers de la méthode <code>signal()</code> avec une valeur d'initialisation. Ici, cette valeur c'est <code>0</code>.<br />En outre, pour lire la valeur du <code>signal</code> créé qui est count dans l'exemple ci-dessus, nous avons utilisé une syntaxe particulière à savoir <code>count()</code>. Il s'agit tout simplement d'une syntaxe simplifiée lorsque l'objet manipulée possède une méthode <code>get()</code> qui nous retourne la valeur de la variable ou de l'objet. On parle de <strong><em>getter function</em></strong> (en anglais).</p>
<ul>
<li>Modification d'un <code>signal</code></li>
</ul>
<p>Tout comme la création d'un <code>signal</code>, la modification de sa valeur est aussi simple. Cependant, nous avons deux façons de la faire :</p>
<pre><code class="lang-typescript">count.set(count() + <span class="hljs-number">1</span>);

<span class="hljs-comment">// Ou</span>

count.update(<span class="hljs-function"><span class="hljs-params">value</span> =&gt;</span> value + <span class="hljs-number">1</span>);
</code></pre>
<p>Dans le premier cas, nous avons utilisé la méthode <code>set()</code> ce qui n'est pas surprenant vu que nous avons une méthode <code>get()</code>.</p>
<blockquote>
<p>Vous l'aurez compris certainement !<br />set() pour modifier la valeur<br />get() pour récupérer la valeur<br />🤓</p>
</blockquote>
<p>Dans le second cas, nous sommes passé par la méthode <code>update()</code>. L'avantage de cette méthode réside dans le fait que nous avons directement l'ancienne valeur de notre signal sur laquelle nous pourrons procéder à des traitements pour la modifier.</p>
<p>A présent, il est crucial de comprendre que lorsque nous décidons de créer un <code>signal</code> qui peut être modifié c'est-à-dire accessible en écriture, Angular le définit comme un <code>WritableSignal</code>.</p>
<blockquote>
<p><em>Bien-sûr vu que vous êtes super doués…on peut déjà se douter qu'il y a des</em> <code>signals</code> <em>qui ne sont pas writable 😂</em></p>
</blockquote>
<p>Pour finir, voici ce que donnerait la création de notre <code>signal</code> avec la déclaration explicite du type :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> count: WritableSignal&lt;<span class="hljs-built_in">number</span>&gt; = signal(<span class="hljs-number">0</span>);
</code></pre>
<p>Comment créer un <code>signal</code> en lecture seule (<em>read only</em>) ?</p>
<p>Dans le jargon technique de Angular, les signals en lecture seule sont appelés <code>Computed signal</code><strong><em>.</em></strong></p>
<p>Pour créer un <code>signal</code> en lecture seule, voici comment procéder :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> count: WritableSignal&lt;<span class="hljs-built_in">number</span>&gt; = signal(<span class="hljs-number">0</span>);
<span class="hljs-keyword">const</span> doubleCount: Signal&lt;<span class="hljs-built_in">number</span>&gt; = computed(<span class="hljs-function">() =&gt;</span> count() * <span class="hljs-number">2</span>);
</code></pre>
<p>Comme nous pouvons le lire dans le code ci-dessus, le type de notre signal <code>doubleCount</code> est <code>Signal</code> qui est l'opposé de <code>WritableSignal</code>. Nous nous sommes basés sur l'exemple précédent afin de faire remarquer les différences.</p>
<p>Le signal <code>doubleCount</code> est en lecture seule mais il dépend du signal <code>count</code> qui est en écriture. Si le signal <code>count</code> se met à jour, automatiquement le signal <code>doubleCount</code> le sera.</p>
<p>Pour les plus curieux qui cherchent à comprendre comment s'effectue la lecture seule, voici ce que la documentation de Angular nous enseigne :</p>
<blockquote>
<p>Les signals calculés sont à la fois évalués paresseusement et mémoïsés.</p>
<p>La fonction de dérivation de doubleCount ne s'exécute pas pour calculer sa valeur avant la première lecture de doubleCount. La valeur calculée est alors mise en cache, et si vous lisez doubleCount à nouveau, il retournera la valeur mise en cache sans recalculer.</p>
<p>Si vous changez ensuite de compte, Angular sait que la valeur mise en cache de doubleCount n'est plus valide, et la prochaine fois que vous lirez doubleCount, sa nouvelle valeur sera calculée.</p>
<p>Par conséquent, vous pouvez effectuer en toute sécurité des dérivations coûteuses en calcul dans les signaux calculés, comme le filtrage des tableaux.</p>
</blockquote>
<p>Dans cette explication, ce qui retient l'attention c'est l'expression <strong><em>mémoïsation.</em> La mémoïsation est une expression en jargon informatique qui signifie tout simplement la mise en cache de la valeur d'une fonction.</strong></p>
<p>Pour finir, on retient de cette section que nous avons deux types de <code>Signals</code> à savoir <code>WritableSignal</code> et <code>Signal</code>.<br /><code>WritableSignal</code> peut être modifié tandis que <code>Signal</code> est en lecture seule.</p>
<blockquote>
<p>Dans la prochaine section, nous parlerons d'une fonctionnalité importante des <code>Signals</code> : les <code>effects</code>.</p>
</blockquote>
<h3 id="heading-les-effects">Les effects</h3>
<p>Supposons un instant que nous aimerions faire des traitements lorsque notre signal change de valeur. Jusqu'à présent, nous n'avons aucun moyen de le faire. Comme vous l'auriez compris, les <code>effects</code> nous servirons à les faire.</p>
<p>Un <code>effect</code> est donc une méthode qui se déclenche chaque fois qu'une ou plusieurs valeurs de <code>signals</code> changent. Pour metter en place un <code>effect</code>, voici ce qu'il faut faire :</p>
<pre><code class="lang-typescript">effect(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`The current count is: <span class="hljs-subst">${count()}</span>`</span>);
});
</code></pre>
<p>Dans le code ci-dessus, nous avons notre <code>effect</code> avec une fonction de callback. La fonction de callback nous permet d'effectuer nos traitements suivant la modification de notre <code>signal</code>.</p>
<p>Les <code>effects</code> sont toujours exécutés au moins une fois. Lorsqu'un <code>effect</code> s'exécute, il suit toutes les valeurs de <code>signals</code> lues. Chaque fois que l'une de ces valeurs change, l'<code>effect</code> s'exécute à nouveau. Comme pour les <code>signals</code> calculés, les <code>effects</code> suivent leurs dépendances de manière dynamique et ne suivent que les <code>signals</code> qui ont été lus lors de l'exécution la plus récente.</p>
<p>Les <code>effects</code> s'exécutent toujours de manière asynchrone, pendant le processus de détection des changements.</p>
<p>En nous rapportant toujours à la documentation de <code>Angular</code>, voici quelques cas d'utilisation des <code>effects</code> :</p>
<ul>
<li><p><strong><em>Enregistrement des données affichées et de leurs modifications, à des fins d'analyse ou comme outil de débogage.</em></strong></p>
</li>
<li><p><strong><em>Garder les données synchronisées avec window.localStorage.</em></strong></p>
</li>
<li><p><strong><em>Ajouter un comportement DOM personnalisé qui ne peut pas être exprimé avec la syntaxe des modèles.</em></strong></p>
</li>
<li><p><strong><em>Rendre le comportement personnalisé à un , à une bibliothèque graphique ou à une autre bibliothèque d'interface utilisateur tierce.</em></strong></p>
</li>
</ul>
<p>En somme, nous avons vu ensemble l'essentiel de <code>Angular Signals</code> ainsi que la philosophie derrière cette approche. Nous avons compris à présent la pertinence de <code>Signals</code> en <code>Angular</code> et comment l'utiliser de façon très subtile.</p>
<p>Nous aborderons dans de prochains articles, tout l'écosystème autour de ce concept qui a révolutionné le fonctionnement du framework Angular depuis la version 16.</p>
<blockquote>
<p>Pour plus d'informations 👉 <a target="_blank" href="https://angular.dev/guide/signals">Angular Signals</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Comment créer facilement un composant input réutilisable piloté par l'interface ControlValueAccessor ?]]></title><description><![CDATA[L'une des problématiques majeures en Angular est de créer des composants réutilisables ce qui constitue l'essence même du framework. Souvent, nous sommes amenés à créer des composants input pour les utiliser un peu partout dans nos projets suivant le...]]></description><link>https://angulardev.fr/comment-creer-facilement-un-composant-input-reutilisable-pilote-par-linterface-controlvalueaccessor</link><guid isPermaLink="true">https://angulardev.fr/comment-creer-facilement-un-composant-input-reutilisable-pilote-par-linterface-controlvalueaccessor</guid><category><![CDATA[controlvalueaccessor]]></category><category><![CDATA[Angular]]></category><category><![CDATA[reactive forms]]></category><category><![CDATA[template-driven-forms]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Wed, 20 Dec 2023 10:08:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/duiE-To8cFc/upload/bc763d44441f4aa628299ae91d826d9f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>L'une des problématiques majeures en Angular est de créer des composants réutilisables ce qui constitue l'essence même du framework. Souvent, nous sommes amenés à créer des composants <code>input</code> pour les utiliser un peu partout dans nos projets suivant le besoin. Ces composants <code>input</code> doivent être fonctionnels lorsque nous mettons en place un formulaire piloté par un Template-Driven Form ou un Reactive Form.</p>
<p>Le but de cet article est de mettre la lumière sur comment créer facilement un composant <code>input</code> réutilisable que nous pourrions utiliser dans nos formulaires réactifs.</p>
<p><strong>Etape 1 : Création du composant</strong> <code>app-input</code></p>
<p>Commençons par générer un nouveau composant avec la commande Angular CLI :</p>
<pre><code class="lang-bash">  ng generate component input
</code></pre>
<p>Nous modifierons le template de notre composant pour avoir ceci de façon très basique :</p>
<pre><code class="lang-xml">  <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> [<span class="hljs-attr">formControl</span>]=<span class="hljs-string">"control"</span>/&gt;</span>
</code></pre>
<p><strong>Etape 2: Implémentation de</strong> <code>ControlValueAccessor</code></p>
<p>Pour que notre composant puisse être utilisé dans un formulaire réactif ou un formulaire basé sur des modèles, nous devons implémenter l'interface <code>ControlValueAccessor</code>.</p>
<p>Nous avons besoin de l'interface <code>ControlValueAccessor</code> parce que lorsqu'on crée le composant et qu'on désire l'utiliser dans un composant parent, nous ne devons pas oublier qu'il sera encapsulé dans son sélecteur <code>app-input</code> (dans notre cas ici). Ainsi, tous les attributs auxquels l'on avait accès directement sur un <code>input</code> ne seront plus disponibles. C'est davantage plus difficile si l'on veut avoir le contrôle sur notre composant via un formulaire réactif ou un formulaire basé sur des modèles.</p>
<p>Cette interface fournit une série de méthodes que nous pouvons utiliser pour synchroniser la valeur de notre composant avec un FormControl.</p>
<p>Nous pouvons le faire en ajoutant les méthodes suivantes à notre composant :</p>
<pre><code class="lang-typescript">onChange: <span class="hljs-built_in">any</span> = <span class="hljs-function">() =&gt;</span> {};

onTouched = <span class="hljs-function">() =&gt;</span> {};

writeValue(value: <span class="hljs-built_in">any</span>): <span class="hljs-built_in">void</span> {
    <span class="hljs-comment">// mettre à jour la valeur de votre composant</span>
}

registerOnChange(fn: <span class="hljs-built_in">any</span>): <span class="hljs-built_in">void</span> {
 <span class="hljs-comment">// enregistrer une fonction qui sera appelée lorsque la valeur du composant change</span>
 <span class="hljs-built_in">this</span>.onChange = fn;
}

registerOnTouched(fn: <span class="hljs-built_in">any</span>): <span class="hljs-built_in">void</span> {
 <span class="hljs-comment">// enregistrer une fonction qui sera appelée lorsque le composant est touché</span>
 <span class="hljs-built_in">this</span>.onTouched = fn;
}
</code></pre>
<p>Lorsqu'on implémente l'interface <code>ControlValueAccessor</code>, ces trois méthodes sont automatiquement redéfinies dans notre composant. Ces méthodes permettront à notre composant d'écouter les changements et de les enregistrer suivant le besoin.</p>
<p>Il faut aussi penser à rendre accessible l'objet <code>AbstractControl</code> afin de pouvoir effectuer des manipulations dans le template <code>HTML</code> de notre composant pour soit afficher des messages d'erreur ou gérer des scénarios. Voici ce qu'il faut faire :</p>
<pre><code class="lang-typescript">...

<span class="hljs-keyword">public</span> control: AbstractControl;

...

<span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> injector: Injector</span>) {}

ngAfterViewInit(): <span class="hljs-built_in">void</span> {
    <span class="hljs-keyword">const</span> ngControl = <span class="hljs-built_in">this</span>.injector.get(NgControl, <span class="hljs-literal">null</span>);
    <span class="hljs-keyword">if</span> (ngControl) {
      <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-built_in">this</span>.control = <span class="hljs-built_in">Object</span>.assign(<span class="hljs-keyword">new</span> FormControl(), ngControl.control) <span class="hljs-keyword">as</span> FormControl;
      });
    }
  }
</code></pre>
<p>Cette initialisation dans la méthode <code>ngAfterViewInit</code> nous permet d'accéder facilement à l'objet <code>AbstractControl</code> de notre composant <code>input</code> et de prévenir un problème lié à l'appel de notre composant dans un composant parent. En effet, le <code>setTimeout</code> est utilisé pour s'assurer que le <code>DOM</code> a été bien initialisé par le parent pour permettre l'utilisation de notre composant enfant.</p>
<p>Nous devons également fournir un provider pour notre composant qui indique à Angular d'utiliser notre composant comme un <code>ControlValueAccessor</code> :</p>
<pre><code class="lang-typescript">  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(<span class="hljs-function">() =&gt;</span> InputComponent),
      multi: <span class="hljs-literal">true</span>
    }
  ]
</code></pre>
<p><strong>Etape 3 : Utilisation du composant dans un formulaire</strong></p>
<p>Maintenant que notre composant est un <code>ControlValueAccessor</code>, nous pouvons l'utiliser dans un formulaire réactif. Voici un exemple :</p>
<pre><code class="lang-xml">  <span class="hljs-tag">&lt;<span class="hljs-name">form</span> [<span class="hljs-attr">formGroup</span>]=<span class="hljs-string">"form"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">app-input</span> <span class="hljs-attr">formControlName</span>=<span class="hljs-string">"firstname"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">app-input</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<p>Nous pouvons obtenir la valeur du composant en utilisant <code>form.get('firstname').value</code> et nous pouvons définir la valeur du composant en utilisant <code>form.get('firstname').setValue(value)</code>.</p>
<blockquote>
<p>Est-ce que ça fonctionne aussi avec <code>[(ngModel)]</code> ?</p>
</blockquote>
<p>La question de savoir si <code>ngModel</code> est pris en compte par le <code>ControlValueAccessor</code> est très récurrente !</p>
<p>La réponse est <code>OUI</code>. Nous pouvons utiliser <code>ngModel</code> sur un composant piloté par <code>ControlValueAccessor</code> et ça fonctionne parfaitement :</p>
<pre><code class="lang-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">app-input</span> [(<span class="hljs-attr">ngModel</span>)]=<span class="hljs-string">"value"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">app-input</span>&gt;</span>
</code></pre>
<p>En somme, lorsque nous décidons de créer des composants réutilisables de type <code>input</code>, pensons toujours à implémenter l'interface <code>ControlValueAccessor</code> pour avoir cette flexibilité à pouvoir l'utiliser dans un formulaire réactif (Reactive Form) ou un formulaire basé sur des modèles (Template-Driven Form).</p>
<blockquote>
<p>Pour plus d'informations :</p>
<p>👉 <a target="_blank" href="https://angular.dev/api/forms/ControlValueAccessor">ControlValueAccessor</a></p>
<p>👉 <a target="_blank" href="https://angular.dev/guide/forms/reactive-forms">Reactive Forms</a></p>
<p>👉 <a target="_blank" href="https://angular.dev/guide/forms/template-driven-forms">Template-Driven Forms</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Introduction au Test-Driven Development en Angular]]></title><description><![CDATA[Le développement piloté par les tests (Test-Driven Development en anglais) est une approche agile du développement de logiciels qui met l'accent sur l'écriture itérative de tests et du code métier. Le processus TDD comprend trois (03) étapes :

écrir...]]></description><link>https://angulardev.fr/introduction-au-test-driven-development-en-angular</link><guid isPermaLink="true">https://angulardev.fr/introduction-au-test-driven-development-en-angular</guid><category><![CDATA[Angular]]></category><category><![CDATA[TDD (Test-driven development)]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Mon, 04 Dec 2023 08:31:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/h2aDKwigQeA/upload/005a2c463dd60be9c5be50ed4993508d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Le développement piloté par les tests (Test-Driven Development en anglais) est <strong><em>une approche agile du développement de logiciels qui met l'accent sur l'écriture itérative de tests et du code métier</em></strong>. Le processus TDD comprend trois (03) étapes :</p>
<ul>
<li><p><strong>écrire un test qui échoue avant d'écrire le code ;</strong></p>
</li>
<li><p><strong>interdire d'écrire un test plus compliqué que nécessaire ;</strong></p>
</li>
<li><p><strong>éviter d'écrire plus de code que nécessaire, mais seulement ce qu'il faut pour réussir le test qui a échoué.</strong></p>
</li>
</ul>
<blockquote>
<p><strong>Ces trois (03) règles fonctionnent ensemble dans le cadre d'un processus appelé le cycle rouge-vert-refactorisation (red-green-refactor en anglais).</strong></p>
<p>Le processus TDD est itératif et implique l'écriture d'un test qui échoue, l'écriture du code pour réussir le test, puis le refactorisation du code pour améliorer sa conception et sa maintenabilité. Le processus est répété jusqu'à ce que le code soit complet. En écrivant d'abord les tests, la méthode TDD garantit que le code est correct et répond aux exigences définies dans les tests. Elle encourage également les développeurs à écrire un code propre, facile à maintenir, à modifier et à faire évoluer.</p>
</blockquote>
<p>Dans cet article, nous allons nous concentrer sur l'introduction au TDD en utilisant Angular, un framework JavaScript populaire pour le développement d'applications web. Nous allons créer une application de calculatrice simple pour illustrer les concepts du TDD.</p>
<p><strong>Étape 1: Création du projet Angular</strong></p>
<p>Avant de commencer à développer notre application de calculatrice, nous devons naturellement créer notre projet en utilisant la commande suivante :</p>
<pre><code class="lang-typescript">ng <span class="hljs-keyword">new</span> calculator-app
</code></pre>
<p><strong>Étape 2: Création du composant de calculatrice</strong></p>
<p>Maintenant que notre projet est créé, nous pouvons commencer à développer notre application de calculatrice. Créez un nouveau composant de calculatrice en utilisant la commande suivante:</p>
<pre><code class="lang-typescript">ng generate component calculator
</code></pre>
<p>Cette commande créera un nouveau dossier <code>calculator</code> contenant les fichiers nécessaires pour notre composant de calculatrice. Nous travaillerons dans un premier temps dans le fichier <code>calculator.component.spec.ts</code> .</p>
<p><strong>Étape 3: Exploration du fichier</strong> <code>calculator.component.spec.ts</code></p>
<p>Lorsque vous ouvrez le fichier, voici le code que vous avez par défaut :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { ComponentFixture, TestBed } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core/testing'</span>;
<span class="hljs-keyword">import</span> { CalculatorComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">'./calculator.component'</span>;

describe(<span class="hljs-string">'CalculatorComponent'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">let</span> calculator: CalculatorComponent; <span class="hljs-comment">// component a été remplacé par calculator</span>
  <span class="hljs-keyword">let</span> fixture: ComponentFixture&lt;CalculatorComponent&gt;;

  beforeEach(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">await</span> TestBed.configureTestingModule({
      declarations: [ CalculatorComponent ]
    })
    .compileComponents();

    fixture = TestBed.createComponent(CalculatorComponent);
    calculator = fixture.componentInstance;
    fixture.detectChanges();
  });

  it(<span class="hljs-string">'should create'</span>, <span class="hljs-function">() =&gt;</span> {
    expect(calculator).toBeTruthy();
  });
});
</code></pre>
<p>Dans le code généré ci-dessous, nous avons une suite de tests utilisant la fonction <code>describe</code>, qui fournit un nom descriptif pour le composant testé. Dans la suite de tests, nous avons un bloc <code>beforeEach</code> pour configurer l'environnement de test. La méthode <code>TestBed.configureTestingModule</code> est utilisée pour configurer le module de test et fournir les dépendances nécessaires. La variable <code>calculator</code> est ensuite assignée à une instance du <code>CalculatorComponent</code> à l'aide de la méthode <code>TestBed.inject</code>.</p>
<p><strong>Etape 4: Ecriture de notre premier test</strong></p>
<p>Notre composant <code>CalculatorComponent</code> nous permettra d'effectuer des opérations arithmétiques de base. Pour écrire un test unitaire en utilisant le TDD, nous allons commencer par créer un scénario de test qui vérifie le comportement attendu du composant. Nous allons maintenant écrire le scénario de test proprement dit en utilisant la fonction <code>it</code>. Dans ce cas, nous allons tester la méthode <code>add</code> du <code>CalculatorComponent</code> en lui passant deux nombres et en nous attendant à ce que le résultat soit <code>5</code>. La fonction <code>expect</code> est utilisée pour définir le comportement attendu et vérifier le résultat réel. Ce code doit être ajouté à la suite de tests, c'est-à-dire à l'intérieur de la fonction <code>describe</code>.</p>
<pre><code class="lang-typescript">  it(<span class="hljs-string">'should add two numbers correctly'</span>, <span class="hljs-function">() =&gt;</span> { 
    <span class="hljs-keyword">const</span> result = calculator.add(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>); 
    expect(result).toBe(<span class="hljs-number">5</span>); 
 });
</code></pre>
<p><strong>Etape 4: Exécution du test</strong></p>
<blockquote>
<p>Avant même d'exécuter le test, vous obtenez une erreur dans votre éditeur de code vous indiquant que la fonction add n'existe pas.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690172515001/9a2e57dd-71ba-46bd-b7ca-b57e1a95f670.png" alt class="image--center mx-auto" /></p>
<p>C'est normal, puisqu'elle n'a pas encore été créée.</p>
<p><em>Ensuite, en retournant sur notre serveur</em> <code>Karma, nous constatons que notre test case n'est pas affiché dans</code> <em>CalculatorComponent` et dans le terminal, nous avons une erreur liée à l'inexistence de la fonction et un message indiquant qu'aucun test n'a réussi.</em></p>
<p><em>Pas de panique, c'est le rouge du TDD ! Bravo 😂</em></p>
<p><strong>Etape 5: Ecriture du code minimum pour que le test passe</strong></p>
<p>Dans notre classe CalculatorComponent, en suivant l'approche TDD, nous allons écrire le minimum de code nécessaire pour que notre test précédemment écrit passe.</p>
<pre><code class="lang-typescript">add(a: <span class="hljs-built_in">number</span>, b: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">number</span> { 
    <span class="hljs-keyword">return</span> a + b; 
}
</code></pre>
<p>Le résultat sur votre serveur Karma :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690172890907/027939bc-76f2-4d36-a09d-00b68c22cc75.png" alt class="image--center mx-auto" /></p>
<p>Et dans votre terminal :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690172943382/4e626171-4510-47d8-8247-39e446becf68.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>Nous en sommes à l'étape <code>green</code> du TDD, c'est-à-dire que nous écrivons la quantité minimale de code nécessaire pour que notre test soit réussi.</p>
<p>C'est très bien !</p>
</blockquote>
<p><strong>Etape 6: Refactoriser le code</strong></p>
<p>Une fois les tests réussis, vous pouvez refactoriser le code pour en améliorer la conception, la lisibilité et la maintenabilité. La refactorisation est une étape essentielle du processus TDD, qui permet d'éliminer les doublons, d'améliorer la structure du code et la qualité globale. Il est essentiel de s'assurer que les tests continuent à fonctionner après la refactorisation. La révision et la mise à jour régulière des tests au fur et à mesure de l'évolution de la base de code contribueront à maintenir l'intégrité et la fiabilité des tests unitaires.</p>
<blockquote>
<p><strong>Maintenant, faites le même exercice pour rajouter les autres types d'opérations qu'on retrouve sur une calculatrice. Vous remarquerez que vous passez plus de temps dans votre éditeur de texte ou IDE que sur le navigateur et dans votre terminal. À aucun moment, vous n'avez eu besoin d'ouvrir votre navigateur sauf pour visualiser quoique ce soit à part le résultat du serveur Karma.</strong></p>
<p>Pour plus d'informations :</p>
<p>👉 <a target="_blank" href="https://jasmine.github.io/">Jasmine</a><br />👉 <a target="_blank" href="https://karma-runner.github.io/latest/index.html">Karma</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Comment mettre en place une stratégie de chargement d'une page web Angular en se basant sur le débit de la connexion internet de l'utilisateur ?]]></title><description><![CDATA[Lorsque nous développons une application web, il y a souvent une question qui nous vient à l'esprit mais que nous négligeons généralement. La question est la suivante :

Comment notre application réagirait avec une connexion internet dont le débit es...]]></description><link>https://angulardev.fr/comment-mettre-en-place-une-strategie-de-chargement-dune-page-web-angular-en-se-basant-sur-le-debit-de-la-connexion-internet-de-lutilisateur</link><guid isPermaLink="true">https://angulardev.fr/comment-mettre-en-place-une-strategie-de-chargement-dune-page-web-angular-en-se-basant-sur-le-debit-de-la-connexion-internet-de-lutilisateur</guid><category><![CDATA[Angular]]></category><category><![CDATA[lazy loading]]></category><category><![CDATA[preloading]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Mon, 26 Jun 2023 09:23:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/HOrhCnQsxnQ/upload/3f13a221c0522221c90b45baf86024c6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Lorsque nous développons une application web, il y a souvent une question qui nous vient à l'esprit mais que nous négligeons généralement. La question est la suivante :</p>
<blockquote>
<p>Comment notre application réagirait avec une connexion internet dont le débit est faible ?</p>
<p>La plupart d'entre nous ne se pose la question qu'à la fin du projet 😂</p>
</blockquote>
<p>Dans cet article, nous verrons comment Angular nous permet de contrôler et de gérer le chargement de nos bundles <strong><em>(le code JS qui s'occupe de l'affichage de notre page web)</em></strong> en se basant sur le débit de la connexion internet.</p>
<blockquote>
<p>Nous prendrons l'exemple d'une petite application Angular ayant trois composants à savoir <code>HomeComponent</code>, <code>AboutComponent</code> et <code>ContactComponent</code> qui seront chargés de façon paresseuse (<code>lazy</code>).</p>
</blockquote>
<p><strong>1- Création d'un nouveau projet Angular à l'aide de la CLI Angular :</strong></p>
<pre><code class="lang-plaintext">ng new testing-internet-connection --routing
</code></pre>
<p><strong>2- Création de trois composants Angular implémentant chacun le routing :</strong></p>
<pre><code class="lang-plaintext">ng g m home --route home --module=app.module
ng g m about --route about --module=app.module
ng g m contact --route contact --module=app.module
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Component } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;

<span class="hljs-meta">@Component</span>({
  selector: <span class="hljs-string">'app-home'</span>,
  template: <span class="hljs-string">`&lt;h1&gt;Home&lt;/h1&gt;`</span>
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> HomeComponent { }
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Component } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;

<span class="hljs-meta">@Component</span>({
  selector: <span class="hljs-string">'app-about'</span>,
  template: <span class="hljs-string">`&lt;h1&gt;About&lt;/h1&gt;`</span>
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> AboutComponent { }
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Component } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;

<span class="hljs-meta">@Component</span>({
  selector: <span class="hljs-string">'app-contact'</span>,
  template: <span class="hljs-string">`&lt;h1&gt;Contact&lt;/h1&gt;`</span>
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ContactComponent { }
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { NgModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
<span class="hljs-keyword">import</span> { CommonModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/common'</span>;
<span class="hljs-keyword">import</span> { RouterModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/router'</span>;
<span class="hljs-keyword">import</span> { HomeComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">'./home.component'</span>;

<span class="hljs-meta">@NgModule</span>({
  declarations: [HomeComponent],
  imports: [
    CommonModule,
    RouterModule.forChild([{ path: <span class="hljs-string">''</span>, component: HomeComponent }])
  ]
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> HomeModule { }
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { NgModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
<span class="hljs-keyword">import</span> { CommonModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/common'</span>;
<span class="hljs-keyword">import</span> { RouterModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/router'</span>;
<span class="hljs-keyword">import</span> { AboutComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">'./about.component'</span>;

<span class="hljs-meta">@NgModule</span>({
  declarations: [AboutComponent],
  imports: [
    CommonModule,
    RouterModule.forChild([{ path: <span class="hljs-string">''</span>, component: AboutComponent }])
  ]
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> AboutModule { }
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { NgModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
<span class="hljs-keyword">import</span> { CommonModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/common'</span>;
<span class="hljs-keyword">import</span> { RouterModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/router'</span>;
<span class="hljs-keyword">import</span> { ContactComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">'./contact.component'</span>;

<span class="hljs-meta">@NgModule</span>({
  declarations: [ContactComponent],
  imports: [
    CommonModule,
    RouterModule.forChild([{ path: <span class="hljs-string">''</span>, component: ContactComponent }])
  ]
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ContactModule { }
</code></pre>
<p><strong>3- Création d'un service appelé</strong> <code>InternetConnectionService</code> <strong>qui nous permet de détecter la qualité de la connexion internet :</strong></p>
<pre><code class="lang-plaintext">ng g s internet-connection
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Injectable } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;

<span class="hljs-keyword">declare</span> <span class="hljs-keyword">var</span> navigator: <span class="hljs-built_in">any</span>;

<span class="hljs-meta">@Injectable</span>({
  providedIn: <span class="hljs-string">'root'</span>
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> InternetConnectionService {

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) { }

  getSpeed(): <span class="hljs-built_in">string</span> {
    <span class="hljs-keyword">const</span> connection = navigator.connection;
    <span class="hljs-keyword">const</span> <span class="hljs-keyword">type</span> = connection.effectiveType;
    <span class="hljs-keyword">const</span> speed = connection.downlink;

    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">type</span> === <span class="hljs-string">'slow-2g'</span> || speed &lt; <span class="hljs-number">0.5</span>) {
      <span class="hljs-keyword">return</span> <span class="hljs-string">'2G'</span>;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">type</span> === <span class="hljs-string">'2g'</span> || speed &lt; <span class="hljs-number">1</span>) {
      <span class="hljs-keyword">return</span> <span class="hljs-string">'3G'</span>;
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-string">'5G'</span>;
    }
  }
}
</code></pre>
<p><strong>4- Création d'une stratégie de préchargement personnalisée appelée</strong> <code>InternetConnectionPreloadingStrategy</code> <strong>:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { PreloadingStrategy, Route } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/router'</span>;
<span class="hljs-keyword">import</span> { Observable, <span class="hljs-keyword">of</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'rxjs'</span>;
<span class="hljs-keyword">import</span> { InternetConnectionService } <span class="hljs-keyword">from</span> <span class="hljs-string">'./internet-connection.service'</span>;

<span class="hljs-meta">@Injectable</span>({ providedIn: <span class="hljs-string">'root'</span> })
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> InternetConnectionPreloadingStrategy <span class="hljs-keyword">implements</span> PreloadingStrategy {

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> connectionService: InternetConnectionService</span>) { }

  preload(route: Route, load: <span class="hljs-function">() =&gt;</span> Observable&lt;<span class="hljs-built_in">any</span>&gt;): Observable&lt;<span class="hljs-built_in">any</span>&gt; {
    <span class="hljs-keyword">const</span> quality = <span class="hljs-built_in">this</span>.connectionService.getSpeed();

    <span class="hljs-keyword">if</span> (quality === <span class="hljs-string">'5G'</span>) {
      <span class="hljs-keyword">return</span> load();
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">of</span>(<span class="hljs-literal">null</span>);
    }
  }
}
</code></pre>
<ul>
<li><em>Notre stratégie</em> <code>InternetConnectionPreloadingStrategy</code> <em>implémente l'interface</em> <code>PreloadingStrategy</code> <em>parce que nous devons redéfinir la méthode</em> <code>preload()</code> <em>en y rajoutant la logique basée sur la qualité de la connexion internet.</em></li>
</ul>
<p><strong>5- Modification de</strong> <code>app-routing.module.ts</code> <strong>:</strong></p>
<p>Dans notre fichier, <code>app-routing.module.ts</code>, voici ce que nous avons jusque-là :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { NgModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
<span class="hljs-keyword">import</span> { Routes, RouterModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/router'</span>;

<span class="hljs-keyword">const</span> routes: Routes = [
  { path: <span class="hljs-string">''</span>, loadChildren: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./home/home.module'</span>).then(<span class="hljs-function"><span class="hljs-params">m</span> =&gt;</span> m.HomeModule) },
  { path: <span class="hljs-string">'about'</span>, loadChildren: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./about/about.module'</span>).then(<span class="hljs-function"><span class="hljs-params">m</span> =&gt;</span> m.AboutModule) },
  { path: <span class="hljs-string">'contact'</span>, loadChildren: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./contact/contact.module'</span>).then(<span class="hljs-function"><span class="hljs-params">m</span> =&gt;</span> m.ContactModule) }
];

<span class="hljs-meta">@NgModule</span>({
  imports: [RouterModule.forRoot(routes)],
  <span class="hljs-built_in">exports</span>: [RouterModule]
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> AppRoutingModule { }
</code></pre>
<p>Comme nous pouvons le remarquer, c'est du classique. À présent, nous procéderons à quelques modifications afin d'ajouter notre stratégie de pré-chargement qui dépend de la qualité de notre connexion internet. Voici le résultat :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { NgModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
<span class="hljs-keyword">import</span> { Routes, RouterModule, PreloadAllModules, PreloadingStrategy, Route } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/router'</span>;
<span class="hljs-keyword">import</span> { InternetConnectionPreloadingStrategy } <span class="hljs-keyword">from</span> <span class="hljs-string">'./internet-connection-preloading-strategy'</span>;

<span class="hljs-keyword">const</span> routes: Routes = [
  { path: <span class="hljs-string">''</span>, loadChildren: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./home/home.module'</span>).then(<span class="hljs-function"><span class="hljs-params">m</span> =&gt;</span> m.HomeModule) },
  { path: <span class="hljs-string">'about'</span>, loadChildren: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./about/about.module'</span>).then(<span class="hljs-function"><span class="hljs-params">m</span> =&gt;</span> m.AboutModule) },
  { path: <span class="hljs-string">'contact'</span>, loadChildren: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./contact/contact.module'</span>).then(<span class="hljs-function"><span class="hljs-params">m</span> =&gt;</span> m.ContactModule) }
];

<span class="hljs-meta">@NgModule</span>({
  imports: [RouterModule.forRoot(routes, {
    preloadingStrategy: InternetConnectionPreloadingStrategy
  })],
  <span class="hljs-built_in">exports</span>: [RouterModule]
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> AppRoutingModule { }
</code></pre>
<ul>
<li><em>L'attribut</em> <code>preloadingStrategy</code> <strong><em>nous permet d'indiquer notre stratégie de pré-chargement.</em></strong></li>
</ul>
<p><strong>6- Modification du composant AppComponent :</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Component } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;

<span class="hljs-meta">@Component</span>({
  selector: <span class="hljs-string">'app-root'</span>,
  template: <span class="hljs-string">`
    &lt;nav&gt;
      &lt;a routerLink="/" routerLinkActive="active"&gt;Home&lt;/a&gt;
      &lt;a routerLink="/about" routerLinkActive="active"&gt;About&lt;/a&gt;
      &lt;a routerLink="/contact" routerLinkActive="active"&gt;Contact&lt;/a&gt;
    &lt;/nav&gt;
    &lt;router-outlet&gt;&lt;/router-outlet&gt;
  `</span>
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> AppComponent { }
</code></pre>
<p><strong>7- Débogage avec un navigateur en simulant différents débits de connexion Internet :</strong></p>
<blockquote>
<p>En inspectant notre navigateur après avoir lancé la commande <code>ng serve -o</code>, nous pouvons nous rendre dans l'option Réseau et simuler le débit de la connexion internet que nous désirons à savoir (2G, 3G, 4G ou 5G). En fonction de ce débit, nous pourrons observer le comportement lié aux chargements des modules qui s'occupent de l'affichage de nos trois (03) pages web créés précédemment.</p>
</blockquote>
<p>En somme, nous avons vu comment définir une stratégie de pré-chargement de nos bundles suivant le débit de la connexion internet de l'utilisateur afin de lui garantir une meilleure expérience utilisateur. Si vos utilisateurs sont susceptibles d'être mobiles et de bénéficier d'une faible bande passante ou d'un faible niveau de WiFi ou de données mobiles, cette stratégie de pré-chargement pourrait s'avérer bénéfique. Si vous n'êtes pas sûr de vous, vous pouvez consulter vos utilisateurs professionnels (les parties prenantes de votre application) pour vous en rendre compte. Vous pouvez également combiner cette stratégie avec l'une des autres stratégies personnalisées. Avant de choisir cette option, ou toute autre stratégie de pré-chargement, il est recommandé d'effectuer des tests à différentes vitesses de réseau dans le cadre de différents flux de travail d'utilisateurs valides et courants. Ces données pourrons vous aider à la prise de décision afin de garantir une meilleure expérience utilisateur à vos utilisateurs.</p>
<blockquote>
<p>Pour plus d'informations :</p>
<p>👉 <a target="_blank" href="https://angular.io/api/router/PreloadingStrategy">Angular Preloading Strategy</a></p>
<p>👉 <a target="_blank" href="https://www.johnpapa.net/preload-angular-bundles-when-good-network-connectivity-is-detected/">Preload Angular Bundles by John Papa</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Tests unitaires sur un composant Angular utilisant @Input() et @Output()]]></title><description><![CDATA[Nombreux sont les développeurs Angular qui ont peur d'écrire des tests unitaires ou ne savent carrément pas comment faire et par où commencer. Le but de cet article est de prendre un léger exemple afin de démystifier la complexité autour des tests un...]]></description><link>https://angulardev.fr/tests-unitaires-sur-un-composant-angular-utilisant-input-et-output</link><guid isPermaLink="true">https://angulardev.fr/tests-unitaires-sur-un-composant-angular-utilisant-input-et-output</guid><category><![CDATA[Angular]]></category><category><![CDATA[Testing]]></category><category><![CDATA[unit testing]]></category><category><![CDATA[karma]]></category><category><![CDATA[jasmine]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Tue, 30 May 2023 07:39:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/dMUt0X3f59Q/upload/790b4a94f8db788d8f63b86c4241b5be.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Nombreux sont les développeurs Angular qui ont peur d'écrire des tests unitaires ou ne savent carrément pas comment faire et par où commencer. Le but de cet article est de prendre un léger exemple afin de démystifier la complexité autour des tests unitaires. Nous utiliserons bien-sûr le combo <code>Jasmine/Karma</code> qui est déjà présent lors de la création d'un projet Angular.</p>
<p>En effet, <code>Jasmine</code> est un framework qui nous permet d'aller vite dans l'écriture de nos tests unitaires et <code>Karma</code> est un serveur qui nous permet de lancer nos tests avec un rendu de tout ce qui se passe sur notre navigateur.</p>
<blockquote>
<p>Considérons un composant réutilisable <code>HelloComponent</code> qui permet d'afficher le nom de l'utilisateur (<code>username</code>) dès qu'on clique sur un bouton (<code>Hello</code>).</p>
</blockquote>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span> {{ username }} <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">button</span> (<span class="hljs-attr">click</span>)=<span class="hljs-string">'sayHello(username)'</span>&gt;</span> Hello <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> HelloComponent {
    <span class="hljs-meta">@Input</span>() username = <span class="hljs-string">''</span>;
    <span class="hljs-meta">@Output</span>() hello = <span class="hljs-keyword">new</span> EventEmitter&lt;<span class="hljs-built_in">string</span>&gt;()

    <span class="hljs-keyword">public</span> sayHello(username: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">void</span> {
        <span class="hljs-built_in">this</span>.hello.emit(username);
    }
}
</code></pre>
<p>Comme nous pouvons le pouvoir, nous avons notre composant <code>HelloComponent</code> qui comprend :</p>
<ul>
<li><p><code>username</code> qui est notre propriété <code>Input()</code></p>
</li>
<li><p><code>hello</code> qui est notre évènement <code>Output()</code></p>
</li>
</ul>
<p>Notre composant réutilisable pourra être appelé par un composant parent comme ceci par exemple :</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">app-hello</span> [<span class="hljs-attr">username</span>]=<span class="hljs-string">"'john'"</span> (<span class="hljs-attr">hello</span>)=<span class="hljs-string">"onSayHello($event)"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">app-hello</span>&gt;</span>
</code></pre>
<p>En créant un composant Angular, nous avons généralement un fichier qui a pour extension <code>`.spec.ts` </code>. C'est le fichier dédié à l'écriture des tests unitaires liés à notre composant. En ouvrant le fichier <code>hello.component.spec.ts</code> qui fait référence à notre composant HelloComponent, nous avons ce code de base :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { ComponentFixture, TestBed } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core/testing'</span>;

<span class="hljs-keyword">import</span> { NotificationToastComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">'./hello.component'</span>;

describe(<span class="hljs-string">'HelloComponent'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">let</span> component: HelloComponent;
  <span class="hljs-keyword">let</span> fixture: ComponentFixture&lt;HelloComponent&gt;;

  beforeEach(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">await</span> TestBed.configureTestingModule({
      declarations: [ HelloComponent ]
    })
    .compileComponents();

    fixture = TestBed.createComponent(HelloComponent);
    component = fixture.componentInstance;
    component.username = <span class="hljs-string">'john'</span>;
    fixture.detectChanges();
  });

  it(<span class="hljs-string">'should create'</span>, <span class="hljs-function">() =&gt;</span> {
    expect(component).toBeTruthy();
  });
});
</code></pre>
<ul>
<li><p>Le code généré nous a créé une instance du composant <code>HelloComponent</code>. Ensuite, il a fait appel à la méthode <code>detectChanges()</code> pour mettre à jour la vue si entre temps une propriété du composant a été modifié.</p>
</li>
<li><p>Nous avons rajouté <code>component.username = 'john';</code> afin qu'il initialise la valeur de <code>username</code> avec <code>'john'</code> lors de la création de l'instance.</p>
</li>
</ul>
<p>Nous pouvons à présent écrire les tests unitaires qu'il faut par rapport à notre logique métier. Nous écrirons deux tests unitaires :</p>
<p><strong>1) Le premier test vise à vérifier que notre variable</strong> <code>username</code> <strong>est bien affichée sur notre interface :</strong></p>
<pre><code class="lang-typescript">it(<span class="hljs-string">'should display username'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> messageElement = fixture.nativeElement.querySelector(<span class="hljs-string">'p'</span>);
  expect(messageElement.textContent).toContain(<span class="hljs-string">'john'</span>);
});
</code></pre>
<ul>
<li>Dans notre premier test ci-dessus, nous avons sélectionné l'élément <code>HTML</code> qui affiche la propriété <code>username</code> et vérifié qu'il contient la valeur <code>"john"</code>. La valeur de <code>username</code> a été initialisée dans la méthode <code>beforeEach()</code> .</li>
</ul>
<p><strong>2) Le second test vise à vérifier que l'évènement</strong> <code>hello()</code> <strong>est émise lorsqu'on fait appel à la méthode</strong> <code>sayHello()</code> <strong>:</strong></p>
<pre><code class="lang-typescript">   it(<span class="hljs-string">'should emit hello event'</span>, <span class="hljs-function">() =&gt;</span> {
    spyOn(component.hello, <span class="hljs-string">'emit'</span>);
    component.sayHello(<span class="hljs-string">'john'</span>);
    expect(component.hello.emit).toHaveBeenCalledWith(<span class="hljs-string">'john'</span>);
  });
</code></pre>
<ul>
<li>Dans notre second test ci-dessus, nous avons utilisé la méthode <code>spyOn()</code> pour espionner l'évènement <code>hello()</code>. En effet, <code>spyOn()</code> est une fonction qui enregistre des informations sur les appels de la méthode à espionner, comme le nombre de fois où elle a été appelée, avec quels arguments et ce qu'elle a renvoyé. Enfin, nous avons vérifié que l'événement <code>hello()</code> a été émis avec la valeur <code>'john'</code> lorsqu'on appelle la fonction <code>sayHello()</code>.</li>
</ul>
<blockquote>
<p>Nous pouvons lancer la commande <code>ng test</code> pour lancer le serveur <code>Karma</code> et voir le rendu de nos tests unitaires sur un navigateur.</p>
</blockquote>
<p>Pour finir, voici le code complet lié aux tests unitaires sur notre composant <code>HelloComponent</code> :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { ComponentFixture, TestBed } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core/testing'</span>;

<span class="hljs-keyword">import</span> { NotificationToastComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">'./hello.component'</span>;

describe(<span class="hljs-string">'HelloComponent'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">let</span> component: HelloComponent;
  <span class="hljs-keyword">let</span> fixture: ComponentFixture&lt;HelloComponent&gt;;

  beforeEach(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">await</span> TestBed.configureTestingModule({
      declarations: [ HelloComponent ]
    })
    .compileComponents();

    fixture = TestBed.createComponent(HelloComponent);
    component = fixture.componentInstance;
    component.username = <span class="hljs-string">'john'</span>;
    fixture.detectChanges();
  });

  it(<span class="hljs-string">'should create'</span>, <span class="hljs-function">() =&gt;</span> {
    expect(component).toBeTruthy();
  });

  it(<span class="hljs-string">'should display username'</span>, <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">const</span> messageElement = fixture.nativeElement.querySelector(<span class="hljs-string">'p'</span>);
      expect(messageElement.textContent).toContain(<span class="hljs-string">'john'</span>);
  });

  it(<span class="hljs-string">'should emit hello event'</span>, <span class="hljs-function">() =&gt;</span> {
    spyOn(component.hello, <span class="hljs-string">'emit'</span>);
    component.sayHello(<span class="hljs-string">'john'</span>);
    expect(component.hello.emit).toHaveBeenCalledWith(<span class="hljs-string">'john'</span>);
  });
});
</code></pre>
<p>En somme, nous avons exploré comment écrire des tests unitaires pour un composant Angular qui utilise les décorateurs <code>@Input()</code> et <code>@Output()</code>. Les tests unitaires sont une partie importante du processus de développement logiciel et nous espérons que nous les aborderons davantage avec plus d'aisance et moins de peur.</p>
<blockquote>
<p>Pour plus d'informations :</p>
<p>👉 <a target="_blank" href="https://angular.io/guide/testing">Angular Testing</a></p>
<p>👉 <a target="_blank" href="https://jasmine.github.io/pages/getting_started.html">Jasmine Documentation</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Les nouveautés en Angular 16 (Partie 2)]]></title><description><![CDATA[Cet article est la suite de ce que nous avons eu à énoncer comme nouveautés dans la version 16 de Angular ici. Aujourd'hui, nous verrons ensemble également cinq (05) autres nouveautés liées à cette version.
esbuild dev-server
Il y a plus d'un an, la ...]]></description><link>https://angulardev.fr/les-nouveautes-en-angular-16-partie-2</link><guid isPermaLink="true">https://angulardev.fr/les-nouveautes-en-angular-16-partie-2</guid><category><![CDATA[Angular]]></category><category><![CDATA[update ]]></category><category><![CDATA[upgrade]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Mon, 22 May 2023 08:03:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/dnknfhZiBKg/upload/2cfa1ca0c7e7734cab2fd0e266392211.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cet article est la suite de ce que nous avons eu à énoncer comme nouveautés dans la version 16 de Angular <a target="_blank" href="https://angulardev.fr/les-nouveautes-en-angular-16-partie-1">ici</a>. Aujourd'hui, nous verrons ensemble également <strong><em>cinq (05) autres nouveautés</em></strong> liées à cette version.</p>
<h3 id="heading-esbuild-dev-server">esbuild dev-server</h3>
<p>Il y a plus d'un an, la core team de Angular a annoncé un support expérimental pour <code>esbuild</code> dans la CLI de Angular afin de rendre les builds plus rapides. Aujourd'hui, avec Angular 16, le système de build basé sur <code>esbuild</code> entre en <em>developer preview</em>. Les premiers tests ont montré une amélioration de 72% dans les builds de production à froid.</p>
<p>Au lancement de la commande <code>ng serve</code>, nous pouvons maintenant utiliser <code>Vite</code> <em>(créé par Evan You, le créateur de Vue.js)</em> pour le serveur de développement, et <code>esbuild</code> pour nos builds de développement et de production.</p>
<blockquote>
<p><strong>La core team de Angular a mis l'accent sur le fait que la CLI de Angular s'appuie exclusivement sur</strong> <code>Vite</code> <strong>en tant que serveur de développement. Pour supporter la correspondance des sélecteurs, le compilateur Angular doit maintenir un graphe de dépendance entre nos composants, ce qui nécessite un modèle de compilation différent de celui de</strong> <code>Vite</code><strong>.</strong></p>
</blockquote>
<p>Pour essayer <code>Vite + esbuild</code> il faut mettre à jour notre fichier <code>angular.json</code> en faisant ceci :</p>
<pre><code class="lang-typescript">...
<span class="hljs-string">"architect"</span>: {
  <span class="hljs-string">"build"</span>: {                     <span class="hljs-comment">/* Add the esbuild suffix  */</span>
    <span class="hljs-string">"builder"</span>: <span class="hljs-string">"@angular-devkit/build-angular:browser-esbuild"</span>,
...
</code></pre>
<h3 id="heading-angular-material-component">Angular Material Component</h3>
<p>La core team de Angular a annoncé qu'au cours des deux derniers trimestres, qu'elle a travaillé en étroite collaboration avec l'équipe <strong><em>Material Design de Google</em></strong> pour fournir l'implémentation de la référence de <code>Material 3</code> pour le Web avec <code>Angular Material</code>. <em>Les composants Web MDC qui ont été livrés en 2022 ont révélé les bases de cet effort.</em></p>
<p><strong><em>La prochaine étape consistera à lancer plus tard dans l'année une API de thématisation expressive basée sur des jetons qui permettra une personnalisation plus poussée des composants matériels d'Angular.</em></strong></p>
<h3 id="heading-api-to-provide-csp-nonce-for-inline-stylesheets">API to provide CSP nonce for inline stylesheets</h3>
<blockquote>
<p><strong>Les éléments de style en ligne que Angular inclut dans le DOM pour les styles de composants violent la politique de sécurité du contenu par défaut</strong> <code>style-src</code> <strong>(CSP). Pour corriger cela, ils devraient soit contenir un attribut</strong> <code>nonce</code><strong>, soit le serveur devrait inclure un hachage du contenu du style dans l'en-tête CSP. Même si Google n'a pas trouvé de vecteur d'attaque significatif pour cette vulnérabilité, de nombreuses entreprises appliquent une CSP stricte, ce qui a conduit à la popularité d'une demande de fonctionnalité sur le référentiel Angular.</strong></p>
<p>Dans Angular 16, la core team a implémenté une nouvelle fonctionnalité couvrant le framework, Universal, CDK, Material, et le CLI qui nous permet de spécifier un attribut <code>nonce</code> pour les styles des composants inline. Il y a deux façons de spécifier le <code>nonce</code> . Soit en utilisant l'attribut <code>ngCspNonce</code> ou à travers le jeton d'injection <code>CSP_NONCE</code>.</p>
</blockquote>
<p>L'attribut <code>ngCspNonce</code> est utile si vous avez accès à un modèle côté serveur qui peut ajouter le nonce à la fois à l'en-tête et dans le fichier <code>index.html</code> lors de la construction de la réponse.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">app</span> <span class="hljs-attr">ngCspNonce</span>=<span class="hljs-string">"{% nonce %}"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">app</span>&gt;</span>  
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>L'autre façon de spécifier <code>nonce</code> est d'utiliser le jeton d'injection <code>CSP_NONCE</code>. Nous pouvons utiliser cette approche si nous avons accès à <code>nonce</code> au moment de l'exécution et que nous voulons mettre en cache le fichier <code>index.html</code> :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {bootstrapApplication, CSP_NONCE} <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
<span class="hljs-keyword">import</span> {AppComponent} <span class="hljs-keyword">from</span> <span class="hljs-string">'./app/app.component'</span>;

bootstrapApplication(AppComponent, {
  providers: [{
    provide: CSP_NONCE,
    useValue: globalThis.myRandomNonceValue
  }]
});
</code></pre>
<h3 id="heading-configure-zone-in-bootstrapapplication">Configure zone in bootstrapApplication</h3>
<p>Dans l'article précédent mentionné plus haut, nous avons vu que <code>Signals</code> a remplacé <code>Zone.js</code>. <strong><em>Toutefois, Zone.js n'a pas disparu de Angular</em></strong>.</p>
<blockquote>
<p><strong>D'après la core team de Angular, après la publication initiale des API autonomes (standalone), des développeurs ont exprimé leur souhait de pouvoir configurer</strong> <code>Zone.js</code> <strong>avec la nouvelle API</strong> <code>bootstrapApplication</code><strong>.</strong></p>
</blockquote>
<p>Elle a donc ajouté une option pour le faire via <code>provideZoneChangeDetection</code> :</p>
<pre><code class="lang-typescript">bootstrapApplication(App, {
  providers: [provideZoneChangeDetection({ eventCoalescing: <span class="hljs-literal">true</span> })]
});
</code></pre>
<h3 id="heading-balises-de-fermeture-automatique">Balises de fermeture automatique</h3>
<p>Il s'agit d'une fonctionnalité très demandée qui a été récemment mise en œuvre qui nous permet d'utiliser des balises de fermeture automatique pour les composants dans les templates Angular. Il s'agit d'une petite amélioration de l'expérience du développeur qui pourrait nous faire économiser de la saisie.</p>
<p>Nous pouvons désormais remplacer :</p>
<pre><code class="lang-typescript">&lt;my-component [prop]=<span class="hljs-string">"props"</span>&gt;&lt;/my-component&gt;
</code></pre>
<p>Par ceci :</p>
<pre><code class="lang-typescript">&lt;my-component [prop]=<span class="hljs-string">"props"</span>/&gt;
</code></pre>
<p>En somme, nous avons vu en deux parties l'essentiel sur les nouveautés liées à Angular 16. J'ai hâte de découvrir les merveilles que nous ferons avec cette nouvelle version.</p>
<blockquote>
<p>Pour plus d'informations 👉 <a target="_blank" href="https://blog.angular.io/angular-v16-is-here-4d7a28ec680d">Angular v16 is here</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Les nouveautés en Angular 16 (Partie 1)]]></title><description><![CDATA[Le 03 Mai 2023, la core team de Angular a effectué la sortie officielle de la version 16.0.0 de Angular. Aujourd'hui, nous verrons ensemble huit (08) nouveautés liées à cette version.
Signals
Si vous êtes à l'écoute de la communauté Angular, vous ave...]]></description><link>https://angulardev.fr/les-nouveautes-en-angular-16-partie-1</link><guid isPermaLink="true">https://angulardev.fr/les-nouveautes-en-angular-16-partie-1</guid><category><![CDATA[Angular]]></category><category><![CDATA[news]]></category><category><![CDATA[upgrade]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Wed, 17 May 2023 07:44:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/dnknfhZiBKg/upload/10a611b4c2424c0427920286ffec2863.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Le 03 Mai 2023, la core team de Angular a effectué la sortie officielle de la version <code>16.0.0</code> de Angular. Aujourd'hui, nous verrons ensemble <strong><em>huit (08) nouveautés</em></strong> liées à cette version.</p>
<h3 id="heading-signals"><strong>Signals</strong></h3>
<p>Si vous êtes à l'écoute de la communauté Angular, vous avez déjà certainement entendu parler du mot "<code>Signals</code>" qui a sucité beaucoup d'engouements. En effet, jusqu'à la version 15 de Angular <strong><em>c'est la bibliothèque</em></strong> <code>Zone.js</code> <strong><em>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.</em></strong> Cependant, <code>Zone.js</code> pose essentiellement <strong><em>quatre (04)</em></strong> <strong><em>problèmes qui sont :</em></strong></p>
<blockquote>
<ul>
<li><p><code>Zone.js</code> <strong><em>a un surcoût d'environ</em></strong> <code>100 Ko</code><strong><em>. Bien que négligeable pour les applications de grande taille, cette surcharge est rédhibitoire pour le déploiement de composants web légers.</em></strong></p>
</li>
<li><p><strong><em>Avec</em></strong> <code>Zone.js</code><strong><em>, les objets du navigateur sont modifiés et les erreurs sont difficiles à diagnostiquer.</em></strong></p>
</li>
<li><p><code>Zone.js</code> <strong><em>n'arrive pas à interprèter</em></strong> <code>async</code> <strong><em>et</em></strong> <code>await</code> <strong><em>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à</em></strong> <code>async</code> <strong><em>et</em></strong> <code>await</code> <strong><em>en mode natif.</em></strong></p>
</li>
<li><p><strong><em>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.</em></strong></p>
</li>
</ul>
</blockquote>
<p>Afin de résoudre ces problèmes, ils ont pensé à la création de <code>Signals</code>. La bibliothèque <code>Signals</code> nous permet de définir des valeurs réactives et d'exprimer des dépendances entre elles. Elle est souple et simple à implémenter. Voyons ensemble un exemple d'implémentation :</p>
<pre><code class="lang-typescript"><span class="hljs-meta">@Component</span>({
  selector: <span class="hljs-string">'my-app'</span>,
  standalone: <span class="hljs-literal">true</span>,
  template: <span class="hljs-string">`
    {{ fullName() }} &lt;button (click)="setName('John')"&gt;Click&lt;/button&gt;
  `</span>,
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> App {
  firstName = signal(<span class="hljs-string">'Jane'</span>);
  lastName = signal(<span class="hljs-string">'Doe'</span>);
  fullName = computed(<span class="hljs-function">() =&gt;</span> <span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-built_in">this</span>.firstName()}</span> <span class="hljs-subst">${<span class="hljs-built_in">this</span>.lastName()}</span>`</span>);

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
    effect(<span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Name changed:'</span>, <span class="hljs-built_in">this</span>.fullName()));
  }

  setName(newName: <span class="hljs-built_in">string</span>) {
    <span class="hljs-built_in">this</span>.firstName.set(newName);
  }
}
</code></pre>
<h3 id="heading-rendu-cote-serveur-ssr-et-hydratation-ameliores"><strong>Rendu côté serveur (SSR) et hydratation améliorés</strong></h3>
<p>La core team de Angular a effectué une enquête annuelle afin d'avoir une idée des choses à améliorer dans les prochaines versions. Le résultat a démontré que les développeurs se plaignaient beaucoup du rendu côté serveur. Il y a donc eu naturellement des améliorations à ce niveau. En voici une preuve :</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/naveedahmed/status/1645983995820376065?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1645983995820376065%7Ctwgr%5E17b16677fde07dd219fc5a726ef6e24923f725a0%7Ctwcon%5Es1_&amp;ref_url=https%3A%2F%2Fcdn.embedly.com%2Fwidgets%2Fmedia.html%3Ftype%3Dtext2Fhtmlkey%3Da19fcc184b9711e1b4764040d3dc5c07schema%3Dtwitterurl%3Dhttps3A%2F%2Ftwitter.com%2Fnaveedahmed%2Fstatus%2F1645983995820376065image%3Dhttps3A%2F%2Fi.embed.ly%2F1%2Fimage3Furl3Dhttps253A252F252Fabs.twimg.com252Ferrors252Flogo46x38.png26key3Da19fcc184b9711e1b4764040d3dc5c07">https://twitter.com/naveedahmed/status/1645983995820376065?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1645983995820376065%7Ctwgr%5E17b16677fde07dd219fc5a726ef6e24923f725a0%7Ctwcon%5Es1_&amp;ref_url=https%3A%2F%2Fcdn.embedly.com%2Fwidgets%2Fmedia.html%3Ftype%3Dtext2Fhtmlkey%3Da19fcc184b9711e1b4764040d3dc5c07schema%3Dtwitterurl%3Dhttps3A%2F%2Ftwitter.com%2Fnaveedahmed%2Fstatus%2F1645983995820376065image%3Dhttps3A%2F%2Fi.embed.ly%2F1%2Fimage3Furl3Dhttps253A252F252Fabs.twimg.com252Ferrors252Flogo46x38.png26key3Da19fcc184b9711e1b4764040d3dc5c07</a></div>
<p> </p>
<h3 id="heading-creation-des-schemas-dapplications-autonomes"><strong>Création des schémas d'applications autonomes</strong></h3>
<p>Précédemment, dans cet <a target="_blank" href="https://angulardev.fr/a-la-decouverte-du-fameux-standalone-component">article</a>, nous avons vu ensemble ce que c'est qu'un composant autonome (<strong><em>standalone</em></strong>). Avec la version 16 de Angular, il est possible via la CLI de créer une application Angular en précisant par défaut que nous voulons créer uniquement des composants autonomes. Voici ce qu'il faut faire :</p>
<pre><code class="lang-typescript">ng <span class="hljs-keyword">new</span> --standalone
</code></pre>
<h3 id="heading-possibilite-dajouter-la-regle-required-sur-des-input"><strong>Possibilité d'ajouter la règle "required" sur des Input</strong></h3>
<p>Lorsque nous créons un composant réutilisable en Angular, nous utilisons le décorateur <code>@Input()</code> pour préciser les <code>props</code> de notre composant. Jusqu'à la version 15 de Angular, il n'est pas possible de rendre obligatoire nos propriétés lorsque notre composant réutilisable est appelé dans un composant parent. Pour améliorer donc cette expérience développeur, la communauté a suggéré ceci :</p>
<pre><code class="lang-typescript"><span class="hljs-meta">@Component</span>(...)
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> App {
  <span class="hljs-meta">@Input</span>({ required: <span class="hljs-literal">true</span> }) title: <span class="hljs-built_in">string</span> = <span class="hljs-string">''</span>;
}
</code></pre>
<h3 id="heading-utilisation-de-input-dans-un-resolver"><strong>Utilisation de @Input() dans un Resolver</strong></h3>
<p>Cette nouveauté facilite l'utilisation des Resolvers en Angular. Cela consiste tout simplement en ceci :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> routes = [
  {
    path: <span class="hljs-string">'about'</span>,
    loadComponent: <span class="hljs-keyword">import</span>(<span class="hljs-string">'./about'</span>),
    resolve: { contact: <span class="hljs-function">() =&gt;</span> getContact() }
  }
];

<span class="hljs-meta">@Component</span>(...)
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> About {
  <span class="hljs-comment">// The value of "contact" is passed to the contact input</span>
  <span class="hljs-meta">@Input</span>() contact?: <span class="hljs-built_in">string</span>;
}
</code></pre>
<p><strong><em>Nous remarquons que notre variable</em></strong> <code>contact</code> <strong><em>qui est un</em></strong> <code>@Input()</code> <strong><em>est directement liée à l'objet ayant pour clé le même nom que notre variable présent dans notre Resolver.</em></strong></p>
<h3 id="heading-destroyref-andamp-takeuntildestroyed"><strong>DestroyRef &amp; takeUntilDestroyed</strong></h3>
<ul>
<li><strong><em>DestroyRef</em></strong><br />  La classe <strong>DestroyRef</strong> a été créée pour faciliter l'utilisation de la méthode <strong>ngOnDestroy()</strong> qui fait partie du cycle de vie d'un composant Angular. L'idée ici est de pouvoir l'utiliser dans les directives, services, pipes, etc...<br />  Voici un exemple :</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Injectable, DestroyRef } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;

<span class="hljs-meta">@Injectable</span>(...)
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> AppService {
  destroyRef = inject(DestroyRef);

  destroy() {
    <span class="hljs-built_in">this</span>.destroyRef.onDestroy(<span class="hljs-function">() =&gt;</span> <span class="hljs-comment">/* cleanup */</span> );
  }
}
</code></pre>
<ul>
<li><strong><em>takeUntilDestroyed</em></strong><br />  Avant la création de la méthode <code>takeUntilDestroyed()</code>, voici comment procédons-nous :</li>
</ul>
<pre><code class="lang-typescript">destroyed$ = <span class="hljs-keyword">new</span> ReplaySubject&lt;<span class="hljs-built_in">void</span>&gt;(<span class="hljs-number">1</span>);

data$ = http.get(<span class="hljs-string">'...'</span>).pipe(takeUntil(<span class="hljs-built_in">this</span>.destroyed$));

ngOnDestroy() {
  <span class="hljs-built_in">this</span>.destroyed$.next();
}
</code></pre>
<p>Aujourd'hui avec la version 16 de Angular, nous avons ceci :</p>
<pre><code class="lang-typescript">data$ = http.get(<span class="hljs-string">'…'</span>).pipe(takeUntilDestroyed());
</code></pre>
<h3 id="heading-ngcc-entrycomponents-et-nodejs-v14-ne-sont-pas-supportes-par-angular-16">NGCC, entryComponents et Node.js v14 ne sont pas supportés par Angular 16</h3>
<p>La compilation <strong><em>NGCC</em></strong> n'est pas supportée par Angular 16. Il faudra faire attention lors de la montée en version. Il en est de même pour <strong><em>entryComponent</em></strong> qui était obsolète depuis Angular 9. Enfin, il est à souligner que Angular 16 ne supporte pas la version 14 de <strong><em>Node.js</em></strong>.</p>
<h3 id="heading-typescript-50">TypeScript 5.0</h3>
<p>La version 16 de Angular supporte le <strong><em>TypeScript 5.0</em></strong> qui inclut l'utilisation des <strong><em>décorateurs ECMAScript</em></strong>.</p>
<p>En somme, nous avons remarqué à travers cette première partie sur les nouveautés liées à Angular 16, qu'il y a eu beaucoup d'améliorations et que l'expérience développeur est toujours au coeur de ce dernier.</p>
<blockquote>
<p>Pour plus d'informations :</p>
<p>👉 <a target="_blank" href="https://blog.angular.io/angular-v16-is-here-4d7a28ec680d">Angular v16 is here</a></p>
<p>👉 <a target="_blank" href="https://www.angulararchitects.io/aktuelles/angular-signals/">Angular Signals by AngularArchitects</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Comment réussir une montée en version en Angular ?]]></title><description><![CDATA[Angular est un framework bien maintenu par une communauté très active autour de ce dernier. Cette réactivité permet la mise à jour continue du framework à travers la résolution des failles de sécurité et l'ajout de nouvelles fonctionnalités. Nous ser...]]></description><link>https://angulardev.fr/comment-reussir-une-montee-en-version-en-angular</link><guid isPermaLink="true">https://angulardev.fr/comment-reussir-une-montee-en-version-en-angular</guid><category><![CDATA[Angular]]></category><category><![CDATA[upgrade]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Fri, 12 May 2023 07:54:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/cYUMaCqMYvI/upload/0ffa78404b9b4e19b31d6b8ab3336102.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Angular est un framework bien maintenu par une communauté très active autour de ce dernier. Cette réactivité permet la mise à jour continue du framework à travers la résolution des failles de sécurité et l'ajout de nouvelles fonctionnalités. Nous serons tôt ou tard confrontés à des soucis d'obsolescence lorsque la version de Angular que nous avons dans notre projet n'est plus <strong><em>LTS (Long Time Support)</em></strong>. Dans cette situation, une mise à niveau ou une montée de version est importante pour résoudre les failles de sécurité ou bénéficier des nouveautés de notre framework préféré.</p>
<p>Cependant, cette tâche n'est pas si aisée parce qu'il faut s'assurer qu'il n'y a pas de régression après notre montée en version. Nous verrons ensemble comment le faire un toute sérénité.</p>
<blockquote>
<ol>
<li><p><strong>Consulter le site</strong> <a target="_blank" href="https://update.angular.io/"><strong>Angular Update Guide</strong></a> <strong>:</strong><br /> Ce site a été conçu par la core team de Angular pour nous permettre d'étudier l'impact de notre montée en version en considérant quelques facteurs à savoir la version actuelle de notre projet, la version ciblée pour la mise à niveau, les différents packages maintenus par la core team de Angular que nous avons dans notre projet. Toutefois, il faut aller de façon méthodique avec les instructions que propose ce site.</p>
</li>
<li><p><strong>S'assurer de la version de</strong> <code>Node.js</code> <strong>compatible avec la nouvelle version de Angular ciblée :</strong><br /> Notre montée en version peut nous contraindre à effectuer une mise à jour de la version de <code>Node.js</code> utilisée. Il ne faut pas hésiter à le faire. Nous pouvons nous rendre ici <a target="_blank" href="https://angular.io/guide/versions">Angular Version Compatibility - Node.js</a> .</p>
</li>
<li><p><strong>Exécuter la commande</strong> <code>ng update</code> <strong>:</strong><br /> L'exécution de la commande <code>ng update</code> tout court n'impactera pas notre projet. Cette commande affichera dans notre terminal un terminal avec les dépendances reconnues officiellement par Angular en affichant la version actuelle et la version à atteindre pendant la mise à niveau.</p>
</li>
<li><p><strong>Effectuer une montée en version progressive :</strong><br /> Il est nécessaire de faire une montée en version progressive. <strong><em>Supposons que nous voulons faire une montée en version de Angular 9 vers Angular 16. Il est capital d'y aller pas-à-pas. D'abord, il faut faire une montée en version de 9 vers 10, ensuite de 10 vers 11 et ainsi de suite.</em></strong> La montée en version se fait en une ligne de commande : <code>ng update @angular/core@[version_cible] @angular/cli@[version cible]</code> .</p>
</li>
<li><p><strong>S'assurer de la compatibilité des dépendances externes avec la version de Angular :</strong><br /> Pendant la montée en version progressive, il faut s'assurer que les bibliothèques externes suivent le mouvement. A chaque montée de version, il faut s'assurer que le projet compile bien et qu'il n'y a pas de régression. Il faut vérifier au niveau de la documentation de ces bibliothèques quelle est la version compatible avec notre version de Angular cible.</p>
</li>
<li><p><strong>S'attendre à faire de la réécriture de code si</strong> <code>NgRx</code> <strong>est utilisée :</strong><br /> Si nous utilisons le gestionnaire d'état <code>NgRx</code> dans notre projet, nous devons vérifier de façon miticuleuse que nous n'avons pas à réécrire nos <code>Effects</code> par exemple. En effet, la bibliothèque dans sa mise à jour est passée des décorateurs aux fonctions create pour faciliter l'implémentation des <code>Actions</code>, <code>Reducers</code>, <code>Effects</code>, etc.</p>
</li>
<li><p><strong>Effectuer du "forcing" quand il le faut :</strong><br /> Lorsque nous rencontrons une bibliothèque développée en Angular dont le package <code>@angular/core</code> ou <code>@angular/common</code> est obsolète par rapport à ce que nous avons dans notre projet, la commande <code>ng update</code> énoncée dans le <em>point 4</em> n'ira pas au bout. <strong><em>Pour y remédier, nous devons pousser un peu plus loin notre investigation. Il faut lire le code source de cette bibliothèque pour analyser les impacts lors de l'ajout de</em></strong> <code>--force</code> <strong><em>dans notre commande du point 4. Si notre bibliothèque qui coince n'a pas d'autres dépendances à part Angular alors il n'y aura pas d'incidence. Dans le cas contraire, il faudra réfléchir à un palliatif.</em></strong></p>
</li>
<li><p><strong>Lire et bien comprendre les warnings et problèmes qui s'affichent dans le terminal et la console de notre navigateur :</strong><br /> Lors de la montée en version, nous devons être très attentif à ce qui s'affiche dans notre terminal et dans la console de notre navigateur.</p>
</li>
<li><p><strong>Vérifier la compatibilité avec les navigateurs obsolètes :</strong></p>
<p> Après la montée en version, il y a des versions de navigateurs qui ne sont pas prises en charge. Notre application risque de ne pas fonctionner sur ces navigateurs. Nous pouvons vérifier la compatibilité ici <a target="_blank" href="https://angular.io/guide/browser-support">Browser Support</a>. Toutefois, avec <strong><em>BrowsersList</em></strong> déjà présent dans notre projet, on peut étendre les versions de navigateurs que nous souhaitons en le couplant avec les polyfills.</p>
</li>
</ol>
</blockquote>
<p>Pour finir, il est à souligner que la <strong><em>CLI (Command-Line Interface)</em></strong> de Angular aide beaucoup dans la montée en version. En effet, elle s'occupe même de modifier le contenu de nos fichiers quand il le faut. Beaucoup de choses se passent de façon transparente. C'est vraiment intéressant pour l'expérience développeur. <strong><em>Aussi, il n'est plus à rappeler, qu'il faut versionner son code. Le code même obsolète qui fonctionne déjà doit être bien conservé en cas de rollback.</em></strong></p>
<p>En somme, nous avons fait un tour d'horizon des points essentiels pour réussir sereinement notre montée en version. La patience et la concentration sont les qualités à avoir pendant cette épreuve.</p>
<blockquote>
<p>Pour plus d'informations :</p>
<p>👉 <a target="_blank" href="https://update.angular.io/">Angular Update Guide</a></p>
<p>👉 <a target="_blank" href="https://angular.io/guide/versions">Angular Version Compatibility - Node.js - RxJS</a></p>
<p>👉 <a target="_blank" href="https://angular.io/guide/browser-support">Browser Support</a></p>
<p>👉 <a target="_blank" href="https://browsersl.ist/">BrowsersList</a></p>
<p>👉 <a target="_blank" href="https://ngrx.io/docs">NgRx Docs</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Pourquoi c'est important d'utiliser la fonction trackBy dans un *ngFor en Angular ?]]></title><description><![CDATA[Dans nos applications Angular, nous sommes parfois amenés à afficher des listes d'élements sur nos interfaces utilisateur. Pour pouvoir réaliser cette opération, nous itérons sur des tableaux d'élements simples ou d'objets en utilisant la directive *...]]></description><link>https://angulardev.fr/pourquoi-cest-important-dutiliser-la-fonction-trackby-dans-un-ngfor-en-angular</link><guid isPermaLink="true">https://angulardev.fr/pourquoi-cest-important-dutiliser-la-fonction-trackby-dans-un-ngfor-en-angular</guid><category><![CDATA[Angular]]></category><category><![CDATA[ngFor]]></category><category><![CDATA[performance]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Tue, 09 May 2023 07:42:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/LKsHwgzyk7c/upload/203bd76ddf6451119a94177a8ef1fcd8.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Dans nos applications Angular, nous sommes parfois amenés à afficher des listes d'élements sur nos interfaces utilisateur. Pour pouvoir réaliser cette opération, nous itérons sur des tableaux d'élements simples ou d'objets en utilisant la directive <code>*ngFor</code>.<br />De façon basique, voici un exemple de comment procédons-nous généralement :</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span> *<span class="hljs-attr">ngFor</span>=<span class="hljs-string">"let item of items"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span> {{ item.name }} <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
</code></pre>
<ul>
<li><code>items</code> <strong><em>est un tableau d'objets qui contient la propriété</em></strong> <code>name</code> <strong><em>comme un attribut de chacun de ces objets.</em></strong></li>
</ul>
<p>Supposons à présent que nous ayons une très grande liste d'éléments à afficher et qu'après affichage, nous faisons des traitements sur quelques éléments de cette liste.</p>
<blockquote>
<p><strong><em>Lorsque nous inspectons notre page web en utilisant le navigateur</em></strong> <code>Google Chrome</code> <strong><em>par exemple, une fois dans le menu</em></strong> <code>Rendering</code> <strong><em>puis l'option</em></strong> <code>Paint Flashing</code><strong><em>, nous remarquons que tous les éléments de notre liste sont mis à jour à chaque fois, même ceux qui n'ont pas subi de modifications.</em></strong> <strong><em>Résultat, nous aurons des soucis de performance.</em></strong></p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1683586860342/73cfc2ea-1198-44a1-8bb5-4382961bf9d0.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>Heureusement pour nous, la core team de Angular y avait déjà pensé !<br />La solution est très simple. Il s'agit de la fonction <code>trackBy</code>😁</p>
</blockquote>
<p>En effet, la fonction <code>trackBy</code> nous permet d'identifier chaque élément de façon unique dans notre <strong><em>DOM</em></strong>. Ainsi, lorsque nous modifions des éléments de notre liste, seuls ces éléments sont mis à jour et toute notre liste ne subit plus de rafraîchissement.<br />Voici un exemple de comment procéder en reprenant l'exemple précédent :</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span> *<span class="hljs-attr">ngFor</span>=<span class="hljs-string">"let item of items; trackBy: itemTrackBy"</span> &gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span> {{ item.name }} <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ItemComponent {
 ...   
    <span class="hljs-keyword">public</span> itemTrackBy(index: <span class="hljs-built_in">number</span>, item: Item) {
      <span class="hljs-keyword">return</span> item.id;
    }
}
</code></pre>
<p>En somme, nous comprenons à présent pourquoi il est crucial de faire gaffe lorsque nous manipulons des boucles <code>*ngFor</code> dans nos applications Angular. C'est tout à notre avantage de permettre au <strong><em>DOM</em></strong> de pouvoir identifier de façon unique chaque élément de nos listes car nous gagnerons nettement en performance en cas de besoin.</p>
<blockquote>
<p>Pour plus d'informations 👉 <a target="_blank" href="https://angular.io/api/core/TrackByFunction">TrackBy Function</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[L'injection de dépendances en Angular: que faut t-il retenir ?]]></title><description><![CDATA[L'une des qualités du framework Angular réside en sa facilité à permettre l'utilisation de la notion d'injection de dépendances (Dependency Injection en anglais). En effet, tout le framework repose sur cette notion ce qui nous permet d'interagir avec...]]></description><link>https://angulardev.fr/linjection-de-dependances-en-angular-que-faut-t-il-retenir</link><guid isPermaLink="true">https://angulardev.fr/linjection-de-dependances-en-angular-que-faut-t-il-retenir</guid><category><![CDATA[Angular]]></category><category><![CDATA[dependency injection]]></category><category><![CDATA[services]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Thu, 04 May 2023 08:03:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/MfnX4XtGnvU/upload/378d140df5beea5616a0a207feb5fe59.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>L'une des qualités du framework Angular réside en sa facilité à permettre l'utilisation de la notion d'<code>injection de dépendances</code> (<code>Dependency Injection</code> en anglais). En effet, tout le framework repose sur cette notion ce qui nous permet d'interagir avec une multitude de classes sans se casser la tête. Avant, d'aller plus loin, voyons brièvement ensemble ce qu'est <code>l'injection de dépendances</code>.</p>
<p>En programmation orientée objet, nous manipulons des objets au moyen des classes. Supposons que nous avons trois (03) classes à savoir <code>ClassA</code>, <code>ClassB</code> et <code>ClassC</code> comme l'illustre cet exemple de code basé sur du <code>TypeScript</code> :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ClassA {
  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ClassB {
  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ClassC {
  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
  }
}
</code></pre>
<p>Notre objectif ici est de créer deux (02) objets respectivement des classes <code>ClassA</code> et <code>ClassB</code> que nous utiliserons dans notre classe <code>ClassC</code>. Nous avons donc le code qui suit :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ClassC {
  objectA = <span class="hljs-keyword">new</span> ClassA();
  objectB = <span class="hljs-keyword">new</span> ClassB();
  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {   
  }
}
</code></pre>
<p>Jusque-là, tout se passe bien. Supposons à présent que nos classes <code>ClassA</code> et <code>ClassB</code> contiennent des arguments dans leurs constructeurs respectifs comme l'indique le code suivant :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ClassA {
  <span class="hljs-keyword">constructor</span>(<span class="hljs-params">args</span>) {
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ClassB {
  <span class="hljs-keyword">constructor</span>(<span class="hljs-params">args</span>) {
  }
}
</code></pre>
<p>Ce changement au niveau de nos classes <code>ClassA</code> et <code>ClassB</code> nous contraint à modifier la façon dont nous créons nos objets dans la classe <code>ClassC</code>. Nous devons passer en paramètre des variables comme ceci :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ClassC {
  objectA;
  objectB;
  <span class="hljs-keyword">constructor</span>(<span class="hljs-params">argsA, argsB</span>) {   
   <span class="hljs-built_in">this</span>.objectA = <span class="hljs-keyword">new</span> ClassA(argsA);
   <span class="hljs-built_in">this</span>.objectB = <span class="hljs-keyword">new</span> ClassB(argsB);
  }
}
</code></pre>
<p>Cette façon classique de faire soulève deux (02) problèmes essentiels :</p>
<blockquote>
<ul>
<li><p><strong><em>Le premier problème c'est que le code n'est plus flexible parce que à chaque changement au niveau du constructeur de nos classes</em></strong> <code>ClassA</code> <strong><em>et</em></strong> <code>ClassB</code><strong><em>, nous devons opérer des changements au niveau de la classe</em></strong> <code>ClassC</code><strong><em>.</em></strong></p>
</li>
<li><p><strong>Le second problème c'est que notre code ne sera plus vraiment facile à tester parce que à chaque fois que nous créerons une instance de la classe</strong> <code>ClassC</code><strong>, nous aurons besoin d'indiquer les paramètres des classes</strong> <code>ClassA</code> <strong>et</strong> <code>ClassB</code> <strong>dans le constructeur de la classe</strong> <code>ClassC</code><strong>.</strong></p>
</li>
</ul>
<p>Afin d'y remédier, l'astuce la plus simple est d'utiliser la notion d'<code>injection de dépendances</code>.</p>
</blockquote>
<p>En reprenant l'exemple de nos trois (03) précédentes classes, nous avons ceci :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ClassA {
  <span class="hljs-keyword">constructor</span>(<span class="hljs-params">args</span>) {
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ClassB {
  <span class="hljs-keyword">constructor</span>(<span class="hljs-params">args</span>) {
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ClassC {
  objectA;
  objectB;
  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> objectA: ClassA, <span class="hljs-keyword">private</span> objectB: ClassB</span>) {   
    <span class="hljs-built_in">this</span>.objectA = objectA;
    <span class="hljs-built_in">this</span>.objectB = objectB;
  }
}
</code></pre>
<p>Nous remarquons donc que les changements au niveau du constructeur de nos classes <code>ClassA</code> et <code>ClassB</code> ne nous obligent plus à modifier nos instances dans la classe <code>ClassC</code>.</p>
<blockquote>
<p>Oui c'est magique 😂</p>
</blockquote>
<p>En revenant sur notre thématique, Angular est entièrement basé sur le langage orienté objet <code>TypeScript</code> ce qui lui de mettre en place la notion d'<code>injection de dépendances</code>. Nous verrons quelques cas usuels d'utilisation de l'<code>injection des dépendances</code> dans nos projets :</p>
<ul>
<li><h3 id="heading-utilisation-des-classes-router-activatedroute-et-httpclient">Utilisation des classes Router, ActivatedRoute et HttpClient :</h3>
</li>
</ul>
<p>Pour créer des instances de ces classes après avoir bien-sûr déclaré leurs modules là où il le faut, voici ce que nous faisons généralement :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> MyComponent {
    <span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> router: Router, <span class="hljs-keyword">private</span> route: ActivatedRoute</span>) {
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> MyService() {
    <span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> http: HttpClient</span>) {
  }
}
</code></pre>
<p>Résultat, nous ne nous soucions pas de comment ces instances sont initialisées. C'est de l'<code>injection de dépendances</code>.</p>
<ul>
<li><h3 id="heading-creation-dun-service">Création d'un service :</h3>
</li>
</ul>
<p>Lorsque nous créons nos propres services, voici comment nous les utilisons dans nos composants :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> MyComponent {
    <span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> myService: MyService</span>) {
  }
}
</code></pre>
<p>En faisant ainsi, nous exploitons la notion d'<code>injection de dépendances</code>. En effet, si nous remarquons bien lors de la création de nos services, nous avons le décorateur <code>@Injectable()</code> qui nous permet d'injecter notre service là où nous désirons l'utiliser. Généralement, nous avons ceci :</p>
<pre><code class="lang-typescript"><span class="hljs-meta">@Injectable</span>({ provideIn: <span class="hljs-string">'root'</span> })
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> MyService() {
    <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
  }
}
</code></pre>
<ul>
<li><code>provideIn</code> p<strong><em>ermet de préciser la portée de notre service. Lorsque nous indiquons</em></strong> <code>'root'</code> <strong><em>le service est créé et disponible au lancement de l'application. Nous avons aussi d'autres valeurs à savoir</em></strong> <code>'platform'</code><strong><em>,</em></strong> <code>'any'</code> <strong><em>et</em></strong> <code>null</code><strong><em>, qui suivant le cas, nous amène à utiliser le tableau</em></strong> <code>providers</code><strong><em>.</em></strong></li>
</ul>
<p>Pour finir, nous pouvons également faire de <code>l'injection de dépendance</code> en utilisant la fonciton <code>inject</code> qui a subi des mises à jour depuis <strong><em>Angular 14</em></strong>:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">/* au lieu de faire ceci : */</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> MyService() {
    <span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> http: HttpClient</span>) {
  }
}


<span class="hljs-comment">/* nous pouvons faire : */</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> MyService() {
   http = inject(HttpClient);
}
</code></pre>
<p>En somme, nous avons à présent une idée de comment Angular raisonne par rapport à <code>l'injection de dépendance</code> et nous comprenons davantage pourquoi nous sommes autorisés à utiliser certaines classes sans le mot clé <code>new</code> .</p>
<blockquote>
<p>Pour plus d'informations :</p>
<p>👉 <a target="_blank" href="https://angular.io/guide/dependency-injection">Understanding Dependency Injection</a></p>
<p>👉 <a target="_blank" href="https://angular.io/guide/dependency-injection-overview">Dependency Injection Overview in Angular</a></p>
<p>👉 <a target="_blank" href="https://angular.io/guide/creating-injectable-service">Creating Injectable Service</a></p>
<p>👉 <a target="_blank" href="https://angular.io/api/core/inject">Inject Function</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Comprendre le lazy-loading au niveau des modules en Angular]]></title><description><![CDATA[Lorsque nous développons des applications web, nous sommes conviés à créer beaucoup de composants pour gérer les interfaces utilisateur. Suivant les actions des utilisateurs, nous les autorisons à naviguer de page en page sur nos applications au moye...]]></description><link>https://angulardev.fr/comprendre-le-lazy-loading-au-niveau-des-modules-en-angular</link><guid isPermaLink="true">https://angulardev.fr/comprendre-le-lazy-loading-au-niveau-des-modules-en-angular</guid><category><![CDATA[Angular]]></category><category><![CDATA[lazy loading]]></category><category><![CDATA[modules]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Tue, 02 May 2023 07:40:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/imAfCYq7KH0/upload/c38e3094583ab4cce27e3ae42cb9641a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Lorsque nous développons des applications web, nous sommes conviés à créer beaucoup de composants pour gérer les interfaces utilisateur. Suivant les actions des utilisateurs, nous les autorisons à naviguer de page en page sur nos applications au moyen d'un système de routes.</p>
<p>Supposons que tous nos composants sont déclarés dans un seul module à savoir <code>AppModule</code> par exemple, ce qui implique que nous avons un seul fichier de routes qui n'est rien d'autre que <code>AppRoutingModule</code>. <strong><em>Lorsque nous créons le build final du projet, nous remarquons la création d'un seul fichier Javascript qui contient toute la logique métier de notre application.</em></strong> En effet, au lancement de notre application web, tous les composants visibles comme invisibles à l'utilisateur sont chargés dans le navigateur ce qui crée des latences lorsque nous avons une grosse application. Cette latence est due au fait que le navigateur doit télécharger tout le bundle (<em>fichier de build Javascript</em>) avant d'afficher la première interface. Résultat, nous faisons face à des soucis d'expérience utilisateur.</p>
<blockquote>
<p>Maintenant que le décor est bien planté, nous verrons comment gérer ça 😁</p>
</blockquote>
<p>Comme vous l'aurez déjà deviné, la core team de Angular s'est penché sur le problème et ils ont trouvé une solution. Ils se sont dits ceci :</p>
<blockquote>
<p>Pourquoi ne pas permettre le chargement des composants uniquement lorsque l'utilisateur demande à y avoir accès ? 🤔</p>
<p>C'est ainsi qu'est né le <code>lazy-loading</code> (<strong><em>chargement paresseux</em></strong> en français) au niveau des modules.</p>
</blockquote>
<p>Pour implémenter cette technique, il faut que chacun des composants que nous voulons rendre paresseux au chargement soit associé à un module et bien évidemment autre que le module de base. En bref, chaque composant avec son module comme l'indique cette image :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1683011924952/c78d4fd3-68e2-4617-86e1-669183ac2151.png" alt class="image--center mx-auto" /></p>
<p>Par la suite, dans notre fichier de route (<code>AppRoutingModule</code> par exemple), nous chargerons notre composant de façon dynamique comme ceci :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> routes: Routes = [
  {
    path: <span class="hljs-string">'customers'</span>,
    loadChildren: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./customers/customers.module'</span>).then(<span class="hljs-function"><span class="hljs-params">m</span> =&gt;</span> m.CustomersModule)
  }
];
</code></pre>
<ul>
<li><p><code>loadChildren</code> <strong><em>permet le chargement dynamique ou asynchrone ou encore paresseux de notre composant à travers son module.</em></strong></p>
</li>
<li><p><strong><em>Nous allons ensuite mettre en place un système de route dans notre composant</em></strong> <code>customers</code> <strong><em>et plus précisement dans un module de route propre à lui qui est</em></strong> <code>CustomersRoutingModule</code> :</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">const</span> routes: Routes = [
    {
      path: <span class="hljs-string">''</span>,
      component: CustomersComponent
    }
  ];
</code></pre>
</li>
</ul>
<p>En réalité, nous n'avons pas besoin de le faire manuellement. Il existe une ligne de commande qui fait le job à notre place :</p>
<pre><code class="lang-typescript">ng generate <span class="hljs-keyword">module</span> customers --route customers --<span class="hljs-keyword">module</span> app.<span class="hljs-keyword">module</span>
</code></pre>
<blockquote>
<p>Cette commande nous créera un dossier avec six (06) fichiers à savoir :</p>
<ul>
<li><p><code>customers-routing.module.ts</code></p>
</li>
<li><p><code>customers.component.html</code></p>
</li>
<li><p><code>customers.component.scss</code></p>
</li>
<li><p><code>customers.component.spec.ts</code></p>
</li>
<li><p><code>customers.component.ts</code></p>
</li>
<li><p><code>customers.module.ts</code></p>
</li>
</ul>
<p>et rajoutera une route dans notre <code>AppRoutingModule</code> qui sera <code>lazy-loading</code> .</p>
</blockquote>
<p>Pour finir, si nous créons un nouveau build, <strong><em>nous remarquerons l'existence d'un nombre de fichiers Javascript équivalent au nombre de chargement paresseux que nous avons implémenté dans notre fichier de routes</em></strong> <code>AppRoutingModule</code>. Ces fichiers sont des <code>chunks</code> c'est-à-dire qu'ils contiennent du code associé au module. Au lancement de l'application, seuls les composants qui sont destinés à apparaître sous les yeux de l'utilisateur se chargeront. Le constat est flagrant en inspectant le navigateur et en allant sur l'option <code>Réseau</code>. Au fur et à mesure que l'utilisateur décide de visiter une page, le chunk associé à cette page est téléchargé dans le navigateur. Résultat, l'application est davantage plus rapide.</p>
<p>En somme, il est judicieux de penser à mettre le <code>lazy-loading</code> dans nos applications si nous devons gérer un système de routes. Cela nous conforterait dans la rapidité d'affichage de nos applications au lancement surtout si l'utilisateur n'a pas une bonne connexion Internet. Nous avons toutefois la possibilité de définir la façon dont nous voulons effectuer notre chargement paresseux à travers des stratégies.</p>
<blockquote>
<p>Pour plus d'informations 👉 <a target="_blank" href="https://angular.io/guide/lazy-loading-ngmodules#lazy-loading-feature-modules">Lazy-loading feature modules</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Partager des informations entre composants sans utiliser @Output() et les services]]></title><description><![CDATA[En Angular, lorsque nous voulons partager des informations entre les composants, nous utilisons généralement le décorateur @Output() ou les services. Le décorateur @Output() nous permet de partager des données d'un composant enfant vers un composant ...]]></description><link>https://angulardev.fr/partager-des-informations-entre-composants-sans-utiliser-output-et-les-services</link><guid isPermaLink="true">https://angulardev.fr/partager-des-informations-entre-composants-sans-utiliser-output-et-les-services</guid><category><![CDATA[Angular]]></category><category><![CDATA[input output]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Thu, 27 Apr 2023 07:05:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/FBNxmwEVpAc/upload/e79e6abf764ad6e0076749480be17b79.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>En Angular, lorsque nous voulons partager des informations entre les composants, nous utilisons généralement le décorateur <code>@Output()</code> ou les services. Le décorateur <code>@Output()</code> nous permet de partager des données d'un composant enfant vers un composant parent. Pour ce qu'il en est des services, ils nous permettent d'avoir accès à une information peu importe la relation entre les composants.</p>
<blockquote>
<p><em>Et si vous appreniez qu'il existe un autre moyen beaucoup plus simple de le faire ?<br />Ah bon ? 😁</em></p>
</blockquote>
<p>Effectivement, il existe une autre façon encore plus simple d'avoir accès aux informations d'un composant depuis un autre composant.</p>
<blockquote>
<p>Il s'agit de l'attribut <code>exportAs</code>. <strong><em>C'est un attribut utilisé sur les directives et donc valable également sur nos composants car un composant est avant tout une</em></strong> <code>Directive</code><strong><em>.</em></strong></p>
</blockquote>
<p>Supposons que nous ayons un composant <code>PersonComponent</code> qui permet à l'utilisateur de choisir son genre :</p>
<pre><code class="lang-typescript"><span class="hljs-meta">@Component</span>({
  selector: <span class="hljs-string">'app-person'</span>,
  template: <span class="hljs-string">`
    &lt;input type="radio" name="gender" [(ngModel)]="myGender" value="male" /&gt; Male
    &lt;input type="radio" name="gender" [(ngModel)]="myGender" value="female" /&gt; Female
  `</span>,
  exportAs: <span class="hljs-string">'personContext'</span>
})

<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> PersonComponent {
    <span class="hljs-keyword">public</span> myGender: <span class="hljs-built_in">string</span>;
}
</code></pre>
<ul>
<li><code>exportAs: 'personContext'</code> <strong><em>permet d'indiquer que</em></strong> <code>personContext</code> <strong><em>fait référence à la classe</em></strong> <code>PersonComponent</code> <strong><em>lorsque nous voulons l'utiliser dans notre template HTML au moyen d'une variable de référence.</em></strong></li>
</ul>
<p>Par la suite, nous avons un autre composant <code>AvatarComponent</code> qui est chargé d'afficher un avatar en fonction du genre de la personne :</p>
<pre><code class="lang-typescript"><span class="hljs-meta">@Component</span>({
  selector: <span class="hljs-string">'app-avatar'</span>,
  template: <span class="hljs-string">`
   &lt;img *ngIf="gender === 'male'" src="avatarMale.jpg" /&gt;
   &lt;img *ngIf="gender === 'female'" src="avatarFemale.jpg" /&gt;
  `</span>,
})

<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> AvatarComponent {
   <span class="hljs-meta">@Input</span>() gender: <span class="hljs-built_in">string</span>;
}
</code></pre>
<p>Pour finir, dans notre composant parent <code>AppComponent</code> par exemple, nous aimerions interagir entre le composant <code>PersonComponent</code> et le composant <code>AvatarComponent</code>. Voici ce qu'il faut faire :</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">app-avatar</span> [<span class="hljs-attr">gender</span>]=<span class="hljs-string">"personContext.myGender"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">app-avatar</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">app-person</span> #<span class="hljs-attr">person</span>=<span class="hljs-string">"personContext"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">app-person</span>&gt;</span>
</code></pre>
<ul>
<li><p><code>#person='personContext'</code> est <em>la variable de référence</em> <code>person</code> qui <em>a pour valeur</em> <code>personContext</code> et <em>qui n'est rien d'autre que la référence de notre composant</em> <code>PersonComponent</code>.</p>
</li>
<li><p><code>personContext.myGender</code> permet d'avoir accès à la valeur de la variable <code>myGender</code> présent dans le composant <code>PersonComponent</code>.</p>
</li>
</ul>
<blockquote>
<p><strong><em>L'attribut</em></strong> <code>exportAs</code><strong><em>, nous permet d'avoir accès aux attributs et méthodes publiques de notre composant afin d'interagir avec eux. Nous remarquons que l'information</em></strong> <code>myGender</code> <strong><em>est partagée entre les deux composants sans que nous ayons besoin de</em></strong> <code>@Output()</code> <strong><em>ou d'un service.</em></strong></p>
</blockquote>
<p>En somme, <code>exportAs</code> est un attribut qui nous évite d'injecter un service ou un composant pour partager des informations entre nos composants ainsi que l'utilisation du décorateur <code>@Output()</code>.</p>
<blockquote>
<p>Pour plus d'informations👉 <a target="_blank" href="https://angular.io/api/core/Directive#exportAs">exportAs</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Comment accéder aux informations de l'en-tête (header) de la réponse renvoyée après l'exécution d'une requête HTTP en Angular ?]]></title><description><![CDATA[Dans nos applications Angular, nous sommes amenés à consommer des APIs (Application Programming Interface) fournies par un service backend via le protocole HTTP (HyperText Transfer Protocol). Pour le faire, nous implémentons le service HttpClient dis...]]></description><link>https://angulardev.fr/comment-acceder-aux-informations-de-len-tete-header-de-la-reponse-renvoyee-apres-lexecution-dune-requete-http-en-angular</link><guid isPermaLink="true">https://angulardev.fr/comment-acceder-aux-informations-de-len-tete-header-de-la-reponse-renvoyee-apres-lexecution-dune-requete-http-en-angular</guid><category><![CDATA[Angular]]></category><category><![CDATA[http]]></category><category><![CDATA[HttpClient Angular]]></category><dc:creator><![CDATA[Ezéchiel Amen AGBLA]]></dc:creator><pubDate>Mon, 24 Apr 2023 07:14:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/SyYmXSDnJ54/upload/0a1ad196944f4281ccb29a3eca8db628.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Dans nos applications <code>Angular</code>, nous sommes amenés à consommer des <code>APIs</code> (<strong><em>Application Programming Interface</em></strong>) fournies par un service backend via le protocole <code>HTTP</code> (<strong><em>HyperText Transfer Protocol</em></strong>). Pour le faire, nous implémentons le service <code>HttpClient</code> disponible dans le noyau de notre framework. Ce service <code>HttpClient</code> nous permet de faire énormément de choses et nous aborderons l'une d'entre elle dans cet article.</p>
<p>Supposons que nous ayons besoin de récupérer une information qui se trouve dans l'en-tête (<code>headers</code> en anglais) de la réponse à notre requête. En effet, pour des raisons de sécurité ou un besoin particulier, notre backend décide de mettre des données au niveau de l'en-tête de la réponse à notre requête. <em>Cette information peut être un token</em> <code>JWT</code> <em>(<strong><strong>JSON Web Token</strong></strong>), un</em> <code>cookie</code> <em>ou toute autre information cruciale pour notre logique métier</em>.</p>
<blockquote>
<p><strong>Le service</strong> <code>HttpClient</code> <strong>de</strong> <code>Angular</code> <strong>nous offre une interface appelée</strong> <code>HttpInterceptor</code> <strong>qui nous permet de manipuler aisément nos requêtes avant, pendant et après leur exécution.</strong> C'est une interface très utilisée et très utile.</p>
</blockquote>
<p>Voici les étapes à suivre pour récupérer une information qui se trouve au niveau de l'en-tête de la réponse :</p>
<p>1- <strong><em>Créer une classe qui implémente l'interface</em></strong> <code>HttpInterceptor</code> <strong><em>:</em></strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Injectable } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/core'</span>;
<span class="hljs-keyword">import</span> {
  HttpInterceptor,
  HttpRequest,
  HttpHandler,
  HttpEvent
} <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/common/http'</span>;

<span class="hljs-meta">@Injectable</span>({
  providedIn: <span class="hljs-string">'root'</span>,
})

<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> RequestInterceptor <span class="hljs-keyword">implements</span> HttpInterceptor {

  intercept(request: HttpRequest&lt;<span class="hljs-built_in">any</span>&gt;, 
            next: HttpHandler): Observable&lt;HttpEvent&lt;<span class="hljs-built_in">any</span>&gt;&gt; {
  } 
}
</code></pre>
<ul>
<li><p><code>intercept</code> : <em>c'est la méthode que nous redéfinissons à travers l'interface</em> <code>HttpInterceptor</code> <em>qui nous permet d'intercepter une requête afin d'effectuer nos traitements.</em></p>
</li>
<li><p><code>request</code> : <em>c'est l'objet qui représente notre requête. Nous y trouverons toutes les informations en rapport avec la requête à envoyer vers notre serveur.</em></p>
</li>
<li><p><code>next</code> : <em>c'est l'objet qui nous permet de passer à l'étape suivante après nos différents traitements afin que notre requête puisse continuer son exécution après avoir été intercepté et subi des traitements.</em></p>
</li>
</ul>
<p>2- <strong><em>Indiquer à notre module root qu'il doit utiliser cet intercepteur de requête</em></strong> :</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { HTTP_INTERCEPTORS } <span class="hljs-keyword">from</span> <span class="hljs-string">'@angular/common/http'</span>;
....
providers: [
....
 { provide: HTTP_INTERCEPTORS, useClass: RequestInterceptor, multi: <span class="hljs-literal">true</span> }
....
]
</code></pre>
<ul>
<li><p><code>provide</code> : cet attribut nous permet d'indiquer que le service à exploiter c'est <code>HTTP_INTERCEPTORS</code>.</p>
</li>
<li><p><code>useClass</code> : cet attribut nous permet d'indiquer la classe à utiliser lorsque nous voulons exploiter le service <code>HTTP_INTERCEPTORS</code>. Cette classe dans notre cas est <code>RequestInterceptor</code>.</p>
</li>
<li><p><code>multi</code> : cet attribut avec sa valeur <code>true</code> nous permet de faire pointer différentes classes sur notre service à exploiter <code>HTTP_INTERCEPTORS</code>. Nous pouvons avoir plusieurs classes d'intercepteur de requêtes qui font différents traitements.</p>
</li>
</ul>
<p>3- <strong><em>Ajouter la logique suivante au niveau de la méthode</em></strong> <code>intercept</code> <strong><em>pour avoir accès aux informations de l'en-tête de la réponse à notre requête HTTP</em></strong> :</p>
<pre><code class="lang-typescript">  intercept(request: HttpRequest&lt;<span class="hljs-built_in">any</span>&gt;, 
            next: HttpHandler): Observable&lt;HttpEvent&lt;<span class="hljs-built_in">any</span>&gt;&gt; {
  <span class="hljs-keyword">return</span> next.handle(request).pipe( 
    tap(<span class="hljs-function">(<span class="hljs-params">event: HttpEvent&lt;<span class="hljs-built_in">any</span>&gt;</span>) =&gt;</span> { 
      <span class="hljs-keyword">if</span> (event <span class="hljs-keyword">instanceof</span> HttpResponse) { 
           <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Http response headers : '</span>, event.headers); 
      } 
    }), 
    catchError(<span class="hljs-function">(<span class="hljs-params">error: HttpErrorResponse</span>) =&gt;</span> { 
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Error '</span>, error); 
      <span class="hljs-keyword">return</span> throwError(error); 
    }) 
  );
}
</code></pre>
<ul>
<li><p><code>tap</code> : <em>cet opérateur</em> <code>RxJS</code> <em>nous permet de capturer un évènement</em> <code>HttpEvent</code> <em>afin de tester si cet évènement est de type</em> <code>HttpResponse</code><em>. C'est important parce que les évènements</em> <code>HTTP</code> <em>sont multiples.</em></p>
</li>
<li><p><code>catchError</code> : <em>cet opérateur</em> <code>RxJS</code> <em>nous permet de capturer les erreurs provenant de notre requête afin de les traiter convenablement.</em></p>
</li>
</ul>
<p>En somme, nous avons vu ensemble comment récupérer les informations contenues dans l'en-tête d'une réponse <code>HTTP</code> en utilisant un intercepteur de requêtes. Le service <code>HttpClient</code> que <code>Angular</code> met à notre disposition est tellement puissant qu'il faut prendre le temps de bien l'étudier.</p>
<blockquote>
<p>Pour plus d'informations :</p>
<p>👉<a target="_blank" href="https://angular.io/api/common/http/HttpInterceptor">HttpInterceptor</a></p>
<p>👉 <a target="_blank" href="https://angular.io/api/common/http/HttpRequest">HttpRequest</a></p>
<p>👉 <a target="_blank" href="https://angular.io/api/common/http/HttpResponse">HttpResponse</a></p>
<p>👉 <a target="_blank" href="https://angular.io/api/common/http/HttpErrorResponse">HttpErrorResponse</a></p>
</blockquote>
]]></content:encoded></item></channel></rss>