Alura > Cursos de Front-end > Cursos de Angular > Conteúdos de Angular > Primeiras aulas do curso Angular: gerenciando estado com Signals

Angular: gerenciando estado com Signals

Sinais graváveis e computados - Apresentação

Boas-vindas ao curso de Angular Signals! Meu nome é Thamiris Adriano, sou coordenadora da Pós Tech na FIAP, instrutora na Alura, e também engenheira de software na Mullet.

Audiodescrição: Thamiris se descreve como uma mulher branca, de cabelos lisos longos pintados de loiro, e olhos castanho-escuros. Ela usa óculos de armação arredondada preta, veste uma camiseta preta com o logotipo branco da Pós Tech, e está sentada em uma cadeira preta, em frente a um microfone preto no estúdio da Alura, composto por uma parede clara ao fundo iluminada em gradiente azul e roxo, com uma estante preta à esquerda da instrutora contendo enfentes, plantas e pontos de iluminação amarela.

O que vamos aprender?

Neste curso, aprenderemos coisas interessantes relacionadas a química. A primeira coisa que vamos aprender será como criar um componente que trará uma lista de elementos químicos. A partir dessa lista, apresentaremos detalhes sobre cada elemento químico, tudo utilizando os signals.

Depois, entenderemos como alterar o estado físico dos elementos conforme a temperatura, a partir de uma simulação. Além disso, criaremos um componente para favoritar e remover dos favoritos.

Por último, vamos aprender calcular a soma da massa de elementos químicos de listas diferentes, clicando em elementos distintos e obtendo a massa total desses elementos juntos.

Neste curso, aprenderemos o que são os sinais graváveis, os sinais computados e a diferença entre eles. Vamos explorar também o que são os effects e utilizá-los para montar um componente, implementando toda a lógica do estado físico dos elementos químicos.

Além disso, conseguiremos aprimorar nossa aplicação, mantendo toda a lógica da aplicação no lugar adequado, isto é, dentro do serviço, e deixando os componentes mais limpos.

Por fim, aprenderemos a compartilhar os signals e a estabelecer uma comunicação eficiente entre os componentes, pensando sempre na performance da aplicação. Dessa forma, buscaremos otimizá-la constantemente para que ela seja ágil e performática.

Quais são os requisitos?

Para melhor acompanhamento do conteúdo, é importante ter noções básicas de Angular, JavaScript, TypeScript, HTML e CSS, pois o foco do nosso curso será Angular.

Conclusão

Esperamos que aproveite o conteúdo que preparamos. Te aguardamos na primeira aula!

Sinais graváveis e computados - Criando um sinal gravável (writable signal) para armazenar informações

Com o projeto pronto, a primeira funcionalidade que vamos inserir será uma lista interativa de elementos químicos. O projeto já está montado e em execução, mas ainda não possui muitas funcionalidades. Atualmente, temos apenas uma introdução no HTML com informações básicas. Dito isso, nosso primeiro desafio será criar essa lista conforme o protótipo do Figma.

Sinais graváveis

Uma vez criada a lista de elementos químicos, ao clicar em qualquer um deles, exibiremos informações mais detalhadas, como o nome, o símbolo e o número atômico.

No entanto, queremos fazer isso sem sobrecarregar a aplicação, evitando que o site fique lento, pois isso afugenta as pessoas usuárias. Para isso, utilizaremos os signals, que têm o poder de atualizar automaticamente a interface sem sobrecarregar o sistema.

Criando a interface Elemento

Com o arquivo signals-intro.component.ts aberto, criaremos o contrato, que será uma interface chamada Elemento. Ao criar uma interface, informamos ao Angular que, ao trabalhar com propriedades como nome, simbolo e numeroMassa, usaremos o mesmo tipo de dado.

Ao trabalhar com a propriedade nome, por exemplo, vamos garantir que o nome será sempre uma string. Se colocarmos um tipo de dado diferente, o Angular avisará, evitando erros futuros. O símbolo (simbolo) também será string, enquanto o número de massa (numeroMassa) será number.

signals-intro.component.ts:

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

