Fermer

janvier 30, 2020

Comment créer un jeu d'association de cartes en utilisant Angular et RxJS


À propos de l'auteur

Anna Prenzel a étudié l'informatique à l'Université des sciences appliquées de Zittau / Görlitz et a obtenu son doctorat au BTU Cottbus-Senftenberg.

Elle est…
En savoir plus sur
Anna

Cet article est dédié aux développeurs Angular qui souhaitent exploiter le concept de programmation réactive. Il s'agit d'un style de programmation qui, en termes simples, traite du traitement des flux de données asynchrones.

Aujourd'hui, je voudrais me concentrer sur les flux de données résultant des événements de clic sur l'interface utilisateur. Le traitement de ces flux de clics est particulièrement utile pour les applications avec une interaction utilisateur intensive où de nombreux événements doivent être traités. Je voudrais également vous présenter un peu plus RxJS; c'est une bibliothèque JavaScript qui peut être utilisée pour exprimer les routines de gestion des événements de manière compacte et concise dans un style réactif.

Que construisons-nous?

Les jeux d'apprentissage et les questionnaires de connaissances sont populaires à la fois pour les utilisateurs plus jeunes et plus âgés. Un exemple est le jeu de «jumelage de paires», où l'utilisateur doit trouver des paires apparentées dans un mélange d'images et / ou d'extraits de texte.

L'animation ci-dessous montre une version simple du jeu: l'utilisateur sélectionne deux éléments sur le les côtés gauche et droit du terrain de jeu l'un après l'autre et dans n'importe quel ordre. Les paires correctement appariées sont déplacées vers une zone distincte du terrain de jeu, tandis que les affectations incorrectes sont immédiatement dissoutes afin que l'utilisateur doive faire une nouvelle sélection.

 Capture d'écran du jeu d'apprentissage "paires appariées"
aperçu du jeu que nous allons créer aujourd'hui

Dans ce tutoriel, nous allons construire un tel jeu d'apprentissage étape par étape. Dans la première partie nous allons construire un composant angulaire qui ne fait que montrer le terrain de jeu. Notre objectif est que le composant puisse être configuré pour différents cas d'utilisation et groupes cibles – d'un quiz animal à un entraîneur de vocabulaire dans une application d'apprentissage des langues. À cette fin, Angular propose le concept de projection de contenu avec des modèles personnalisables, que nous utiliserons. Pour illustrer le principe, je vais construire deux versions du jeu ("game1" et "game2") avec des dispositions différentes.

Dans la deuxième partie du tutoriel, nous nous concentrerons sur la programmation réactive. Chaque fois qu'une paire est appariée, l'utilisateur doit obtenir une sorte de rétroaction de l'application; c'est cette gestion d'événements qui est réalisée à l'aide de la bibliothèque RxJS.

1. Création d'un composant angulaire pour le jeu d'apprentissage

Comment créer le cadre de base

Commençons par créer un nouveau projet nommé «learning-app». Avec la CLI angulaire, vous pouvez le faire avec la commande ng new learning-app . Dans le fichier app.component.html je remplace le code source pré-généré comme suit:

L'apprentissage est amusant!

Dans l'étape suivante, le composant du jeu d'apprentissage est créé. Je l'ai appelé "match de jeu" et j'ai utilisé la commande ng générer un jeu de correspondance de composants . Cela créera un sous-dossier séparé pour le composant de jeu avec les fichiers HTML, CSS et Typescript requis.

Comme déjà mentionné, le jeu éducatif doit être configurable à différentes fins. Pour le démontrer, je crée deux composants supplémentaires ( game1 et game2 ) en utilisant la même commande. J'ajoute le composant de jeu en tant que composant enfant en remplaçant le code pré-généré dans le fichier game1.component.html ou game2.component.html par la balise suivante:


Au début, je n'utilise que le composant game1 . Afin de m'assurer que le jeu 1 s'affiche immédiatement après le démarrage de l'application, j'ajoute cette balise au fichier app.component.html :


Lors du démarrage de l'application avec ng serve - ouvert le navigateur affichera le message "Matching Game Works". (Il s'agit actuellement du seul contenu de matching-game.component.html .)

Maintenant, nous devons tester les données. Dans le dossier / app je crée un fichier nommé pair.ts où je définis la classe Pair :

 export class Pair Pair {
  partie gauche: chaîne;
  partie droite: chaîne;
  id: nombre;
}

Un objet paire comprend deux textes apparentés ( partie gauche et partie droite ) et un ID.

Le premier jeu est censé être un quiz sur les espèces dans lequel les espèces (par exemple chien ) doivent être affectés à la classe animale appropriée (c.-à-d. mammifère ).

Dans le fichier animaux.ts je définis un tableau avec des données de test :

 importez {Pair} de './pair';
export const ANIMALS: Pair [] = [
  { id: 1, leftpart: 'dog', rightpart: 'mammal'},
  { id: 2, leftpart: 'blickbird', rightpart: 'bird'},
  { id: 3, leftpart: 'spider', rightpart: 'insect'},
  { id: 4, leftpart: 'turtle', rightpart: 'reptile' },
  { id: 5, leftpart: 'guppy', rightpart: 'fish'},
];

Le composant game1 a besoin d'accéder à nos données de test. Ils sont stockés dans la propriété animaux . Le fichier game1.component.ts a maintenant le contenu suivant:

 import {Component, OnInit} de '@ angular / core';
importez {ANIMALS} depuis '../animals';
@Composant({
  sélecteur: 'app-game1',
  templateUrl: './game1.component.html',
  styleUrls: ['./game1.component.css']
})
la classe d'exportation Game1Component implémente OnInit {
  animaux = ANIMAUX;
  constructeur () {}
  ngOnInit () {
  }
}

La première version du composant de jeu

Notre prochain objectif: le composant de jeu match-game doit accepter les données de jeu du composant parent (par exemple game1 ) comme contribution. L'entrée est un tableau d'objets «paire». L'interface utilisateur du jeu doit être initialisée avec les objets transmis lors du démarrage de l'application.

 Capture d'écran du jeu d'apprentissage "paires correspondantes"

Pour cela, nous devons procéder comme suit:

  1. Ajouter le La propriété associe au composant de jeu en utilisant le décorateur @Input .
  2. Ajoutez les tableaux solvedPairs et unsolvedPairs comme propriétés privées supplémentaires de le composant. (Il est nécessaire de faire la distinction entre les paires déjà «résolues» et «pas encore résolues».)
  3. Au démarrage de l'application (voir fonction ngOnInit ) toutes les paires sont toujours «non résolues» et sont donc déplacées au tableau unsolvedPairs .
 import {Component, OnInit, Input} de '@ angular / core';
importer {Pair} à partir de '../pair';
@Composant({
  sélecteur: 'app-matching-game',
  templateUrl: './matching-game.component.html',
  styleUrls: ['./matching-game.component.css']
})

la classe d'exportation MatchingGameComponent implémente OnInit {
  @Input () pairs: Pair [];
  private solvedPairs: Pair [] = [];
  private unsolvedPairs: Pair [] = [];
  constructeur () {}
  ngOnInit () {
    pour (soit i = 0; i <this.pairs.length; i ++) {
        this.unsolvedPairs.push (this.pairs [i]);
    }
  }
}

De plus, je définis le modèle HTML du composant match-game . Il existe des conteneurs pour les paires non résolues et résolues. La directive ngIf garantit que le conteneur respectif ne s'affiche que s'il existe au moins une paire non résolue ou résolue.

Dans le conteneur des paires non résolues (classe conteneur non résolu ), d'abord tous les composants gauche (voir le cadre gauche dans le GIF ci-dessus), puis tous les composants droit (voir le cadre droit dans le GIF) des paires sont répertoriés. (J'utilise la directive ngFor pour répertorier les paires.) Pour le moment, un simple bouton suffit comme modèle.

