Junho de 2018

Volume 33 Número 6

O programador: Como ser MEAN: Programação reativa

Por Ted Neward | De 2018 junho

Bem-vindos de volta, MEANers.

Nos dois artigos anteriores (msdn.com/magazine/mt829391 e msdn.com/magazine/mt814413), muito falamos sobre o que Angular se refere a como "controlado por modelo formulários", que são formulários descrito por modelos Angular e estendidos com o uso de código para validação de controle e outra lógica. Isso é muito bom, mas um novo conceito sobre como exibir o fluxo"" de informações — e o controle resultante exibidos no código — dentro de uma Web aplicativo está começando a aproveitar imaginação dos desenvolvedores.

Posso falar, obviamente, "reativa" programação, e outra do ótimos turnos entre AngularJS (ou seja, Angular 1.0) e Angular (Angular 2.0 e posterior) é que Angular agora tem suporte explícito para um estilo reativo de programação, pelo menos no nível de Angular. Tudo isso é dizer, Angular oferece suporte ao uso de estilos reativos de programação em Angular sem a necessidade de confirmação de um estilo completamente reativo de arquitetura fora Angular.

Antes de me aprofundar, no entanto, é importante para garantir que todas as pessoas integrar com o que significa "reativa" aqui.

Reativas reações

Essencialmente, reativos estilos de programação são fáceis de entender, desde que você está disposto a inverter completamente sua perspectiva da forma tradicional de fazer a interface de programação. (Sim, que é uma piadas. Mas somente um pequeno.)

A abordagem tradicional do baseado em MVC para a criação de interfaces do usuário, os elementos de interface do usuário (botões, campos de texto e assim por diante) são criados e, em seguida,... você espera. O sistema processa mensagens do hardware e, quando uma combinação de mensagens indica que o usuário fez algo — clicou em um botão, digitado em uma caixa de texto ou qualquer outro — o código associado à aciona controles. Esse código geralmente modifica o modelo reside atrás de interface do usuário e o aplicativo de volta em espera.

Esse estilo "controlada por evento" de programação é intrinsecamente assíncrono, em que o código que o programador escreve sempre é orientado pelos quais o usuário não. De certa forma, seria melhor para chamá-lo programação "determinísticos", porque você não pode já sabe o que o usuário fará Avançar. Angular procurado facilitar essa tarefa criando uma associação bidirecional entre o modelo de dados e da interface do usuário, mas ainda pode ser difícil de manter a ordem das coisas simples.

O estilo reativo, no entanto, tem uma abordagem mais unidirecional. Controles são construídos em código (e não no modelo) e você programaticamente assinar eventos emitidos por esses controles para responder ao usuário fazer as coisas. Não é bem reagir estilo reativa programação, mas é bastante fechar e ainda permite que o mesmo tipo de estilo de "modelo de dados imutáveis" de programação que tem enamored uma porcentagem do setor.

Construção reativa

Para começar, vamos examinar construindo o componente SpeakerUI em um modo reativo, em vez de uma maneira controlada por modelo. (Aqui, vou vá com a convenção Angular mais tradicional e chamá-lo SpeakerDetail, mas o nome é totalmente irrelevante a discussão.) Primeiro, para ajudar a simplificar o que estou trabalhando com, vou usar a forma abreviada do apresentador e SpeakerService, conforme mostrado no Figura 1.

SpeakerService e locutor 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 ]);
  }
}

Observe que o SpeakerService é usando o método Promise para retornar uma promessa instantaneamente resolvido. Isso é uma maneira fácil para simular o serviço sem a necessidade de criar um objeto de promessa da maneira mais usando o construtor de promessa.

Em seguida, o componente SpeakerDetail é apenas um componente padrão do Angular ("ng novo componente locutor detalhes"), e o construtor deve inserir uma instância de SpeakerService como um parâmetro de construtor particular. (Isso detalhado na minha coluna de agosto de 2017, "como ser médio: Busca de desempenha angular"[msdn.com/magazine/mt826349].) Enquanto você faz isso, use o construtor para chamar o método de getSpeakers do SpeakerService para retornar a matriz e armazenar que localmente para o componente como uma propriedade chamada "alto-falantes." Até agora, isso parece muito o componente de modelo descrito anteriormente. O modelo HTML para este componente exiba uma lista suspensa de todos os alto-falantes do sistema (conforme obtida pelo getSpeakers) e como cada um é selecionada, exibir os detalhes de outro conjunto de controles sob essa lista suspensa. Portanto, o modelo de aparência Figura 2.

Figura 2 modelo de HTML

<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>

Pode parecer estranho alternativa "baseado em modelo" formulários usa modelos HTML. Isso ocorre em grande parte porque a abordagem reativa formulários não imediatamente com o modelo, mas em vez disso, remove a maioria da lógica para o modelo e para o componente. Isso é que as coisas começam tenham um turn esquerdo de técnicas examinadas em pares de colunas anteriores. O código do componente, na verdade, criará uma árvore de objetos de controle e a maioria das (se não todos) interação com os controles dessas duas formas acontecerá inteiramente dentro da base de código. Eu não colocar manipuladores de eventos no modelo. Em vez disso, eu vai conectá-las dentro do código do componente.

Mas primeiro, preciso criar os próprios controles. Esses serão todas as instâncias de FormControl, importadas do módulo @angular/forms, mas pode ser um pouco tediosa para construí-las cada (junto com qualquer validação necessária) manualmente no código. Felizmente, Angular fornece uma classe FormBuilder que foi projetado para tornar as coisas um pouco mais sucinta e compact para construir a partir do formulário inteira de controles, particularmente para formulários maiores (ou aninhados).

No caso do meu formulário locutor — onde quero ter uma lista suspensa de servir como um mecanismo de seleção para escolher quais locutor na lista para funcionar em — que preciso fazer algo diferente. Eu quero usar duas formas: um controle suspenso de seleção do apresentador e outra que contém os detalhes para cada pessoa. (Normalmente, isso seria dois componentes separados em um tipo de mestre-detalhes da organização, mas ainda não cobertos roteamento ainda). Portanto, preciso de duas formas e, em Figura 3 mostrar como construí-las, com o FormBuilder e sem.

Figura 3 Criando formulários

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]
    });
  }
  // ...
}

Isso é, logicamente, somente um trecho da classe, há algumas coisas mais preciso adicionar antes que ele esteja pronto para ser entregue, mas se esse código demonstra duas maneiras para construir um formulário. "selectGroup" é o FormGroup que contém o um FormControl para o controle HTML < select >, e usar o SpeakerService para preencher uma matriz de local de instâncias do apresentador para que < select > pode popular em si. (Isso é realmente o modelo, não no código do componente. Se houver uma maneira de preencher a lista suspensa do código do componente, não descobri-lo ainda Angular 5.)

O segundo formulário, chamado speakerForm, é preenchido usando FormBuilder, que é uma pequena pouco DSL linguagem para a criação de controles. Observe como posso chamar "group" para indicar que eu estou criando um grupo de controles, que são pares nome-valor com eficiência. O nome é o nome do controle (que deve corresponder a propriedade formControlName no modelo para cada controle) e o valor é um valor inicial ou um valor inicial, seguido por uma matriz de funções de validação (duas matrizes, se você quiser incluir validação funções que são executados de forma assíncrona) de execução para validar o tipo de usuários de valor ao controle.

Isso cria duas formas, mas selecionar algo na lista suspensa não faz nada. Necessário responder ao evento irá transmitir a lista suspensa e possa fazer isso por capturando uma função para o método "valueChanges" de FormControl, da seguinte forma:

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);
      }
    );
  }
  // ...
}

Observe que a propriedade valueChanges é, na verdade, um fluxo de eventos. Assim, posso usar forEach para indicar que para cada evento que é fornecido por meio de, desejo pegar o valor para o qual o controle foi alterado (o parâmetro lambda) e usá-la para localizar que no apresentador por sobrenome na matriz retornado pelo SpeakerService. Então colocar essa instância do apresentador e passá-lo para o método popular, mostrado aqui:

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);
  }
  // ...
}

O método popular simplesmente obtém a referência para o FormControls do grupo de speakerForm e usa o método setValue para preencher o valor apropriado de no apresentador para o controle apropriado.

Análise de reativo

Uma pergunta razoável neste ponto é como isso é melhor que os formulários baseados em modelo mostrados anteriormente. Francamente, não é uma questão de melhor — é uma questão de diferentes. (Os documentos Angular mesmo que nesse totalmente na página de documentação oficial em formulários reativo em angular.io/guide/reactive-forms.) Capturando a maior parte da lógica no código do componente (e não no modelo) pode strike alguns desenvolvedores como sendo mais lógica ou coesa, mas formulários reativos também tem a vantagem de que eles são essencialmente um reativo "loop": Porque os controles não mapeiam diretamente para o objeto de modelo subjacente. O desenvolvedor mantém controle total sobre o que acontece quando, que pode simplificar muito vários cenários.

A equipe Angular não acha que uma abordagem para formulários é inerentemente melhor que o outro, na verdade especificamente informando que os dois estilos podem ser usados dentro do mesmo aplicativo. (Não tentar usar ambos no mesmo componente, no entanto, a menos que você compreenda Angular muito profundamente.) A chave aqui é que Angular dá suporte a dois estilos diferentes de coleta de informações de usuários, e ambos são válidas e viável para vários cenários. Mas o que acontece se você tiver um grande número de formas diferentes que precisam ser exibidos ou o formulário que precise ser exibido depende do objeto de modelo subjacente, o que pode alterar? Angular oferece outra opção, o que você verá na próxima coluna. Boa codificação.


Ted Newardé consultor polytechnology em Seattle, palestrante e orientador, trabalhando no momento como diretor de engenharia e que Rela desenvolvedor em Smartsheet.com. Ele escreveu inúmeras artigos, criados ou coautoria de dezenas de livros e fala todos os sobre o mundo. Entre em contato com ele em ted@tedneward.com ou leia seu blog em blogs.tedneward.com.