interface Elemento {
  nome: string;
  simbolo: string;
  numeroMassa: number; 
}

@Component({
  selector: 'app-signals-intro',
  templateUrl: './signals-intro.component.html',
  styleUrls: ['./signals-intro.component.css']
})
export class SignalsIntroComponent {

}

Declarando a variável elementoSelecionado

Criada a interface, o próximo passo será declarar a variável reativa. Abaixo da classe SignalsIntroComponent, criaremos o sinal gravável, responsável por armazenar as informações do elemento. Podemos pensá-lo como um bloco de notas que guarda dados para uso posterior.

Inicialmente, ele não fará nada com essas informações; vamos apenas criar uma variável chamada elementoSelecionado, do tipo signal. Feito isso, usaremos o contrato criado (Elemento) e evidenciaremos que a variável começará como nula (null).

// código omitido

export class SignalsIntroComponent {
  elementoSelecionado = signal<Elemento | null>(null);
}

Assim, a variável guardará as informações para quando precisarmos delas.

Se houver um erro no signal, será necessário atualizar a importação. Ao clicar sobre signal, podemos usar a opção "Quick Fix…" para atualizar a importação do signal no @angular/core.

import { Component, signal } from '@angular/core';

Criando a lista elementos

Com a interface e a variável criadas, vamos trazer a lista de elementos químicos.

Esses dados serão inseridos no arquivo signals-intro.component.ts, mas também poderíamos buscar esses dados de uma API. Para fins didáticos, traremos as informações diretamente.

No escopo da classe SignalsIntroComponent, logo após a declaração de elementoSelecionado, vamos colar a lista de elementos químicos, que segue o contrato criado com a interface Elemento.

// código omitido

export class SignalsIntroComponent {
  elementoSelecionado = signal<Elemento | null>(null);

  elementos: Elemento[] = [
    { nome: 'Hidrogênio', simbolo: 'H', numeroMassa: 1 },
    { nome: 'Carbono', simbolo: 'C', numeroMassa: 12 },
    { nome: 'Nitrogênio', simbolo: 'N', numeroMassa: 14 },
    { nome: 'Oxigênio', simbolo: 'O', numeroMassa: 16 },
    { nome: 'Sódio', simbolo: 'Na', numeroMassa: 23 },
    { nome: 'Cloro', simbolo: 'Cl', numeroMassa: 35 }
  ];
}

Por exemplo: o primeiro elemento é o Hidrogênio, cujo símbolo é H e o número de massa é 1. Note que obedecemos ao contrato, garantindo que nome e simbolo são string e numeroMassa é number.

Criando a função selecionarElemento()

Criamos a interface, o sinal gravável que armazena as informações, e a lista de elementos que guarda as informações para quando a pessoa usuária interagir. Dessa forma, quando a pessoa usuária clicar sobre os elementos químicos, traremos informações mais detalhadas da lista.

Agora, precisamos de uma função que altere as informações do elemento selecionado. Para isso, criaremos a função selecionarElemento() — cujo nome faz sentido, pois ela será usada para selecionar um elemento —, do tipo elemento: Elemento.

No escopo da função, vamos alterar a variável elementoSelecionado com o método set(), responsável por atualizar informações e mantê-las atualizadas.

// código omitido

export class SignalsIntroComponent {
  elementoSelecionado = signal<Elemento | null>(null);

  elementos: Elemento[] = [
    { nome: 'Hidrogênio', simbolo: 'H', numeroMassa: 1 },
    { nome: 'Carbono', simbolo: 'C', numeroMassa: 12 },
    { nome: 'Nitrogênio', simbolo: 'N', numeroMassa: 14 },
    { nome: 'Oxigênio', simbolo: 'O', numeroMassa: 16 },
    { nome: 'Sódio', simbolo: 'Na', numeroMassa: 23 },
    { nome: 'Cloro', simbolo: 'Cl', numeroMassa: 35 }
  ];

  selecionarElemento(elemento: Elemento) {
    this.elementoSelecionado.set(elemento);
  }
}

Conclusão

Assim, finalizamos a função que recebe o elemento e mantém as informações atualizadas. Com o sinal gravável, a lista e a função, podemos ir para a próxima etapa. Nos encontramos na sequência!