Avec l'expression de modèle {{{pair.leftpart}} et { {{pair.rightpart}}} les valeurs des propriétés partie gauche et partie droite des objets paire individuels sont interrogés lors de l'itération de la paire tableau. Ils sont utilisés comme étiquettes pour les boutons générés.

Les paires affectées sont répertoriées dans le deuxième conteneur (classe conteneur résolu ). Une barre verte (connecteur de classe ) indique qu'ils appartiennent ensemble.

Le code CSS correspondant du fichier matching-game.component.css se trouve dans le code source à l'adresse le début de l'article.

0 ">       
0 ">        

Dans le composant game1 le tableau animaux est maintenant lié au ] pairs propriété du composant match-game (liaison de données unidirectionnelle).

   

Le résultat est illustré dans l'image ci-dessous.

 État actuel de l'interface utilisateur
État actuel de l'interface utilisateur

Évidemment, notre jeu d'association n'est pas encore trop difficile, car les parties gauche et droite des paires sont directement opposées. Pour que l'appariement ne soit pas trop trivial, les bonnes pièces doivent être mélangées. Je résous le problème avec un tuyau auto-défini shuffle que j'applique au tableau unsolvedPairs sur le côté droit (le paramètre test est nécessaire plus tard pour forcer la pipe à mettre à jour):

 ...
...

Le code source de la pipe est stocké dans le fichier shuffle.pipe.ts dans le dossier de l'application (voir le code source au début de l'article). Notez également le fichier app.module.ts où le tuyau doit être importé et répertorié dans les déclarations du module. La vue souhaitée apparaît maintenant dans le navigateur.

Version étendue: utilisation de modèles personnalisables pour autoriser une conception individuelle du jeu

Au lieu d'un bouton, il devrait être possible de spécifier des extraits de modèle arbitraires pour personnaliser le jeu. Dans le fichier matching-game.component.html je remplace le modèle de bouton pour les côtés gauche et droit du jeu par une balise ng-template . J'attribue ensuite le nom d'une référence de modèle à la propriété ngTemplateOutlet . Cela me donne deux espaces réservés, qui sont remplacés par le contenu de la référence de modèle respective lors du rendu de la vue.

Nous traitons ici du concept de projection de contenu : certaines parties du modèle de composant sont données de l'extérieur et sont «projetés» dans le modèle aux emplacements marqués.

Lors de la génération de la vue, Angular doit insérer les données du jeu dans le modèle. Avec le paramètre ngTemplateOutletContext je dis à Angular qu'une variable contextPair est utilisée dans le modèle, à laquelle il faut attribuer la valeur actuelle de la variable pair à partir de la variable ] Directive ngFor .

La liste suivante montre le remplacement du conteneur non résolu . Dans le conteneur résolu les boutons doivent également être remplacés par les balises ng-template .

 
0 ">
...

Dans le fichier matching-game.component.ts les variables des deux références de modèle ( leftpart_temp et rightpart_temp ) doivent être déclarées. Le décorateur @ContentChild indique qu'il s'agit d'une projection de contenu, c.-à-d. Angular s'attend maintenant à ce que les deux extraits de modèle avec le sélecteur respectif ( partie gauche ou partie droite ) soient fournis. dans le composant parent entre les balises de l'élément hôte (voir @ViewChild ).

 @ContentChild ('leftpart', {static: false}) leftpart_temp: TemplateRef ;
@ContentChild ('rightpart', {statique: faux}) rightpart_temp: TemplateRef ;

N'oubliez pas: les types ContentChild et TemplateRef doivent être importés du package de base.

Dans le composant parent game1 les deux les extraits de modèle requis avec les sélecteurs partie gauche et partie droite sont maintenant insérés.

Par souci de simplicité, je vais réutiliser les boutons ici à nouveau:

 
    
                 
       
    
          
       

