Compartir a través de



Junio de 2018

Volumen 33; número 6

El programador ocupado: cómo dominar MEAN: Programación reactiva

Por Ted Neward | De 2018 de junio

Bienvenidos de nuevo, usuarios de MEAN.

En los dos artículos anteriores (msdn.com/magazine/mt829391 y msdn.com/magazine/mt814413), hablado mucho lo que hace referencia Angular como "controladas por plantillas forms", que son formas descrito por plantillas Angular y extender mediante el uso de código para controlar la validación y cualquier otra lógica. Que es muy bien, pero un nuevo concepto acerca de cómo ver el "intercambio" de información, y el control resultante se muestra en el código, dentro de un sitio Web está iniciando la aplicación asumir imaginación de los desarrolladores.

Hablan, por supuesto, de "reactiva" de programación y uno de los turnos excelentes entre AngularJS (es decir, Angular 1.0) y Angular (Angular 2.0 y posteriores) es que Angular tiene ahora compatibilidad explícita para un estilo reactivo de la programación, por lo menos en el nivel de Angular. Todos ellos consiste en indicar, Angular admite el uso de reactivos estilos de programación en Angular sin tener que confirmar en un estilo de arquitectura fuera Angular completamente reactivo.

Sin embargo, antes de que descubra, es útil para asegurarse de que todos los usuarios incorporar con lo que significa "reactiva" aquí.

Reacciones reactivas

Básicamente, reactivos estilos de programación son fáciles de entender, siempre y cuando esté dispuesto a invertir completamente su perspectiva en la forma tradicional de hacer la programación de la interfaz de usuario. (Sí, es un humor. Pero solo una pequeña).

En el enfoque tradicional de basada en MVC para la creación de interfaces de usuario, se crean los elementos de interfaz de usuario (botones, campos de texto y similares) y, a continuación,... espere. El sistema procesa los mensajes del hardware y una combinación de mensajes indica que el usuario ha hecho algo, hace clic en un botón, escrito en un cuadro de texto o cualquier otra, el código enlaza a la se desencadena de controles. Normalmente, ese código modifica el modelo que se encuentra detrás de la interfaz de usuario y la aplicación regresa en espera.

Este estilo "orientada a eventos" de la programación es intrínsecamente asincrónica, en que el código que el programador escribe siempre está controlado por qué el usuario no. En cierto modo, sería mejor para que se llame "indeterministas" de programación, porque no se sabe alguna vez lo el usuario va a hacer a continuación. Busca angular facilitar esta tarea mediante la creación de un enlace bidireccional entre el modelo de datos y la interfaz de usuario, pero puede ser difícil de mantener el orden de cosas rectas.

El estilo reactivo, sin embargo, adopta un enfoque más unidireccional. Los controles se construyen en código (en lugar de en la plantilla), y mediante programación suscribirse a eventos emitidos por dichos controles para responder al usuario hacer las cosas. No es bastante reaccionan reactiva programación de estilo, pero resulta bastante estrecha y todavía permita realizar la misma clase de estilo "modelo de datos inmutables" de la programación que se enamored un porcentaje de la industria.

Construcción reactiva

Para empezar, echemos un vistazo a construir el componente SpeakerUI de manera reactiva, en lugar de una manera controlada por la plantilla. (En este caso, voy a ir con la convención Angular más tradicional y llámelo SpeakerDetail, pero es irrelevante a la discusión, el nombre). En primer lugar, con el fin de ayudar a simplificar lo que estoy trabajando con, deberá usar la forma abreviada de altavoces y SpeakerService, tal y como se muestra en figura 1.

SpeakerService y el altavoz de la figura 1

import { Injectable } from '@angular/core';

export class Speaker {
  id = 0
  firstName = ""
  lastName = ""
  age = 0
}

@Injectable()
export class SpeakerService {
  private speakers : Speaker[] = [
    {
      id: 1,
      firstName: 'Brian',
      lastName: 'Randell',
      age: 47,
    },
    {
      id: 2,
      firstName: 'Ted',
      lastName: 'Neward',
      age: 46,
    },
    {
      id: 3,
      firstName: 'Rachel',
      lastName: 'Appel',
      age: 39,
    }
  ]
    getSpeakers() : Promise<Array<Speaker>> {
    return Promise.resolve(this.speakers);
  }
  getSpeaker(id: number) : Promise<Speaker> {
    return Promise.resolve(this.speakers[id - 1 ]);
  }
}

Tenga en cuenta que el SpeakerService está usando el método Promise.resolve para devolver una promesa que se resuelve al instante. Se trata de una manera sencilla de simular el servicio sin tener que crear un objeto de compromiso de la manera más usando el constructor de compromiso.

A continuación, el componente de SpeakerDetail es simplemente un componente estándar del Angular ("ng nuevo componente altavoz-detail"), y el constructor debe insertar una instancia de SpeakerService como un parámetro de constructor privado. (Esto se detalla en mi columna de agosto de 2017, "cómo como medio: Fetch desempeña angular"[msdn.com/magazine/mt826349].) Mientras se encuentra en él, utilice el constructor para que llame al método de getSpeakers del SpeakerService para devolver la matriz y almacenar localmente en el componente como una propiedad denominada "altavoces". Hasta ahora, esto parece mucho el componente basadas en plantillas que se ha descrito anteriormente. La plantilla HTML para este componente se mostrará una lista desplegable de todos los altavoces en el sistema (que se obtienen mediante getSpeakers) y, a continuación, que cada uno de ellos se ha seleccionado, se muestran los detalles de otro conjunto de controles debajo de esa lista desplegable. Por lo tanto, la plantilla es similar a figura 2.

Plantilla de HTML de la figura 2

<h2>Select Speaker</h2>
<form [formGroup]="selectGroup">
<label>Speaker:
  <select formControlName="selectSpeaker">
    <option *ngFor="let speaker of speakers"
            [value]="speaker.lastName">{{speaker.lastName}}</option>
  </select>
</label>
</form>

<h2>Speaker Detail</h2>
<form [formGroup]="speakerForm" novalidate>
  <div>
    <label>First Name:
      <input formControlName="firstName">
    </label>
    <label>Last Name:
      <input formControlName="lastName">
    </label>
    <label>Age:
      <input formControlName="age">
    </label>
  </div>
</form>

Puede parecer extraño que la alternativa a los formularios "basadas en plantillas" usa plantillas de HTML. Que es en gran medida porque el enfoque reactivo formularios no hace inmediatamente con la plantilla, pero en su lugar, quita la mayor parte de la lógica de fuera de la plantilla y en el componente. Esto es donde cosas podrán realizar un giro a la izquierda de las técnicas que se examina en el par de columnas anterior. El código de componente realmente construirá un árbol de objetos de control y se realizará la mayoría (si no todos) interacción con los controles de estas dos formas completamente dentro de la base de código. No incluir controladores de eventos en la plantilla. En su lugar, podrá enlazar ellos dentro del código de componente.

Pero, en primer lugar, se necesita construir los propios controles. Todos serán instancias FormControl, importados desde el módulo @angular/forms, pero puede obtener un poco tedioso construirlas cada (junto con las validaciones necesarias) manualmente en el código. Afortunadamente, Angular proporciona una clase FormBuilder que ha diseñado para facilitar un poco más concisa y compacto para construir correspondientes de un formulario completo de controles, especialmente para los formularios más tiempo (o anidadas).

En el caso de mi forma de altavoces: donde desea tener una lista desplegable que actúan como un mecanismo de selección para elegir qué altavoces en la lista para que funcione en: tengo que hacer algo diferente. Deseo tienen dos formas: uno alrededor del control de selección de altavoz de la lista desplegable y la otra que contiene los detalles para cada usuario individual. (Normalmente sería dos componentes independientes en un tipo de principal-detalle de organización, pero que no he tratado enrutamiento todavía). Por lo tanto, se necesitan dos formas y en figura 3 muestra cómo crear, con el FormBuilder y sin.

Figura 3 construir las formas

export class SpeakerDetailComponent implements OnInit {
  selectGroup : FormGroup
  speakerForm : FormGroup
  speakers : Speaker[]

  constructor(private formBuilder : FormBuilder,
              private speakerService : SpeakerService) {
    speakerService.getSpeakers().then( (res) => { this.speakers = res; });
    this.createForm();
  }

  createForm() {
    this.selectGroup = new FormGroup({
      selectSpeaker : new FormControl()
    });

    this.speakerForm = this.formBuilder.group({
      firstName: ['', Validators.required],
      lastName: ['', Validators.required],
      age: [0]
    });
  }
  // ...
}