Sinais graváveis e computados - Aplicando um sinal computado (computed signal) para atualizar valores e exibir dados

Até o momento, trabalhamos com o SignalsIntroComponent (signals-intro.component.ts), único componente criado no projeto. Ao baixar o projeto, ele estará presente. Assim, o código CSS (arquivo signals-intro.component.css) já estará atualizado e não precisaremos nos aprofundar nele.

No arquivo HTML (signals-intro.component.html), por enquanto, temos apenas um texto de introdução, e até então, trabalhamos no componente .ts, conforme abordado no vídeo anterior.

Nesse componente, criamos a interface Elemento, declaramos o sinal gravável elementoSelecionado, definimos a lista de elementos químicos elementos contendo nome, simbolo e numeroMassa, e também criamos a função selecionarElemento().

Agora, precisamos atualizar o HTML para exibir algo na aplicação.

Sinais computados

No momento, a aplicação não reflete nada do que fizemos em código, pois antes de atualizar o HTML, precisamos criar o sinal computado, função que vamos fazer para exibir os itens na tela.

Criando a função elementoInfo

Nosso primeiro passo será criar o sinal computado no arquivo signals-intro.component.ts. O sinal computado é uma função, que nomearemos como elementoInfo, pois ela trará as informações detalhadas. É importante nomear as funções de modo a detalhar exatamente o que elas fazem.

Essa função será do tipo computed() e precisaremos importá-lo do @angular/core. Faremos isso da mesma forma que fizemos com os signals: clicaremos sobre computed() e selecionaremos a opção "Quick Fix…" para importar a função.

Para finalizar, entre os parênteses de computed(), adicionaremos uma arrow function (() =>).

signals-intro.component.ts:

import { Component, computed, signal } from '@angular/core';

// código omitido

export class SignalsIntroComponent {
  elementoSelecionado = signal<Elemento | null>(null);

  elementos: Elemento[] = [
    { nome: 'Hidrogênio', simbolo: 'H', numeroMassa: 1 },
    { nome: 'Carbono', simbolo: 'C', numeroMassa: 12 },
    { nome: 'Nitrogênio', simbolo: 'N', numeroMassa: 14 },
    { nome: 'Oxigênio', simbolo: 'O', numeroMassa: 16 },
    { nome: 'Sódio', simbolo: 'Na', numeroMassa: 23 },
    { nome: 'Cloro', simbolo: 'Cl', numeroMassa: 35 }
  ];

  selecionarElemento(elemento: Elemento) {
    this.elementoSelecionado.set(elemento);
  }

  elementoInfo = computed(() => {

  });
}

Declarando a variável elemento

Agora, precisamos declarar as variáveis (const). No escopo da arrow function, a primeira variável será elemento, que será responsável por trazer o elemento selecionado (this.elementoSelecionado()), sinal gravável criado anteriormente.

Observação: utilizamos this por chamar elementoSelecionado fora do escopo.

O elementoSelecionado será alterado sempre que a pessoa usuária clicar em um elemento diferente.

// código omitido

elementoInfo = computed(() => {
  const elemento = this.elementoSelecionado();
});

// código omitido

Feito isso, vamos adicionar um return logo abaixo. Nesse momento, poderíamos trazer apenas elemento e as informações necessárias, mas o ideal seria construir uma condição.

Estabelecendo condições

Atualizando o HTML, ele trará as informações de cada elemento. No entanto, antes de ter qualquer elemento selecionado, ele não terá nenhuma mensagem de que a aplicação está funcionando, o que pode gerar dúvidas para a pessoa usuária. Será que a aplicação funciona de fato?

Para evitar esse tipo de dúvida, vamos estabelecer duas condições. A primeira condição é:

Para representar isso, após uma interrogação (?), adicionaremos entre crases o texto "Nome:" seguido do símbolo ${}, onde traremos as informações do elemento, como elemento.nome.

Lembre-se! Essas informações vêm da lista de elementos (elementos) criada acima.