L'attribut let-animalPair = "contextPair" est utilisé pour spécifier que la variable de contexte contextPair est utilisée dans l'extrait de modèle avec le nom animalPair .

Les extraits de modèle peuvent maintenant être modifiés à votre goût. Pour le démontrer, j'utilise le composant game2 . Le fichier game2.component.ts obtient le même contenu que game1.component.ts . Dans game2.component.html j'utilise un élément div conçu individuellement au lieu d'un bouton. Les classes CSS sont stockées dans le fichier game2.component.css .

 
    
          
{{animalPair.leftpart}}
{{animalPair.rightpart}}
       

Après avoir ajouté les balises sur la page d'accueil app.component.html la deuxième version du jeu apparaît lorsque je lance l'application:

 Une autre vue du jeu dans le composant game2
Une autre vue du jeu dans le composant game2

Les possibilités de conception sont maintenant presque illimitées. Il serait possible, par exemple, de définir une sous-classe de Pair qui contient des propriétés supplémentaires. Par exemple, les adresses d'images peuvent être stockées pour les parties gauche et / ou droite. Les images peuvent être affichées dans le modèle avec le texte ou à la place du texte.

2. Contrôle de l'interaction utilisateur avec RxJS

Avantages d'une programmation réactive avec RxJS

Pour transformer l'application en un jeu interactif, les événements (par exemple les événements de clic de souris) qui sont déclenchés sur l'interface utilisateur doivent être traités. Dans la programmation réactive, des séquences continues d'événements, appelés «flux», sont prises en compte. Un flux peut être observé (c'est un «observable»), c'est-à-dire qu'il peut y avoir un ou plusieurs «observateurs» ou «abonnés» abonnés au flux. Ils sont informés (généralement de manière asynchrone) de chaque nouvelle valeur dans le flux et peuvent y réagir d'une certaine manière.

Avec cette approche, un faible niveau de couplage entre les parties d'une application peut être atteint. Les observateurs et observables existants sont indépendants les uns des autres et leur couplage peut être modifié au moment de l'exécution.

La bibliothèque JavaScript RxJS fournit une implémentation mature du modèle de conception Observer. De plus, RxJS contient de nombreux opérateurs pour convertir les flux (par exemple filtre, carte) ou pour les combiner en de nouveaux flux (par exemple fusionner, concat). Les opérateurs sont des «fonctions pures» au sens de la programmation fonctionnelle: ils ne produisent pas d'effets secondaires et sont indépendants de l'état extérieur à la fonction. Une logique de programme composée uniquement d'appels à des fonctions pures n'a pas besoin de variables auxiliaires globales ou locales pour stocker des états intermédiaires. Ceci, à son tour, favorise la création de blocs de code sans état et à couplage lâche. Il est donc souhaitable de réaliser une grande partie de la gestion des événements par une combinaison intelligente d'opérateurs de flux. Des exemples de cela sont donnés dans la section suivante, basée sur notre jeu de correspondance.

Intégration de RxJS dans la gestion des événements d'un composant angulaire

Le framework Angular fonctionne avec les classes de la bibliothèque RxJS.

L'image ci-dessous montre les principales classes et fonctions qui jouent un rôle dans nos considérations:

 Un modèle des classes essentielles pour la gestion des événements dans Angular / RxJS
A modèle des classes essentielles pour la gestion d'événements dans Angular / RxJS
Nom de classe Fonction
Observable (RxJS) Classe de base qui représente un flux; en d'autres termes, une séquence continue de données. Un observable peut être souscrit. La fonction pipe est utilisée pour appliquer une ou plusieurs fonctions d'opérateur à l'instance observable.
Subject (RxJS) La sous-classe d'observable fournit la fonction suivante pour publier de nouvelles données dans le flux. [19659095] EventEmitter (Angular) Il s'agit d'une sous-classe angulaire spécifique qui n'est généralement utilisée qu'en conjonction avec le décorateur @Output pour définir une sortie de composant. Comme la fonction suivante, la fonction emit est utilisée pour envoyer des données aux abonnés.
Subscription (RxJS) La fonction subscribe d'une observable renvoie une instance d'abonnement. Il est nécessaire d'annuler l'abonnement après avoir utilisé le composant.

