Fermer

octobre 31, 2022

Comprendre pas à pas le canal asynchrone


Le canal asynchrone peut faire une énorme différence dans votre stratégie de détection des changements pour votre application Angular. Si cela vous a dérouté jusqu’à présent, suivez cette explication étape par étape. Nous le comprendrons ensemble !

Dans Angular, le tube asynchrone est un tube qui effectue essentiellement ces trois tâches :

  • Il souscrit à une observable ou une promesse et renvoie la dernière valeur émise.
  • Chaque fois qu’une nouvelle valeur est émise, elle marque le composant à vérifier. Cela signifie qu’Angular exécutera Change Detector pour ce composant lors du prochain cycle.
  • Il se désabonne de l’observable lorsque le composant est détruit.

De plus, comme bonne pratique, il est conseillé d’essayer d’utiliser un composant sur la stratégie de détection de changement onPush avec un canal asynchrone pour s’abonner aux observables.

Si vous êtes un débutant dans Angular, peut-être que l’explication ci-dessus du tuyau asynchrone est écrasante. Dans cet article, nous allons donc essayer de comprendre le tube asynchrone étape par étape à l’aide d’exemples de code. Créez simplement un nouveau projet Angular et suivez-le; à la fin de l’article, vous devriez avoir une compréhension pratique du tube asynchrone.

Créer un service

Commençons par créer une interface produit et un service.

export interface IProduct {

     Id : string; 
     Title : string; 
     Price : number; 
     inStock : boolean;

}

Après avoir créé le IProduct interface, créez ensuite un tableau de IProduct à l’intérieur du service Angular pour effectuer des opérations de lecture et d’écriture.

import { Injectable } from '@angular/core';
import { IProduct } from './product.entity';

@Injectable({
  providedIn: 'root'
})
export class AppService {

  Products : IProduct[] = [
    {
      Id:"1",
      Title:"Pen",
      Price: 100,
      inStock: true 
    },
    {
      Id:"2",
      Title:"Pencil",
      Price: 200,
      inStock: false 
    },
    {
      Id:"3",
      Title:"Book",
      Price: 500,
      inStock: true 
    }
  ]

  constructor() { }
}

N’oubliez pas que dans les applications réelles, vous obtenez des données à partir d’une API ; cependant, ici, nous imitons les opérations de lecture et d’écriture dans le tableau local pour nous concentrer sur le canal asynchrone.

Pour effectuer des opérations de lecture et d’écriture, enveloppons le tableau Products dans un BehaviorSubject et émettre un nouveau tableau chaque fois qu’un nouvel élément est poussé vers le Products déployer.

Pour ce faire, ajoutez du code dans le service comme indiqué ci-dessous :

  Products$ : BehaviorSubject<IProduct[]>; 
  constructor() {
    this.Products$ = new BehaviorSubject<IProduct[]>(this.Products);
   }
  
   AddProduct(p: IProduct): void{
    this.Products.push(p);
    this.Products$.next(this.Products);
   }

Passons en revue le code :

  1. ComportementSujet est un type de sujet qui émet une valeur par défaut ou la dernière valeur émise. Nous utilisons BehaviorSubject pour émettre la valeur par défaut Products tableau initialement.
  2. Dans le AddProduct méthode, nous passons un produit et le poussons vers le tableau.
  3. Dans le AddProduct méthode, après avoir poussé un élément dans le tableau Products, nous émettons la mise à jour Products déployer.

A partir de maintenant, le service est prêt. Ensuite, nous allons créer deux composants : un pour ajouter un produit et un pour afficher tous les produits sur une table.

Ajout d’un produit

Créez un composant appelé le AddProduct composant et ajoutez un formulaire réactif pour accepter les informations sur le produit.

  productForm: FormGroup;
  constructor(private fb: FormBuilder, private appService: AppService) {
    this.productForm = this.fb.group({
      Id: ["", Validators.required],
      Title: ["", Validators.required],
      Price: [],
      inStock: []
    })
  }

Nous utilisons FormBuilder service pour créer le FormGroup et sur le gabarit du composant en utilisant productForm avec le formulaire HTML comme indiqué ci-dessous :

<form (ngSubmit)='addProduct()' [formGroup]='productForm'>
    <input formControlName='Id' type="text" class="form-control" placeholder="Enter ID" />
    <input formControlName='Title' type="text" class="form-control" placeholder="Enter Title" />
    <input formControlName='Price' type="text" class="form-control" placeholder="Enter Price" />
    <input formControlName='inStock' type="text" class="form-control" placeholder="Enter Stock " />
    <button [disabled]='productForm.invalid' class="btn btn-default">Add Product</button>
</form>

Et dans le AddProduct fonction, nous vérifierons si le formulaire est valide. Si oui, nous appelons le service pour pousser un produit vers le tableau Products. La AddProduct la fonction devrait ressembler à ci-dessous :

  addProduct() {
    if (this.productForm.valid) {
      this.appService.AddProduct(this.productForm.value);
    }
  }

Jusqu’à présent, nous avons créé un composant qui contient un formulaire réactif pour entrer des informations sur le produit et appeler le service pour insérer un nouveau produit dans le tableau Produits. Le code ci-dessus devrait être simple si vous avez travaillé sur Angular.

Liste des produits

Après avoir ajouté un composant à la liste des produits, suivez les étapes habituelles :

  • Met le Changer la stratégie de détection du composant par défaut.
  • Injectez AppService dans le composant.
  • Utilisez la méthode subscribe pour récupérer les données de l’observable.
@Component({
  selector: 'app-list-products',
  templateUrl: './list-products.component.html',
  styleUrls: ['./list-products.component.css'],
  changeDetection: ChangeDetectionStrategy.Default
})
export class ListProductsComponent implements OnInit, OnDestroy {

  products: IProduct[] = []
  productSubscription?: Subscription
  constructor(private appService: AppService) { }

  productObserver = {
    next: (data: IProduct[]) => { this.products = data; },
    error: (error: any) => { console.log(error) },
    complete: () => { console.log('product stream completed ') }
  }
  ngOnInit(): void {
    this.productSubscription = this.appService.Products$.subscribe(this.productObserver)
  }

  ngOnDestroy(): void {
    if (this.productSubscription) {
      this.productSubscription.unsubscribe();
    }
  }
}

Parcourons le code :

  • La products La variable contient le tableau renvoyé par le service.
  • La productSubscription est une variable de type d’abonnement RxJS pour affecter l’abonnement renvoyé par la méthode d’abonnement de l’observable.
  • La productObserver est un objet avec les fonctions de rappel suivant, d’erreur et complète.
  • La productObserver observer est passé à la méthode subscribe.
  • Dans le ngOnDestrory() life cyclehook, on se désabonne de l’observable.

Sur le modèle, vous pouvez afficher les produits dans un tableau comme indiqué ci-dessous :

<table>
    <thead>
        <tr>
            <th>Id</th>
            <th>Title</th>
            <th>Price</th>
            <th>inStock</th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor="let p of products">
            <td>{{p.Id}}</td>
            <td>{{p.Title}}</td>
            <td>{{p.Price}}</td>
            <td>{{p.inStock}}</td>
        </tr>
    </tbody>
</table>

Utilisation des composants

Nous utiliserons ces deux composants comme composants frères, comme indiqué ci-dessous.

<h1>{{title}}</h1>

<app-add-product></app-add-product>

<hr/>
<app-list-products></app-list-products>

Un point critique que vous devriez remarquer ici est que le AddProduct composant et le ListProducts composant ne sont pas liés. Il n’y a que deux façons de se transmettre des données :

  1. En communiquant via le composant parent
  2. Par communication à l’aide d’un service

Nous avons déjà créé un service et nous l’utiliserons pour transmettre des informations sur les produits entre ces deux composants.

Exécutez l’application

Lors de l’exécution de l’application, vous devriez obtenir la sortie ci-dessous.

L'utilisateur peut ajouter un produit avec quatre champs : Id, Title, Price, InStock.  Puis une liste de quatre produits : stylo, crayon, livre, couleur

Comme vous le constatez, vous pouvez ajouter un produit en cliquant sur le bouton Ajouter un produit. Cela appelle une fonction dans le service, qui met à jour le tableau et émet un tableau mis à jour à partir de l’observable.

Le composant où les produits sont répertoriés s’abonne à l’observable, donc chaque fois que nous ajoutons un autre élément, la table se met à jour. Jusqu’ici tout va bien.

Utilisation de la stratégie de détection de changement onPush

Si vous vous rappelez ListProducts composant Changer la stratégie de détection est réglé sur défaut. Maintenant, allons de l’avant et changeons la stratégie pour onPush:

Changer la stratégie de détection onPush

Et encore une fois, lancez l’application. Qu’as-tu trouvé? Comme vous l’avez remarqué à juste titre, lorsque vous ajoutez un produit de la AddProduct composant, il est ajouté au tableau, et même le tableau mis à jour est émis par le service. Pourtant, le
ListProducts le composant n’est pas mis à jour. Cela se produit parce que le ListProducts la stratégie de détection des modifications du composant est définie sur onPush.

Changer la stratégie de détection de changement en onPush empêche le tableau d’être actualisé avec de nouveaux produits.

Pour un composant avec un onPush stratégie de détection de changement, Angular exécute le détecteur de changement uniquement lorsqu’une nouvelle référence est transmise au composant. Cependant, lorsqu’un observable émet un nouvel élément, il ne donne pas de nouvelle référence. Par conséquent, Angular n’exécute pas le détecteur de changement et le tableau Products mis à jour n’est pas projeté dans le composant.

Vous pouvez en savoir plus sur Détecteur de changement angulaire ici.

