← Angular: componentes, reatividade e decisões de aplicação
02 / 10 · 65 MIN

Signals, inputs e contratos de outputs

Separar estado gravável, derivações e entradas controladas pelo pai. Interpretar dependências reativas, transforms e eventos para conservar uma direção de dados explícita.

Estado gravável e leitura controlada

signal cria um contentor reativo com um valor inicial. Chamar quantity() lê o valor; set fornece um valor novo e update calcula-o a partir do atual. asReadonly expõe leitura sem os métodos de escrita, mas não congela profundamente arrays e objetos guardados.

Por isso, um consumidor que altera diretamente um array pode contornar a política de propriedade mesmo sem encontrar set. Mantém operações de escrita no proprietário e produz valores coerentes com a política de igualdade. A igualdade default é Object.is: mutar um objeto e voltar a fornecer a mesma referência não garante uma notificação.

Derivações e dependências dinâmicas

computed representa um valor derivado readonly. O cálculo é preguiçoso e memoizado: a primeira leitura calcula, leituras seguintes podem usar a cache e uma dependência alterada invalida o valor para a próxima leitura. Não se deve chamar set no resultado.

As dependências são as leituras efetivamente feitas. Se um ramo só lê count quando expanded é true, uma avaliação com expanded false não cria dependência de count. Uma avaliação posterior pode acrescentar essa dependência, e outra pode removê-la.

Isto explica por que nomes que aparecem no corpo da função não representam necessariamente subscrições permanentes.

Inputs estáticos e presença obrigatória

input declara entradas que Angular regista estaticamente em initializers de propriedades de componentes ou diretivas. Não se criam novos inputs dentro de um handler em resposta a dados. input(1) tem default 1; input<number>() sem default permite undefined. input.required<number>() exige que o uso no template forneça a entrada e não acrescenta undefined implicitamente ao tipo.

Essa verificação de build não prova que um número é válido para uma regra de negócio ou que uma resposta remota foi validada. O InputSignal é readonly: o filho lê value(), mas comunica uma proposta ao pai em vez de alterar a entrada diretamente.

Transforms, aliases e fronteiras de tipos

Uma transformação de input deve ser pura e estaticamente analisável. O tipo do seu parâmetro define o que a binding aceita; o resultado define o valor lido no signal. Uma função que recebe number e devolve uma string com px permite uma entrada numérica e uma leitura textual.

As transformações booleanAttribute e numberAttribute têm semântica própria: a primeira trata a string literal false como falso e a segunda pode produzir NaN quando não consegue converter. Um alias muda o nome usado no template, mas não a propriedade TypeScript. value com alias sliderValue continua a ser lido como value() na classe.

Outputs transportam intenção explícita

output devolve um emissor tipado. No exemplo, next.emit transporta o número proposto e $event entrega esse número ao listener do pai. Não é obrigatoriamente um MouseEvent: isso dependeria do payload que o componente escolhesse emitir.

Outputs Angular não fazem bubbling automático pelo DOM e os nomes distinguem maiúsculas de minúsculas. Um wrapper que deve participar no fluxo precisa de uma ligação explícita, por subscrição ou encaminhamento apropriado. O exemplo aceita a proposta imediatamente em estado local, mas outra aplicação pode aplicar uma política de validação antes de fornecer novas entradas.

Quando o contrato é bidirecional

model é uma entrada especial gravável que pode propagar mudanças para uma binding bidirecional. Se o controlo declara value = model(0), Angular cria também valueChange. Quando volume é um signal gravável no pai, o uso documentado é [(value)]="volume", passando a instância, não volume().

Isto difere do exemplo desta aula, que usa input readonly e um output de proposta separado. Escolhe conforme a responsabilidade pretendida: um controlo de valor pode expor model; um botão que solicita uma decisão pode comunicar intenção sem assumir aprovação.

Inputs model não suportam input transforms na documentação consultada.

Diagnóstico e resumo do fluxo

Para explicar uma atualização em falta, segue o percurso: houve uma escrita através da API? O valor foi considerado diferente? O consumidor leu esse signal num contexto rastreado?

Existe uma binding do output no nível correto? Não troques imediatamente todos os inputs por model nem todos os valores por estado duplicado. No exemplo, a sequência é 3 no pai, proposta 5 no filho, receção 5 no pai e novo input 5.

Cada passo tem uma responsabilidade identificável. A avaliação usa excertos e escolhas para testar essa leitura, sem exigir compilação, instalação ou execução de código.

// Reading example: parent owns quantity; child emits a proposed value.
import {Component, computed, input, output, signal} from '@angular/core';

@Component({
  selector: 'number-stepper',
  standalone: true,
  template: `<p>{{ label() }}</p>
    <button type="button" (click)="requestNext()">Request next</button>`,
})
export class NumberStepper {
  readonly value = input.required<number>();
  readonly step = input(1);
  readonly next = output<number>();
  readonly label = computed(() => `Quantity: ${this.value()}`);
  requestNext() { this.next.emit(this.value() + this.step()); }
}

@Component({
  selector: 'quantity-review',
  standalone: true,
  imports: [NumberStepper],
  template: `<number-stepper [value]="quantity()" [step]="2"
    (next)="quantity.set($event)" />`,
})
export class QuantityReview {
  readonly quantity = signal(3);
}
NA PRÁTICA

O pai começa com 3. O filho recebe o valor e step 2, calcula uma proposta 5 no clique e emite next. A binding do pai aceita essa proposta no signal local.

O filho não chama set no input. O label computed deriva da entrada atual. Não há validação de serviço, persistência ou autorização remota no excerto.

Armadilhas comuns

Usar set num computed ou input comum; esperar congelamento profundo de asReadonly; alterar arrays mantendo a mesma referência e assumir notificação; confundir alias de template com nome TypeScript; esperar bubbling de outputs pelo DOM.

Tópicos relacionados: Componentes, templates e identidade da vista · Ciclo de vida, efeitos e limpeza reativa · Formulários, validação e intenção do utilizador

Leva esta ideia contigo

Declara quem escreve e quem lê. Derivações computed seguem leituras efetivas; inputs comuns são readonly e outputs comunicam dados explicitamente. Model serve outro contrato, o de escrita e binding bidirecional. A sintaxe não substitui validação de dados ou regras de negócio.

Criar conta

Referência: Writable signals and updates · Angular 22; standalone components