Avec l'aide de ces classes, nous voulons implémenter l'interaction utilisateur dans notre jeu. La première étape consiste à s'assurer qu'un élément sélectionné par l'utilisateur à gauche ou à droite est mis en évidence visuellement.

La représentation visuelle des éléments est contrôlée par les deux extraits de modèle dans le composant parent. La décision quant à leur affichage dans l'état sélectionné doit donc également être laissée au composant parent. Il doit recevoir les signaux appropriés dès qu'une sélection est effectuée sur le côté gauche ou droit ou dès qu'une sélection doit être annulée.

Pour cela, je définis quatre valeurs de sortie de type EventEmitter dans le fichier matching-game.component.ts . Les types Output et EventEmitter doivent être importés du package de base.

 @Output () leftpartSelected = new EventEmitter  ();
@Output () rightpartSelected = new EventEmitter  ();
@Output () leftpartUnselected = new EventEmitter ();
@Output () rightpartUnselected = new EventEmitter ();

Dans le modèle matching-game.component.html je réagis à l'événement mousedown à gauche et à droite, puis envoie l'ID de l'élément sélectionné à tous récepteurs.

...

Dans notre cas, les récepteurs sont les composants game1 et game2 . Vous pouvez maintenant définir la gestion des événements pour les événements leftpartSelected rightpartSelected leftpartUnselected et rightpartUnselected . La variable $ event représente la valeur de sortie émise, dans notre cas l'ID. Ci-dessous, vous pouvez voir la liste pour game1.component.html pour game2.component.html les mêmes changements s'appliquent.



      
                  
          
    
        
     

Dans game1.component.ts (et de même dans game2.component.ts ), les fonctions de gestionnaire d'événement sont désormais implémentées. Je stocke les ID des éléments sélectionnés. Dans le modèle HTML (voir ci-dessus), ces éléments se voient attribuer la classe sélectionnée . Le fichier CSS game1.component.css définit les changements visuels que cette classe entraînera (par exemple les changements de couleur ou de police). La réinitialisation de la sélection (désélectionner) est basée sur l'hypothèse que les objets paire ont toujours des ID positifs.

 onLeftpartSelected (id: number): void {
    this.leftpartSelectedId = id;
}
onRightpartSelected (id: number): void {
    this.rightpartSelectedId = id;
}
onLeftpartUnselected (): void {
    this.leftpartSelectedId = -1;
}
onRightpartUnselected (): void {
    this.rightpartSelectedId = -1;
}

Dans l'étape suivante, la gestion des événements est requise dans le composant de jeu correspondant. Il faut déterminer si une affectation est correcte, c'est-à-dire si l'élément sélectionné à gauche correspond à l'élément sélectionné à droite. Dans ce cas, la paire affectée peut être déplacée dans le conteneur pour les paires résolues.

Je voudrais formuler la logique d'évaluation en utilisant les opérateurs RxJS (voir la section suivante). Pour la préparation, je crée un sujet assignStream dans matching-game.component.ts . Il doit émettre les éléments sélectionnés par l'utilisateur sur le côté gauche ou droit. Le but est d'utiliser des opérateurs RxJS pour modifier et diviser le flux de manière à obtenir deux nouveaux flux: un flux solvedStream qui fournit les paires correctement attribuées et un second flux failedStream qui fournit les mauvaises affectations. Je souhaite m'abonner à ces deux flux avec subscribe afin de pouvoir effectuer un traitement d'événement approprié dans chaque cas.

J'ai également besoin d'une référence aux objets d'abonnement créés, afin de pouvoir annuler les abonnements avec «unsubscribe» à la sortie du jeu (voir ngOnDestroy ). Les classes Subject et Subscription doivent être importées du package «rxjs».

 private cessionStream = new Subject ();

private solvedStream = new Observable  ();
private failedStream = new Observable  ();

s_Subscription privé: abonnement;
f_Subscription privé: abonnement;

ngOnInit () {

  ...
  // TODO: appliquez les opérateurs de flux sur
  // leftpartClicked und rightpartClicked
  this.s_Subscription = this.solvedStream.subscribe (paire =>
  handleSolvedAssignment (paire));
  this.f_Subscription = this.failedStream.subscribe (() =>
  handleFailedAssignment ());
}

ngOnDestroy () {
   this.s_Subscription.unsubscribe ();
   this.f_Subscription.unsubscribe ();
}

Si l'affectation est correcte, les étapes suivantes sont effectuées:

  • La ​​paire affectée est déplacée vers le conteneur pour les paires résolues.
  • Les événements leftpartUnselected et rightpartUnselected ] sont envoyés au composant parent.

Aucune paire n'est déplacée si l'affectation est incorrecte. Si la mauvaise affectation a été exécutée de gauche à droite ( side1 a la valeur left ), la sélection doit être annulée pour l'élément du côté gauche (voir le GIF au début de l'article). Si une affectation est effectuée de droite à gauche, la sélection est annulée pour l'élément du côté droit. Cela signifie que le dernier élément sur lequel vous avez cliqué reste dans un état sélectionné.

Dans les deux cas, je prépare les fonctions de gestionnaire correspondantes handleSolvedAssignment et handleFailedAssignment (supprimer la fonction: voir la source code à la fin de cet article):

 private handleSolvedAssignment (paire: Pair): void {
   this.solvedPairs.push (paire);
   this.remove (this.unsolvedPairs, pair);
   this.leftpartUnselected.emit ();
   this.rightpartUnselected.emit ();
   // solution de contournement pour forcer la mise à jour du shuffle pipe
   this.test = Math.random () * 10;
}
private handleFailedAssignment (side1: string): void {

   if (side1 == "left") {
        this.leftpartUnselected.emit ();
   }autre{
        this.rightpartUnselected.emit ();
   }

}

Maintenant, nous devons changer le point de vue du consommateur qui souscrit aux données au producteur qui génère les données. Dans le fichier matching-game.component.html je m'assure qu'en cliquant sur un élément, l'objet paire associé est poussé dans le flux assignStream . Il est logique d'utiliser un flux commun pour les côtés gauche et droit car l'ordre de l'affectation n'est pas important pour nous.

...

Conception de l'interaction du jeu avec les opérateurs RxJS

Il ne reste plus qu'à convertir le flux assignStream en flux solvedStream et failedStream . J'applique les opérateurs suivants dans l'ordre:

par paire

Il y a toujours deux paires dans une affectation. L'opérateur par paire sélectionne les données par paires dans le flux. La valeur actuelle et la valeur précédente sont combinées en une paire.

Du flux suivant…

 „{pair1, left}, {pair3, right}, {pair2, left}, {pair2, right}, { pair1, gauche}, {pair1, droite} "

… donne ce nouveau flux:

 „({pair1, gauche}, {pair3, droite}), ({pair3, droite}, {pair2, gauche}), ({pair2, gauche}, {pair2 , droite}), ({pair2, droite}, {pair1, gauche}), ({pair1, gauche}, {pair1, droite}) "
 

Par exemple, nous obtenons la combinaison ({pair1, gauche}, {pair3, droite}) lorsque l'utilisateur sélectionne chien (id = 1) sur le côté gauche et insecte (id = 3) sur le côté droit (voir tableau ANIMALS au début de l'article). Ces combinaisons et les autres résultent de la séquence de jeu montrée dans le GIF ci-dessus.

filtre