Além disso, traremos o elemento de símbolo (${elemento.simbolo}) após o texto "Símbolo:". Por fim, vamos trazer o número de massa (${elemento.numeroMassa}) com o texto "Número de massa:".

// código omitido

elementoInfo = computed(() => {
  const elemento = this.elementoSelecionado();
  return elemento
    ? `Nome: ${elemento.nome}, Símbolo: (${elemento.simbolo}), Número de Massa: ${elemento.numeroMassa}`
});

// código omitido

Por enquanto, temos apenas essas três propriedades: nome; simbolo; e numeroMassa.

Agora, podemos estabelecer a segunda condição:

Para isso, vamos exibir uma mensagem indicando que a aplicação funciona, mas nenhum elemento foi selecionado ainda. Portanto, adicionamos entre aspas simples "Nenhum elemento selecionado".

// código omitido

elementoInfo = computed(() => {
  const elemento = this.elementoSelecionado();
  return elemento
    ? `Nome: ${elemento.nome}, Símbolo: (${elemento.simbolo}), Número de Massa: ${elemento.numeroMassa}`
    : 'Nenhum elemento selecionado';
});

// código omitido

Atualizando o código HTML

Uma vez atualizado o arquivo .ts, para exibir tudo na tela, precisamos atualizar o arquivo HTML. No entanto, o objetivo desta aula não é estudar HTML. Para isso, existem outros cursos ótimos na plataforma da Alura que abordam o tema. Por ora, apenas traremos as informações.

signals-intro.component.html:

<div>
  <h2>Elementos Químicos</h2>
  <ul>
    <li
      *ngFor="let elemento of elementos"
      (click)="selecionarElemento(elemento)"
    >
      {{ elemento.nome }}
    </li>
  </ul>

  <p *ngIf="elementoSelecionado()">
    Elemento Selecionado: {{ elementoSelecionado()?.nome }}
  </p>

  <p>Informações Detalhadas: {{ elementoInfo() }}</p>
</div>

Basicamente, a lista (ul) de elementos químicos do Figma traz todos os elementos químicos disponíveis na aplicação. Trazemos somente o nome deles (elemento.nome), então apenas o nome estará disponível para clicar. Ao clicar sobre ele, exibidos as informações detalhadas.

No item de lista li, trazemos no método click a função selecionarElemento(). Quando tivermos o elementoSelecionado, serão trazidas as informações de elementoInfo() e também do elementoSelecionado. Assim, teremos o nome e também os detalhes do elemento.

Testando a aplicação

Com o CSS e o HTML prontos, temos o aplicativo em funcionamento. Se clicarmos em "Oxigênio", por exemplo, teremos acesso às informações do elementoSelecionado e do elementoInfo, onde encontramos nome, símbolo e número de massa.

Recapitulando o que fizemos

No arquivo signals-intro.component.ts, criamos o sinal gravável elementoSelecionado, que permite que o estado seja armazenado e atualizado diretamente. Nesse caso, usamos ele para armazenar as informações do elemento químico selecionado.

No elementoInfo, temos o sinal computado elemento. Sinais computados são valores derivados de outros sinais. Isso significa que, quando usamos os sinais computados, eles atualizam automaticamente as informações e recalculam as informações com base nos sinais graváveis.

Para usar a função que contém o sinal computado, precisamos utilizar a função computed(), importada do @angular/core. Esse sinal computado é responsável por atualizar automaticamente as informações com base no sinal gravável que trouxemos de elementoSelecionado().

No entanto, isso só é feito quando realmente há mudança no valor, o que torna a aplicação muito mais leve, uma vez que as informações não são atualizadas o tempo todo.

Conclusão

Com isso, nossa primeira tarefa foi entregue e conseguimos criar uma lista interativa de elementos sem sobrecarregar a aplicação. No entanto, ainda existem algumas funcionalidades adicionais para incluir no projeto. Te aguardamos no próximo vídeo!

Sobre o curso Angular: gerenciando estado com Signals

O curso Angular: gerenciando estado com Signals possui 103 minutos de vídeos, em um total de 47 atividades. Gostou? Conheça nossos outros cursos de Angular em Front-end, ou leia nossos artigos de Front-end.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Angular acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas