Cómo crear un juego de combinación de cartas usando Angular y RxJS

 

 

 

  • Planificación y proceso del sistema de diseño, con Nathan Curtis
  • Creación y mantenimiento de sistemas de diseño exitosos, con Brad Fost

  • Índice
    1. ¿Qué estamos construyendo?
    2. 1. Construyendo un componente angular para el juego de aprendizaje
      1. Cómo crear el marco básico
      2. La primera versión del componente del juego.
      3. Versión ampliada: uso de plantillas personalizables para permitir un diseño individual del juego
    3. 2. Control de la interacción del usuario con RxJS
      1. Ventajas de la programación reactiva con RxJS
      2. Integración de RxJS en el manejo de eventos de un componente angular
    4. Diseño de la interacción del juego con operadores RxJS.
      1. pairwise
      2. filter
      3. partition
      4. map

    Este artículo está dedicado a los desarrolladores de Angular que desean aprovechar el concepto de programación reactiva. Este es un estilo de programación que, en pocas palabras, se ocupa del procesamiento de flujos de datos asincrónicos.

     

    Hoy me gustaría centrarme en los flujos de datos resultantes de eventos de clic en la interfaz de usuario. El procesamiento de dichos flujos de clics es particularmente útil para aplicaciones con una interacción intensiva del usuario donde se deben procesar muchos eventos. También me gustaría presentarles un poco más a RxJS; Es una biblioteca de JavaScript que se puede utilizar para expresar rutinas de manejo de eventos de manera compacta y concisa en un estilo reactivo.

    ¿Qué estamos construyendo?

    Los juegos de aprendizaje y los cuestionarios de conocimientos son populares tanto entre los usuarios más jóvenes como entre los mayores. Un ejemplo es el juego "pareja", donde el usuario tiene que encontrar pares relacionados en una mezcla de imágenes y/o fragmentos de texto.

     

    La siguiente animación muestra una versión sencilla del juego: el usuario selecciona dos elementos en el lado izquierdo y derecho del campo de juego, uno tras otro y en cualquier orden. Las parejas correctamente emparejadas se trasladan a una zona separada del campo de juego, mientras que las asignaciones incorrectas se disuelven inmediatamente para que el usuario tenga que realizar una nueva selección.

    En este tutorial, crearemos un juego de aprendizaje paso a paso. En la primera parte , construiremos un componente Angular que solo muestra el campo de juego del juego. Nuestro objetivo es que el componente pueda configurarse para diferentes casos de uso y grupos objetivo, desde un cuestionario sobre animales hasta un entrenador de vocabulario en una aplicación de aprendizaje de idiomas. Para ello, Angular ofrece el concepto de proyección de contenidos con plantillas personalizables, que haremos uso. Para ilustrar el principio, crearé dos versiones del juego (“juego1” y “juego2”) con diseños diferentes.

    En la segunda parte del tutorial, nos centraremos en la programación reactiva. Siempre que se empareja un par, el usuario necesita obtener algún tipo de retroalimentación de la aplicación; es este manejo de eventos el que se realiza con la ayuda de la biblioteca RxJS.

    • Requisitos
      Para seguir este tutorial, se debe instalar Angular CLI .
    • Código fuente
      El código fuente de este tutorial se puede encontrar aquí (14 KB).

    1. Construyendo un componente angular para el juego de aprendizaje

    Cómo crear el marco básico

    Primero, creemos un nuevo proyecto llamado "aplicación de aprendizaje". Con Angular CLI, puedes hacer esto con el comando ng new learning-app. En el archivo app.component.html , reemplazo el código fuente generado previamente de la siguiente manera:

    div h1Learning is fun!/h1/div

    En el siguiente paso se crea el componente para el juego educativo. Lo llamé "juego de correspondencias" y usé el comando ng generate component matching-game. Esto creará una subcarpeta separada para el componente del juego con los archivos HTML, CSS y Typecript necesarios.

    Como ya se mencionó, el juego educativo debe poder configurarse para diferentes propósitos. Para demostrar esto, creo dos componentes adicionales ( game1y game2) usando el mismo comando. Agrego el componente del juego como componente secundario reemplazando el código generado previamente en el archivo game1.component.html o game2.component.html con la siguiente etiqueta:

     

    app-matching-game/app-matching-game

    Al principio, sólo uso el componente game1. Para asegurarme de que el juego 1 se muestre inmediatamente después de iniciar la aplicación, agrego esta etiqueta al archivo app.component.html :

    app-game1/app-game1

    Al iniciar la aplicación con ng serve --open, el navegador mostrará el mensaje "el juego de combinación funciona". (Este es actualmente el único contenido de match-game.component.html ).

    Ahora, necesitamos probar los datos. En la /appcarpeta, creo un archivo llamado pair.ts donde defino la clase Pair:

    export class Pair { leftpart: string; rightpart: string; id: number;}

    Un objeto de par comprende dos textos relacionados ( leftparty rightpart) y una identificación.

    Se supone que el primer juego es un cuestionario sobre especies en el que las especies (p. ej. dog) deben asignarse a la clase de animal adecuada (p. ej. mammal).

    En el archivo animales.ts , defino una matriz con datos de prueba:

    import { Pair } from './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'},];

    El componente game1necesita acceso a nuestros datos de prueba. Se guardan en la propiedad animals. El archivo game1.component.ts ahora tiene el siguiente contenido:

    import { Component, OnInit } from '@angular/core';import { ANIMALS } from '../animals';@Component({ selector: 'app-game1', templateUrl: './game1.component.html', styleUrls: ['./game1.component.css']})export class Game1Component implements OnInit { animals = ANIMALS; constructor() { } ngOnInit() { }}

    La primera versión del componente del juego.

    Nuestro próximo objetivo: el componente del juego matching-gametiene que aceptar los datos del juego del componente principal (p. ej. game1) como entrada. La entrada es una matriz de objetos "pares". La interfaz de usuario del juego debe inicializarse con los objetos pasados ​​al iniciar la aplicación.

    Para ello debemos proceder de la siguiente manera:

    1. Agregue la propiedad pairsal componente del juego usando el @Inputdecorador.
    2. Agregue las matrices solvedPairsy unsolvedPairscomo propiedades privadas adicionales del componente. (Es necesario distinguir entre pares ya “resueltos” y “aún no resueltos”).
    3. Cuando se inicia la aplicación (ver función ngOnInit), todos los pares todavía están "sin resolver" y, por lo tanto, se mueven a la matriz unsolvedPairs.
    import { Component, OnInit, Input } from '@angular/core';import { Pair } from '../pair';@Component({ selector: 'app-matching-game', templateUrl: './matching-game.component.html', styleUrls: ['./matching-game.component.css']})export class MatchingGameComponent implements OnInit { @Input() pairs: Pair[]; private solvedPairs: Pair[] = []; private unsolvedPairs: Pair[] = []; constructor() { } ngOnInit() { for(let i=0; ithis.pairs.length; i++){ this.unsolvedPairs.push(this.pairs[i]); } }}

    Además, defino la plantilla HTML del matching-gamecomponente. Hay contenedores para los pares resueltos y sin resolver. La ngIfdirectiva garantiza que el contenedor respectivo solo se muestre si existe al menos un par resuelto o sin resolver.

     

    En el contenedor de los pares sin resolver (clase ), primero se enumeran container unsolvedtodos left(ver el marco izquierdo en el GIF arriba) y luego todos (ver el marco derecho en el GIF) los componentes de los pares. right(Utilizo la ngFordirectiva para enumerar los pares). Por el momento, un simple botón es suficiente como plantilla.

    Con la expresión de plantilla {{{pair.leftpart}}y { , se consultan {{pair.rightpart}}}los valores de las propiedades leftparty de los objetos de pares individuales al iterar la matriz. Se utilizan como etiquetas para los botones generados.rightpartpair

    Los pares asignados se enumeran en el segundo contenedor (clase container solved). Una barra verde (clase connector) indica que van juntos.

    El código CSS correspondiente del archivo match-game.component.css se puede encontrar en el código fuente al principio del artículo.

    div div *ngIf="unsolvedPairs.length0" div button *ngFor="let pair of unsolvedPairs" {{pair.leftpart}} /button /div div button *ngFor="let pair of unsolvedPairs" {{pair.rightpart}} /button /div /div div *ngIf="solvedPairs.length0" div *ngFor="let pair of solvedPairs" button{{pair.leftpart}}/button div/div button{{pair.rightpart}}/button /div /div/div

    En el componente game1, la matriz animalsahora está vinculada a la pairspropiedad del componente matching-game(enlace de datos unidireccional).

    app-matching-game [pairs]="animals"/app-matching-game

    El resultado se muestra en la imagen de abajo.

    Estado actual de la interfaz de usuario.

    Obviamente, nuestro juego de emparejar no es demasiado difícil todavía, porque las partes izquierda y derecha de los pares están directamente opuestas entre sí. Para que el emparejamiento no sea demasiado trivial, conviene mezclar las partes adecuadas. Resuelvo el problema con una tubería autodefinida shuffle, que aplico a la matriz unsolvedPairsdel lado derecho (el parámetro testse necesita más adelante para forzar la actualización de la tubería):

    ...div button *ngFor="let pair of unsolvedPairs | shuffle:test" {{pair.rightpart}} /button /div...

    El código fuente de la tubería se almacena en el archivo shuffle.pipe.ts en la carpeta de la aplicación (consulte el código fuente al principio del artículo). También tenga en cuenta el archivo app.module.ts , donde se debe importar la tubería y enumerarla en las declaraciones del módulo. Ahora aparece la vista deseada en el navegador.

     

    Versión ampliada: uso de plantillas personalizables para permitir un diseño individual del juego

    En lugar de un botón, debería ser posible especificar fragmentos de plantilla arbitrarios para personalizar el juego. En el archivo match-game.component.html reemplazo la plantilla de botones para el lado izquierdo y derecho del juego con una ng-templateetiqueta. Luego asigno el nombre de una referencia de plantilla a la propiedad ngTemplateOutlet. Esto me da dos marcadores de posición, que se reemplazan por el contenido de la referencia de plantilla respectiva al representar la vista.

    Estamos tratando aquí con el concepto de proyección de contenido : ciertas partes de la plantilla del componente se dan desde afuera y se "proyectan" en la plantilla en las posiciones marcadas.

    Al generar la vista, Angular debe insertar los datos del juego en la plantilla. Con el parámetro ngTemplateOutletContextle digo a Angular que contextPairse usa una variable dentro de la plantilla, a la que se le debe asignar el valor actual de la pairvariable de la ngFordirectiva.

    El siguiente listado muestra el reemplazo del contenedor unsolved. En el contenedor , los botones también solveddeben ser reemplazados por etiquetas.ng-template

    div *ngIf="unsolvedPairs.length0"div div *ngFor="let pair of unsolvedPairs" ng-template [ngTemplateOutlet]="leftpart_temp" [ngTemplateOutletContext]="{contextPair: pair}" /ng-template /div /div div div *ngFor="let pair of unsolvedPairs | shuffle:test" ng-template [ngTemplateOutlet]="leftpart_temp" [ngTemplateOutletContext]="{contextPair: pair}" /ng-template /div/div/div...

    En el archivo match-game.component.ts se deben declarar las variables de ambas referencias de plantilla ( leftpart_tempy ). rightpart_tempEl decorador @ContentChildindica que se trata de una proyección de contenido, es decir, Angular ahora espera que los dos fragmentos de plantilla con el selector respectivo ( leftparto rightpart) se proporcionen en el componente principal entre las etiquetas app-matching-game/app-matching-gamedel elemento host (ver @ViewChild).

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

    No lo olvide: los tipos ContentChildy TemplateRefdeben importarse desde el paquete principal.

    En el componente principal , ahora están insertados game1los dos fragmentos de plantilla requeridos con los selectores .leftpartrightpart

    Para simplificar, reutilizaré los botones aquí nuevamente:

    app-matching-game [pairs]="animals" ng-template #leftpart let-animalPair="contextPair" button{{animalPair.leftpart}}/button /ng-template ng-template #rightpart let-animalPair="contextPair" button{{animalPair.rightpart}}/button /ng-template/app-matching-game

    El atributo let-animalPair="contextPair"se usa para especificar que la variable de contexto contextPairse usa en el fragmento de plantilla con el nombre animalPair. Significado de emojis

     

    Los fragmentos de la plantilla ahora se pueden cambiar a tu gusto. Para demostrar esto utilizo el componente game2. El archivo game2.component.ts obtiene el mismo contenido que game1.component.ts . En game2.component.html utilizo un divelemento diseñado individualmente en lugar de un botón. Las clases CSS se almacenan en el archivo game2.component.css .

    app-matching-game [pairs]="animals" ng-template #leftpart let-animalPair="contextPair" div{{animalPair.leftpart}}/div /ng-template ng-template #rightpart let-animalPair="contextPair" div{{animalPair.rightpart}}/div /ng-template/app-matching-game

    Después de agregar las etiquetas app-game2/app-game2en la página de inicio app.component.html , aparece la segunda versión del juego cuando inicio la aplicación:

    Una vista alternativa del juego en el componente.game2

    Las posibilidades de diseño son ahora casi ilimitadas. Sería posible, por ejemplo, definir una subclase Pairque contenga propiedades adicionales. Por ejemplo, se podrían almacenar direcciones de imágenes para las partes izquierda y/o derecha. Las imágenes podrían mostrarse en la plantilla junto con el texto o en lugar del texto.

    2. Control de la interacción del usuario con RxJS

    Ventajas de la programación reactiva con RxJS

    Para convertir la aplicación en un juego interactivo, se deben procesar los eventos (por ejemplo, eventos de clic del mouse) que se activan en la interfaz de usuario. En la programación reactiva se consideran secuencias continuas de eventos, las llamadas "streams". Una secuencia puede ser observada (es un “observable”), es decir, puede haber uno o más “observadores” o “suscriptores” suscritos a la secuencia. Se les notifica (generalmente de forma asincrónica) sobre cada nuevo valor en la secuencia y pueden reaccionar ante él de cierta manera.

    Con este enfoque, se puede lograr un bajo nivel de acoplamiento entre las partes de una aplicación. Los observadores y observables existentes son independientes entre sí y su acoplamiento se puede variar en tiempo de ejecución.

    La biblioteca JavaScript RxJS proporciona una implementación madura del patrón de diseño Observer. Además, RxJS contiene numerosos operadores para convertir flujos (por ejemplo, filtrar, mapear) o combinarlos en nuevos flujos (por ejemplo, fusionar, concat). Los operadores son “funciones puras” en el sentido de programación funcional: no producen efectos secundarios y son independientes del estado fuera de la función. Una lógica de programa compuesta únicamente de llamadas a funciones puras no necesita variables auxiliares globales o locales para almacenar estados intermedios. Esto, a su vez, promueve la creación de bloques de código sin estado y débilmente acoplados. Por lo tanto, es deseable realizar una gran parte del manejo de eventos mediante una combinación inteligente de operadores de flujo. En la siguiente sección se dan ejemplos de esto, basados ​​en nuestro juego de correspondencias.

    Integración de RxJS en el manejo de eventos de un componente angular

    El framework Angular trabaja con las clases de la biblioteca RxJS. Por lo tanto, RxJS se instala automáticamente cuando se instala Angular.

     

    La siguiente imagen muestra las principales clases y funciones que desempeñan un papel en nuestras consideraciones:

    Un modelo de las clases esenciales para el manejo de eventos en Angular/RxJS

    Nombre de la clase Función
    Observable (RxJS) Clase base que representa una secuencia; en otras palabras, una secuencia continua de datos. Se puede suscribir a un observable. La pipefunción se utiliza para aplicar una o más funciones de operador a la instancia observable.
    Asunto (RxJS) La subclase de observable proporciona la siguiente función para publicar nuevos datos en la secuencia.
    Emisor de eventos (angular) Esta es una subclase específica de angular que generalmente solo se usa junto con el @Outputdecorador para definir la salida de un componente. Al igual que la siguiente función, la emitfunción se utiliza para enviar datos a los suscriptores.
    Suscripción (RxJS) La subscribefunción de un observable devuelve una instancia de suscripción. Es necesario cancelar la suscripción después de utilizar el componente.

    Con la ayuda de estas clases, queremos implementar la interacción del usuario en nuestro juego. El primer paso es asegurarse de que un elemento seleccionado por el usuario en el lado izquierdo o derecho esté resaltado visualmente.

    La representación visual de los elementos está controlada por los dos fragmentos de plantilla en el componente principal. Por lo tanto, la decisión de cómo se muestran en el estado seleccionado también debe dejarse en manos del componente principal. Debería recibir las señales adecuadas en cuanto se realiza una selección en el lado izquierdo o derecho o en cuanto se desea deshacer una selección.

    Para ello, defino cuatro valores de salida de tipo EventEmitteren el archivo match-game.component.ts . Los tipos Outputy EventEmitterdeben importarse desde el paquete principal.

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

    En la plantilla match-game.component.html , reacciono al mousedownevento en el lado izquierdo y derecho, y luego envío el ID del elemento seleccionado a todos los receptores.

    div *ngFor="let pair of unsolvedPairs" (mousedown)="leftpartSelected.emit(pair.id)"...div *ngFor="let pair of unsolvedPairs | shuffle:test" (mousedown)="rightpartSelected.emit(pair.id)"

    En nuestro caso, los receptores son los componentes game1y game2. Allí ahora puede definir el manejo de eventos para los eventos leftpartSelected, y . La variable representa el valor de salida emitido, en nuestro caso el ID. A continuación puede ver la lista de game1.component.html ; para game2.component.html se aplican los mismos cambios.rightpartSelectedleftpartUnselectedrightpartUnselected$event

    app-matching-game [pairs]="animals" (leftpartSelected)="onLeftpartSelected($event)" (rightpartSelected)="onRightpartSelected($event)" (leftpartUnselected)="onLeftpartUnselected()" (rightpartUnselected)="onRightpartUnselected()" ng-template #leftpart let-animalPair="contextPair" button [class.selected]="leftpartSelectedId==animalPair.id" {{animalPair.leftpart}} /button /ng-template ng-template #rightpart let-animalPair="contextPair" button [class.selected]="rightpartSelectedId==animalPair.id" {{animalPair.rightpart}} /button /ng-template/app-matching-game

    En game1.component.ts (y de manera similar en game2.component.ts ), las eventfunciones del controlador ahora están implementadas. Almaceno los ID de los elementos seleccionados. En la plantilla HTML (ver arriba), a estos elementos se les asigna la clase selected. El archivo CSS game1.component.css define qué cambios visuales provocará esta clase (por ejemplo, cambios de color o fuente). Restablecer la selección (anular selección) se basa en la suposición de que el par de objetos siempre tiene ID positivos.

     

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

    En el siguiente paso, se requiere el manejo de eventos en el componente del juego de correspondencias. Se debe determinar si una asignación es correcta, es decir, si el elemento seleccionado a la izquierda coincide con el elemento seleccionado a la derecha. En este caso, el par asignado se puede mover al contenedor de los pares resueltos.

    Me gustaría formular la lógica de evaluación utilizando operadores RxJS (consulte la siguiente sección). Para prepararme, creo un tema assignmentStreamen match-game.component.ts . Debe emitir los elementos seleccionados por el usuario en el lado izquierdo o derecho. El objetivo es utilizar operadores RxJS para modificar y dividir la secuencia de tal manera que obtenga dos secuencias nuevas: una secuencia solvedStreamque proporciona los pares asignados correctamente y una segunda secuencia failedStreamque proporciona las asignaciones incorrectas. Me gustaría suscribirme a estas dos transmisiones subscribepara poder realizar el manejo de eventos adecuado en cada caso.

    También necesito una referencia a los objetos de suscripción creados, para poder cancelar las suscripciones con "cancelar suscripción" al salir del juego (ver ngOnDestroy). Las clases Subjecty Subscriptiondeben importarse del paquete “rxjs”.

    private assignmentStream = new Subject{pair:Pair, side:string}();private solvedStream = new ObservablePair();private failedStream = new Observablestring();private s_Subscription: Subscription;private f_Subscription: Subscription;ngOnInit(){ ... //TODO: apply stream-operators on //assignmentStream this.s_Subscription = this.solvedStream.subscribe(pair = handleSolvedAssignment(pair)); this.f_Subscription = this.failedStream.subscribe(() = handleFailedAssignment());}ngOnDestroy() { this.s_Subscription.unsubscribe(); this.f_Subscription.unsubscribe();}

    Si la asignación es correcta, se realizan los siguientes pasos:

     

    • El par asignado se mueve al contenedor de los pares resueltos.
    • Los eventos leftpartUnselectedy rightpartUnselectedse envían al componente principal.

    No se mueve ningún par si la asignación es incorrecta. Si se ejecutó la asignación incorrecta de izquierda a derecha ( side1tiene el valor left), se debe deshacer la selección para el elemento del lado izquierdo (ver el GIF al principio del artículo). Si se realiza una asignación de derecha a izquierda, la selección se deshace para el elemento del lado derecho. Esto significa que el último elemento en el que se hizo clic permanece en un estado seleccionado.

    Para ambos casos, preparo las funciones de controlador correspondientes handleSolvedAssignmenty handleFailedAssignment(eliminar función: ver el código fuente al final de este artículo):

    private handleSolvedAssignment(pair: Pair):void{ this.solvedPairs.push(pair); this.remove(this.unsolvedPairs, pair); this.leftpartUnselected.emit(); this.rightpartUnselected.emit(); //workaround to force update of the shuffle pipe this.test = Math.random() * 10;}private handleFailedAssignment(side1: string):void{ if(side1=="left"){ this.leftpartUnselected.emit(); }else{ this.rightpartUnselected.emit(); } }

    Ahora tenemos que cambiar el punto de vista del consumidor que suscribe los datos al productor que los genera. En el archivo match-game.component.html , me aseguro de que al hacer clic en un elemento, el objeto par asociado se inserte en la secuencia assignmentStream. Tiene sentido utilizar una secuencia común para el lado izquierdo y derecho porque el orden de la tarea no es importante para nosotros.

    div *ngFor="let pair of unsolvedPairs" (mousedown)="leftpartSelected.emit(pair.id)"(click)="assignmentStream.next({pair: pair, side: 'left'})"...div *ngFor="let pair of unsolvedPairs | shuffle:test" (mousedown)="rightpartSelected.emit(pair.id)" (click)="assignmentStream.next({pair: pair, side: 'right'})"

    Diseño de la interacción del juego con operadores RxJS.

    Todo lo que queda es convertir la secuencia assignmentStreamen secuencias solvedStreamy failedStream. Aplico los siguientes operadores en secuencia:

    pairwise

    Siempre hay dos parejas en una tarea. El pairwiseoperador recoge los datos en pares del flujo. El valor actual y el valor anterior se combinan en un par.

    De la siguiente transmisión…

    „{pair1, left}, {pair3, right}, {pair2, left}, {pair2, right}, {pair1, left}, {pair1, right}“

    …resultados de esta nueva transmisión:

    „({pair1, left}, {pair3, right}), ({pair3, right}, {pair2, left}), ({pair2, left}, {pair2, right}), ({pair2, right}, {pair1, left}), ({pair1, left}, {pair1, right})“ 

    Por ejemplo, obtenemos la combinación ({pair1, left}, {pair3, right})cuando el usuario selecciona dog(id=1) en el lado izquierdo y insect(id=3) en el lado derecho (ver matriz ANIMALSal principio del artículo). Estas y otras combinaciones resultan de la secuencia del juego que se muestra en el GIF de arriba.

    filter

    Tienes que eliminar todas las combinaciones de la transmisión que se realizaron en el mismo lado del campo de juego como ({pair1, left}, {pair1, left})o ({pair1, left}, {pair4, left}).

    Por tanto , la condición de filtrado para una combinación combes comb[0].side != comb[1].side.

    partition

    Este operador toma una secuencia y una condición y crea dos secuencias a partir de ellas. La primera secuencia contiene los datos que cumplen la condición y la segunda secuencia contiene los datos restantes. En nuestro caso, las transmisiones deben contener asignaciones correctas o incorrectas. Entonces la condición para una combinación combes comb[0].pair===comb[1].pair.

    El ejemplo da como resultado una secuencia "correcta" con

    ({pair2, left}, {pair2, right}), ({pair1, left}, {pair1, right}) 

    y una secuencia “incorrecta” con

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

    map

    Sólo se necesita el objeto de par individual para el procesamiento posterior de una asignación correcta, como por ejemplo pair2. El operador de mapa se puede utilizar para expresar que la combinación combdebe asignarse a comb[0].pair. Si la asignación es incorrecta, la combinación combse asigna a la cadena comb[0].sideporque la selección debe restablecerse en el lado especificado por side.

    La pipefunción se utiliza para concatenar los operadores anteriores. Los operadores pairwise, filter,, partitiondeben mapimportarse del paquete rxjs/operators.

    ngOnInit() { ... const stream = this.assignmentStream.pipe( pairwis 




    Tal vez te puede interesar:

    1. ¿Deberían abrirse los enlaces en ventanas nuevas?
    2. 24 excelentes tutoriales de AJAX
    3. 70 técnicas nuevas y útiles de AJAX y JavaScript
    4. Más de 45 excelentes recursos y repositorios de fragmentos de código

    Cómo crear un juego de combinación de cartas usando Angular y RxJS

    Cómo crear un juego de combinación de cartas usando Angular y RxJS

    Planificación y proceso del sistema de diseño, con Nathan Curtis Creación y mantenimiento de sistemas de diseño exitosos, con Brad Fost Índice

    programar

    es

    https://pseint.es/static/images/programar-como-crear-un-juego-de-combinacion-de-cartas-usando-angular-y-rxjs-1011-0.jpg

    2024-04-04

     

    Cómo crear un juego de combinación de cartas usando Angular y RxJS
    Cómo crear un juego de combinación de cartas usando Angular y RxJS

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20