Vous devez supprimer toutes les combinaisons du flux qui ont été faites du même côté du terrain de jeu comme ({pair1, left}, {pair1, left}) ou ({pair1, left}, {pair4, left}) .

La condition de filtre pour une combinaison peigne est donc peigne [0] .side! = Comb [1] .side .

partition

Cet opérateur prend un flux et une condition et crée deux flux à partir de celui-ci . Le premier flux contient les données qui remplissent la condition et le second flux contient les données restantes. Dans notre cas, les flux doivent contenir des affectations correctes ou incorrectes. La condition pour une combinaison peigne est donc peigne [0] .pair === peigne [1] .pair .

L'exemple donne un flux «correct» avec [19659129] ({pair2, gauche}, {pair2, droite}), ({pair1, gauche}, {pair1, droite})
 

et un flux "incorrect" avec

 ({pair1, left}, {pair3, right}), ({pair3, right}, {pair2, left}), ({pair2, right}, {pair1 , la gauche})
 

map

Seul l'objet paire individuel est requis pour le traitement ultérieur d'une affectation correcte, comme pair2 . L'opérateur de carte peut être utilisé pour exprimer que la combinaison comb doit être mappée sur comb [0] .pair . Si l'affectation est incorrecte, la combinaison comb est mappée à la chaîne comb [0] .side car la sélection doit être réinitialisée du côté spécifié par side

La fonction pipe est utilisée pour concaténer les opérateurs ci-dessus. Les opérateurs par paire filtre partition carte doivent être importés du package rxjs / operators . [19659145] ngOnInit () {
   …
   const stream = this.assignmentStream.pipe (
                   par paire (),
                   filtre (comb => comb [0] .side! = comb [1] .side)
                  );
   // la notation pipe conduit à un message d'erreur (Angular 8.2.2, RxJS 6.4.0)
   const [stream1, stream2] = partition (comb =>
                                        peigne [0] .pair === peigne [1] .pair) (flux);
   this.solvedStream = stream1.pipe (
                         map (comb => comb [0] .pair)
                       );
   this.failedStream = stream2.pipe (
                         map (comb => comb [0] .side)
                       );
   this.s_Subscription = this.solvedStream.subscribe (paire =>
                             this.handleSolvedAssignment (paire));
   this.f_Subscription = this.failedStream.subscribe (côté =>
                             this.handleFailedAssignment (côté));
}

Maintenant, le jeu fonctionne déjà!

 Capture d'écran du jeu d'apprentissage "paires correspondantes"
Résultat final

En utilisant les opérateurs, la logique du jeu pourrait être décrite de manière déclarative. Nous avons seulement décrit les propriétés de nos deux flux cibles (combinés en paires, filtrés, partitionnés, remappés) et n'avons pas eu à nous soucier de la mise en œuvre de ces opérations. Si nous les avions implémentés nous-mêmes, nous aurions également dû stocker des états intermédiaires dans le composant (par exemple, les références aux derniers éléments cliqués à gauche et à droite). Au lieu de cela, les opérateurs RxJS encapsulent la logique d'implémentation et les états requis pour nous et élèvent ainsi la programmation à un niveau d'abstraction plus élevé.

Conclusion

En utilisant un jeu d'apprentissage simple comme exemple, nous avons testé l'utilisation de RxJS dans une composante angulaire. L'approche réactive est bien adaptée pour traiter les événements qui se produisent sur l'interface utilisateur. Avec RxJS, les données nécessaires à la gestion des événements peuvent être facilement organisées sous forme de flux. De nombreux opérateurs, tels que filtre carte ou partition sont disponibles pour transformer les flux. Les flux résultants contiennent des données préparées dans leur forme finale et auxquelles vous pouvez vous abonner directement. Il faut un peu de compétence et d'expérience pour sélectionner les opérateurs appropriés pour le cas respectif et pour les relier efficacement. Cet article devrait en fournir une introduction.

Autres ressources

Lecture connexe sur SmashingMag:

 Éditorial Smashing (ra, il)




Source link