Primeiros passos com os sinais do Angular

1. Antes de começar

Logotipo do Black Angular

Os sinais do Angular introduzem três primitivos reativos ao Angular que você conhece e adora, simplificando seu desenvolvimento e ajudando a criar apps mais rápidos por padrão.

O que você vai criar

  • Você aprenderá sobre os três primitivos reativos introduzidos com os sinais do Angular: signal(), computed() e effect().
  • Use os sinais do Angular para potencializar um jogo de criptografia do Angular. As criptografias são sistemas para criptografar e descriptografar dados. Neste jogo, os usuários podem decodificar uma mensagem secreta arrastando e soltando pistas para resolver uma cifra, personalizar a mensagem e compartilhar o URL para enviar mensagens secretas a amigos.

Jogo Angular Cypher no estilo de um console de jogos verde vintage, com uma mensagem oculta na tela do "Anqnxaa Lpcnaxl aaf pn jfafxyofa aofapfm pn a16 wyjak!"

Pré-requisitos

2. Acessar o código

Tudo o que você precisa para este projeto está em um Stackblitz. O Stackblitz é o método recomendado para trabalhar neste codelab. Como alternativa, clone o código e abra-o no seu ambiente de desenvolvimento favorito.

Abra o Stackblitz e execute o app

Para começar, abra o link do Stackblitz no seu navegador da Web favorito:

  1. Abra uma nova guia do navegador e acesse https://stackblitz.com/edit/io-signals-codelab-starter?file=src%2Fcipher%2Fservice.cipher.ts,src%2Fsecret-message%2Fservice.message.ts&service.massage.ts
  2. Bifurque o Stackblitz para criar seu próprio espaço de trabalho editável. O Stackblitz executará o app automaticamente, e você estará pronto para começar.

Alternativa: clonar o repositório e exibir o app

Usar o VSCode ou um ambiente de desenvolvimento integrado local é um método alternativo para trabalhar com este codelab:

  1. Abra uma nova guia do navegador e acesse https://github.com/angular/codelabs/tree/signals-get-started.
  2. Copie e clone o repositório e use o comando cd codelabs/ para acessar o repositório.
  3. Confira a ramificação do código inicial com o comando git checkout signals-get-started.
  4. Abra o código no VSCode ou no ambiente de desenvolvimento integrado de sua preferência.
  5. Para instalar as dependências necessárias para executar o servidor, use o comando npm install.
  6. Para executar o servidor, use o comando ng serve.
  7. Abra uma guia do navegador e acesse http://localhost:4200.

3. Definir um valor de referência

Seu ponto de partida é um jogo de criptografia do Angular, mas ele ainda não está funcionando. Os sinais do Angular vão melhorar a funcionalidade do jogo.

Jogo Angular Cypher no estilo de um console de jogos verde vintage, com uma mensagem oculta na tela do "Anqnxaa Lpcnaxl aaf pn jfafxyofa aofapfm pn a16 wyjak!"

Para começar, veja a versão final do que você criará: Cypher de sinais de Angular.

  1. Veja a mensagem codificada na tela.
  2. Arraste e solte o botão de letra no teclado para resolver a cifra e decodificar a mensagem secreta.
  3. Em caso de sucesso, veja como a mensagem é atualizada para decodificar mais mensagens secretas.
  4. Clique em Personalizar para alterar o remetente e a mensagem. Em seguida, clique em Criar e copiar URL para ver os valores na tela e o URL ser alterado.
  5. Bônus: copie e cole o URL em uma nova guia ou compartilhe com um amigo para ver como o remetente e a mensagem são armazenados no URL.

GIF do jogo Angular Cypher, com uma mensagem oculta na tela informando que "Angular Signals are in developer preview in v16 today!"

4. Definir seu primeiro sinal()

Um sinal é um valor que pode informar ao Angular quando ele mudar. Alguns indicadores podem ser alterados diretamente, enquanto outros calculam os valores deles. Juntos, os indicadores criam um gráfico direcionado de dependências que modela como os dados fluem no seu app.

O Angular pode usar as notificações de sinais para saber quais componentes precisam ser detectados para a alteração ou executar funções de efeito que você define.

Converter superSecretMessage em um signal()

superSecretMessage é um valor em MessageService que define a mensagem secreta decodificada pelo jogador. Atualmente, o valor não notifica o app sobre as mudanças, então o botão Personalizar está corrompido. Você pode resolver isso com um sinal.

Ao fazer com que o superSecretMessage sinalize, é possível notificar partes do app que dependem de saber quando a mensagem mudou. Ao personalizar a mensagem em uma caixa de diálogo, você definirá o sinal para atualizar o restante do app com a nova mensagem.