Por supuesto, esto es sólo un extracto de la clase, hay algunas cosas más necesito para agregar antes de que está listo para enviar, pero claridad, este código muestra dos de las formas de crear un formulario. "selectGroup" es el FormGroup que contiene el control uno FormControl para el control HTML < select >, y se usa el SpeakerService para rellenar una matriz local de instancias de altavoces, por lo que puede rellenar el elemento < select > Sí. (Esto es realmente en la plantilla, no en el código de componente. Si no hay una forma de rellenar la lista desplegable desde el código de componente, he no encontré lo todavía en 5 Angular.)

El segundo formulario, denominado speakerForm, se rellena con el FormBuilder, que es un lenguaje similar a DSL poco pequeño para construir controles. Tenga en cuenta cómo llamar a "group" para indicar que me construir un grupo de controles, que son los pares de nombre-valor eficazmente. El nombre es el nombre del control (que debe coincidir con la propiedad formControlName en la plantilla para cada control) y el valor es un valor inicial o un valor inicial seguido de una matriz de funciones de validación (dos matrices, si desea incluir la validación ejecución de las funciones que se ejecutan de forma asincrónica) para validar el tipo de los usuarios de valor al control.

Esto crea los dos formularios, pero seleccionando algo en la lista desplegable no hace nada. Se necesita responder al evento difundirá la lista desplegable y puedo hacerlo al enlazar una función en el método "valueChanges" de FormControl, así:

export class SpeakerDetailComponent implements OnInit {
  // ...
  ngOnInit() {
    const speakerSelect = this.selectGroup.get('selectSpeaker');
    speakerSelect.valueChanges.forEach(
      (value:string) => {
        const speaker = this.speakers.find( (s) => s.lastName === value);
        this.populate(speaker);
      }
    );
  }
  // ...
}

Tenga en cuenta que la propiedad valueChanges es realmente un flujo de eventos. Por lo tanto, utilizar forEach para indicar que para cada evento que se incluye a través, quiere tomar el valor al que el control ha cambiado (el parámetro a la expresión lambda) y usarlo para buscar que el altavoz por apellido en la matriz devuelta por la SpeakerService. A continuación, sacar esa instancia de altavoces y pasarlo al método rellenar, que se muestra aquí:

export class SpeakerDetailComponent implements OnInit {
  // ...
  populate(speaker) {
    const firstName = this.speakerForm.get('firstName');
    const lastName = this.speakerForm.get('lastName');
    const age = this.speakerForm.get('age');
    firstName.setValue(speaker.firstName);
    lastName.setValue(speaker.lastName);
    age.setValue(speaker.age);
  }
  // ...
}

El método rellenar simplemente toma la referencia a la FormControls del grupo speakerForm y utiliza el método setValue para rellenar el valor adecuado de los altavoces para el control adecuado.

Análisis reactiva

Una pregunta razonable correspondiente en este momento es cómo esto es mejor que los formularios basados en plantillas mostrados anteriormente. Sincero, no es cuestión de mejor: es una cuestión de diferentes. (Los documentos Angular incluso supongamos este afirmaciones completamente en la página de documentación oficial en los formularios de reactiva en angular.io/guide/reactive-forms.) Capturar la mayor parte de la lógica en el código de componente (en lugar de en la plantilla) puede lograr algunos desarrolladores como más productiva o lógico, pero reactivas forms también tienen la ventaja que son esencialmente un reactivo "bucle": Dado que los controles no se asignen directamente al objeto del modelo subyacente. El desarrollador mantiene el control completo sobre lo que sucede cuando, que puede simplificar enormemente una serie de escenarios.

El equipo Angular no considera que un enfoque de formularios es inherentemente mejor que el otro, de hecho específicamente que indica que se pueden utilizar ambos estilos dentro de la misma aplicación. (No intente usar tanto en el mismo componente, sin embargo, a menos que comprenda realmente profundamente Angular.) La clave aquí es que Angular admite dos estilos diferentes de recopilación de entrada de los usuarios, y ambos son válidos y viable para un buen número de escenarios. Pero ¿qué ocurre si tiene un gran número de distintas formas que deben mostrarse o el formulario que tenga que mostrarse depende del modelo de objetos subyacente, que puede cambiar? Angular ofrece otra opción, que verá en la columna siguiente. ¡Que disfrute programando!


Ted Newardes un consultor polytechnology con sede en Seattle, altavoces y profesor, trabajando actualmente como el director de ingeniería y desarrollador Rela-ciones en Smartsheet.com. Ha escrito una gran cantidad de artículos, que se crearon o creación compartida de una docena de libros y habla al mundo entero. Puede ponerse en contacto con él en ted@tedneward.com o leer su blog en blogs.tedneward.com.