Comment pouvons-nous y remédier ?

Nous pouvons résoudre ce problème en appelant manuellement le détecteur de changement. Pour ce faire, injectez ChangeDetectorRef dans le composant et appeler le markForCheck() méthode.

export class ListProductsComponent implements OnInit, OnDestroy {

  products: IProduct[] = []
  productSubscription?: Subscription
  constructor(private appService: AppService, 
    private cd: ChangeDetectorRef) {

   }

  productObserver = {
    next: (data: IProduct[]) => {
       this.products = data; 
      this.cd.markForCheck(); 
    },
    error: (error: any) => { console.log(error) },
    complete: () => { console.log('product stream completed ') }
  }
  ngOnInit(): void {
    this.productSubscription = this.appService.Products$.subscribe(this.productObserver)
  }

  ngOnDestroy(): void {
    if (this.productSubscription) {
      this.productSubscription.unsubscribe();
    }
  }
}

Ci-dessus, nous avons effectué les tâches suivantes :

  • Nous avons injecté Angular ChangeDetectorRef au composant.
  • La markForCheck() marque ce composant et tous ses parents comme sales afin qu’Angular vérifie les modifications lors du prochain cycle de détection des modifications.

Maintenant, lors de l’exécution de l’application, vous devriez pouvoir voir le tableau de produits mis à jour.

Analyse de l’approche d’abonnement

Comme vous l’avez vu, dans le composant défini sur onPushpour travailler avec des observables, vous suivez les étapes ci-dessous.

  1. Abonnez-vous à l’observable.
  2. Exécutez la détection de changement manuellement.
  3. Se désabonner de l’observable.

Abonnez-vous à l'observable.  Exécutez la détection de changement manuellement.  Se désabonner de l'observable.

Avantages de la subscribe() approche sont :

  • La propriété peut être utilisée à plusieurs endroits dans le modèle.
  • La propriété peut être utilisée à divers emplacements dans la classe de composants.
  • Vous pouvez exécuter une logique métier personnalisée lors de l’abonnement à l’observable.

Certains des inconvénients sont :

  • Pour le onPush stratégie de détection de changement, vous devez marquer manuellement le composant pour exécuter le détecteur de changement à l’aide de la markForCheck méthode.
  • Vous devez vous désabonner explicitement des observables.

Cette approche peut devenir incontrôlable lorsque de nombreux observables sont utilisés dans le composant. Si nous manquons de désabonner un observable, il peut y avoir des fuites de mémoire potentielles, etc.

Les problèmes ci-dessus peuvent être résolus en utilisant le canal asynchrone.

Le tuyau asynchrone

Le canal asynchrone est une méthode meilleure et plus recommandée pour travailler avec des observables dans un composant. Sous le capot, le canal asynchrone effectue ces trois tâches :

  1. Il s’abonne à l’observable et émet la dernière valeur émise.
  2. Lorsqu’une nouvelle valeur est émise, elle marque le composant à vérifier pour les changements.
  3. Le canal asynchrone se désabonne automatiquement lorsque le composant est détruit pour éviter les fuites de mémoire potentielles.

Canal asynchrone au centre avec des lignes pour s'abonner, exécuter un détecteur de changement, se désabonner

Donc, fondamentalement, le canal asynchrone effectue les trois tâches que vous faisiez manuellement pour l’approche d’abonnement.

Modifions le ListProducts composant pour utiliser le canal asynchrone.

@Component({
  selector: 'app-list-products',
  templateUrl: './list-products.component.html',
  styleUrls: ['./list-products.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ListProductsComponent implements OnInit {

  products?: Observable<IProduct[]>;
  constructor(private appService: AppService) {}
  ngOnInit(): void {
    this.products = this.appService.Products$;
  }
}

Nous avons supprimé tout le code et attribué les rendements observables du service à la variable products. Sur le modèle pour restituer les données maintenant, utilisez le canal asynchrone.

<table>
    <thead>
        <tr>
            <th>Id</th>
            <th>Title</th>
            <th>Price</th>
            <th>inStock</th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor="let p of products | async">
            <td>{{p.Id}}</td>
            <td>{{p.Title}}</td>
            <td>{{p.Price}}</td>
            <td>{{p.inStock}}</td>
        </tr>
    </tbody>
</table>

L’utilisation du canal asynchrone permet de garder le code plus propre et vous n’avez pas besoin d’exécuter manuellement le détecteur de changement pour la stratégie onPush Change Detection. Sur l’application, vous voyez que le ListProducts Le composant est restitué chaque fois qu’un nouveau produit est ajouté.

Il est toujours recommandé et la meilleure pratique de :

  1. Gardez la stratégie de détection de changement de composant sur onPush
  2. Utiliser le canal asynchrone pour travailler avec des observables

J’espère que vous trouverez cet article utile et que vous êtes maintenant prêt à utiliser le canal asynchrone dans votre projet Angular.




Source link

octobre 31, 2022