Para definir seu primeiro indicador, siga as etapas abaixo no comentário TODO(1): Define your first signal() em cada arquivo:

  1. No arquivo service.message.ts, use a biblioteca Signals para tornar superSecretMessage reativo:

src/app/secret-message/service.message.ts

superSecretMessage = signal(
  'Angular Signals are in developer preview in v16 today!'
);

Isso solicita automaticamente a importação de signal de @angular/core. Se você atualizar a página, provavelmente vai encontrar erros em que se referiu a superSecretMessage. Isso ocorre porque você alterou o tipo de superSecretMessage de string para SettableSignal<string>. Para corrigir isso, altere todas as referências de superSecretMessage para usar a API Signals. Sempre que você ler o valor, chame o getter de sinal superSecretMessage(). E sempre que você gravar o valor, use a API .set em SettableSignal para definir o novo valor para a mensagem.

  1. Nos arquivos secret-message.ts e service.message.ts, atualize todas as referências de superSecretMessage para superSecretMessage():

src/app/secret-message/secret-message.ts

// Before
this.messages.superSecretMessage
this.messages.superSecretMessage = message;

// After
this.messages.superSecretMessage()
this.messages.superSecretMessage.set(message);

src/app/secret-message/service.message.ts (link em inglês)

// Before
this.superSecretMessage

// After
this.superSecretMessage()

Explorar os outros dois sinais

  • Você tem dois outros sinais no app:

src/app/cipher/service.cipher.ts (link em inglês)

cipher = signal(this.createNewCipherKey());
decodedCipher = signal<CipherKey[]>([]);

O CipherService define um sinal cipher, um mapeamento gerado aleatoriamente de pares de chave-valor de uma letra do alfabeto para uma nova letra cipher. Use isso para embaralhar a mensagem e determinar se o player encontra uma correspondência bem-sucedida no teclado.

Você também tem um sinal decodedCipher dos pares de chave-valor decodificados que serão adicionados quando o player resolver a criptografia.

Um atributo exclusivo e poderoso do design da biblioteca de sinais do Angular é que você pode introduzir reatividade em qualquer lugar. Você definiu os sinais uma vez nos serviços do aplicativo e pode usá-los em um modelo, componentes, barras verticais, outros serviços ou em qualquer lugar em que possa escrever o código do aplicativo. Eles não estão limitados ou vinculados a um escopo de componente.

Verificar alterações

  • Você tem mais uma etapa a ser executada antes que o aplicativo funcione. Por enquanto, adicione um console.log() em diferentes partes do app para ver como o novo superSecretMessage está sendo definido.

Stackblitz com uma mensagem console.log() mostrando o superSecretMessage registrando corretamente a nova mensagem.

5. Defina seu primeiro compute()

Em muitas situações, você pode se deparar com um estado derivando dos valores existentes. É melhor atualizar o estado derivado quando o valor dependente muda.

Com o computed(), você pode expressar um sinal de maneira declarativa que deriva o valor dele de outros indicadores.

Converter solvedMessage em um computed()

solvedMessage converte o valor secretMessage de codificado para decodificado usando o sinal decodedCipher.

Isso é muito legal, porque é possível ver que você está extraindo um computado com base em outro computado. Assim, sempre que um sinal dentro desse contexto reativo mapeado, as dependências são notificadas.

Atualmente, o solvedMessage não é atualizado quando você altera secretMessage, decodedCipher ou superSecretMessage. Portanto, você não está vendo atualizações na tela quando o player resolve a criptografia.

Ao tornar solvedMessage um computado, você cria um contexto reativo para que, quando você atualizar a mensagem ou resolver a criptografia, possa derivar a atualização de estado das dependências rastreadas.

Para converter solvedMessage em uma computed(), siga estas etapas no comentário TODO(2): Define your first computed() em cada arquivo:

  1. No arquivo service.message.ts, use a biblioteca Signals para tornar solvedMessage reativo:

src/app/secret-message/service.message.ts (link em inglês)

solvedMessage = computed(() =>
  this.translateMessage(
    this.secretMessage(),
    this.cipher.decodedCipher()
  )
);

Isso solicita automaticamente a importação de computed de @angular/core. Se você atualizar a página, provavelmente vai encontrar erros em que se referiu a solvedMessage. Isso ocorre porque você alterou o tipo de superSecretMessage de string para Signal<string>, uma função. Isso pode ser corrigido alterando todas as referências de solvedMessage para solvedMessage().

  1. No arquivo secret-message.ts, atualize todas as referências de solvedMessage para solvedMessage():

src/app/secret-message/secret-message.ts

// Before
<span *ngFor="let char of this.messages.solvedMessage.split(''); index as i;" [class.unsolved]="this.messages.solvedMessage[i] !== this.messages.superSecretMessage()[i]" >{{ char }}</span>

// After
<span *ngFor="let char of this.messages.solvedMessage().split(''); index as i;" [class.unsolved]="this.messages.solvedMessage()[i] !== this.messages.superSecretMessage()[i]" >{{ char }}</span>

Ao contrário de superSecretMessage, solvedMessage não é um SettableSignal. Não é possível mudar o valor diretamente. Em vez disso, o valor dele é atualizado sempre que um dos indicadores de dependência (secretMessage e decodedCipher) é atualizado.

Explore as duas outras funções computed()

  • Você tem dois outros valores calculados no seu app:

src/app/secret-message/service.message.ts (link em inglês)

secretMessage = computed(() =>
  this.translateMessage(
    this.superSecretMessage(),
    this.cipher.cipher()
  )
);

src/app/cipher/service.cipher.ts (link em inglês)

unsolvedAlphabet = computed(() =>
  ALPHABET.filter(
    (letter) => !this.decodedCipher().find((guess) => guess.value === letter)
  )
);

O MessageService define um secretMessage calculado, o superSecretMessage codificado pelo cipher que os jogadores trabalham para resolver.

O CipherService define um unsolvedAlphabet calculado, uma lista de todas as letras que o jogador não resolveu, derivada da lista de chaves de criptografia resolvidas em decodedCipher.

Verificar alterações

Agora que superSecretMessage é um sinal e solvedMessage é computado, o app funcionará. Teste as funcionalidades do jogo:

  1. Arraste e solte um LetterGuessComponent em um LetterKeyComponent no CipherComponent para resolver a cifra e decodificar a mensagem secreta.
  2. Veja como o SecretMessageComponent é atualizado à medida que você decodifica mais da mensagem secreta.
  3. Clique em Personalizar para alterar o remetente e a mensagem. Em seguida, clique em Criar e copiar URL para ver os valores na tela e o URL ser alterado.
  4. Bônus: copie e cole o URL em uma nova guia ou compartilhe com um amigo para ver como o remetente e a mensagem são armazenados no URL.

GIF do jogo Angular Cypher, com uma mensagem oculta na tela informando que "Angular Signals are in developer preview in v16 today!"

6. Adicione seu primeiro efeito()

Há momentos em que você quer que algo ocorra quando um sinal tem um novo valor. Com o effect(), é possível programar e executar uma função de gerenciador em resposta à mudança de sinais.

Adicionar confete quando a criptografia for resolvida

Agora que o app está funcionando, você pode adicionar confetes quando a criptografia for resolvida e a mensagem secreta for decodificada.

Para adicionar confetes, siga as etapas abaixo no comentário TODO(3): Add your first effect():

  1. No arquivo cipher.ts, programe um efeito para adicionar confete quando a mensagem for decodificada:

src/app/cipher/cipher.ts

import * as confetti from 'canvas-confetti';

ngOnInit(): void {
  ...

  effect(() => {
    if (this.messages.superSecretMessage() === this.messages.solvedMessage()) {
      var confettiCanvas = document.getElementById('confetti-canvas');
      confetti.create()(confettiCanvas, { particleCount: 100 });
    }
  });
}

Observe como esse efeito depende de um sinal e de um valor calculado: this.messages.superSecretMessage() e this.messages.solvedMessage().

O efeito ajuda a programar a função de confetes dentro de um contexto reativo para rastrear e reavaliar quando as dependências são atualizadas.

Verificar alterações

  • Tente resolver a cifra. Dica: você pode alterar a mensagem para algo curto para testar mais rapidamente. Um confete vai parabenizar você no seu primeiro effect()!

GIF do jogo Angular Cypher, com uma mensagem oculta sendo decodificada na tela para soletrar "Confetti time!" e "confetes" tocando quando a mensagem é resolvida.

7. Parabéns!

Seu código do Angular está pronto para decodificar e compartilhar mensagens secretas. Tem uma mensagem para a equipe do Angular? Marque nossa mídia social em @Angular para que possamos decodificá-la. 🎉

Jogo do Angular Cypher resolvido com uma mensagem oculta na tela de "Angular Signals are in developer preview in v16 today!"

Agora você tem três primitivos reativos na caixa de ferramentas do Angular para simplificar o desenvolvimento e criar apps mais rápidos por padrão.

Saiba mais

Confira estes codelabs:

Leia estes materiais: