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 :
- 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éfautProducts
tableau initialement. - Dans le
AddProduct
méthode, nous passons un produit et le poussons vers le tableau. - Dans le
AddProduct
méthode, après avoir poussé un élément dans le tableau Products, nous émettons la mise à jourProducts
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 :
- En communiquant via le composant parent
- 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.
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:
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 onPush
pour travailler avec des observables, vous suivez les étapes ci-dessous.
- 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 lamarkForCheck
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 :
- Il s’abonne à l’observable et émet la dernière valeur émise.
- Lorsqu’une nouvelle valeur est émise, elle marque le composant à vérifier pour les changements.
- Le canal asynchrone se désabonne automatiquement lorsque le composant est détruit pour éviter les fuites de mémoire potentielles.
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 :
- Gardez la stratégie de détection de changement de composant sur onPush
- 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