Angular

3 jul, 2026

Angular 11 moderno: passando dados para componentes filhos com input()

Publicidade

Esta é a lição 11 do Curso de Angular Moderno . Na lição anterior, renderizamos uma lista de produtos usando sinais e `$(‘product’)` @for. Cada card ainda exibe conteúdo de espaço reservado. Agora é hora de conectar dados reais do componente pai ao componente filho usando input()a API de sinais moderna do Angular.

Este post faz parte da série do Curso de Angular Moderno . Confira a página do curso para ver a lista completa de episódios.

Neste post, abordaremos:

  • O padrão de comunicação entre pais e filhos
  • Criando uma entrada obrigatória cominput.required<T>()
  • Renderizando dados de entrada no modelo filho.
  • Passando dados do pai para o filho com vinculação de propriedade
  • Utilizando entradas opcionais com valores padrão.
  • Armadilhas comuns a evitar

O Modelo Mental

Antes de escrever qualquer código, vamos alinhar o padrão:

  • O componente pai é o proprietário dos dados (a lista de produtos).
  • O processo pai percorre os produtos com@for
  • O pai/mãe passa um produto para cada cartão infantil.
  • A criança recebe dados input()e os renderiza.

Esta é a comunicação clássica entre pais e filhos, mas utilizando entradas modernas baseadas em sinais. É a primeira forma de comunicação por componentes que abordamos neste curso.

Criando uma entrada obrigatória

Abra product-card.tse adicione uma entrada obrigatória para o produto:

import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { Product } from '../product';

@Component({
  selector: 'app-product-card',
  imports: [MatCardModule, MatButtonModule],
  templateUrl: './product-card.html',
  styleUrl: './product-card.scss',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProductCard {
  readonly product = input.required<Product>();
}

A frase-chave:

readonly product = input.required<Product>();

Eis o que isso significa:

  1. input.requiredmarca esta entrada como obrigatória
  2. <Product>nos dá tipagem rigorosa
  3. A entrada é baseada em sinal, então a lemos comproduct()

Se o componente pai se esquecer de passar esse parâmetro, o Angular nos avisará em tempo de compilação e em tempo de execução — que é exatamente o que queremos para dados críticos.

O Decorador @Input() Legado

Se você analisar códigos-fonte ou tutoriais mais antigos do Angular, frequentemente encontrará entradas escritas desta forma:

import { Component, Input } from '@angular/core';
import { Product } from '../product';

export class ProductCard {
  @Input({ required: true }) product!: Product;
}

Essa é a abordagem baseada em decoradores que era o padrão antes do Angular v17.

As principais diferenças em comparação com a input()API moderna:

  • @Input()é um decorador — input()é uma função que retorna um sinal
  • Com @Input()`__init__`, o valor é uma propriedade de classe simples; com `__init__` input(), é um sinal que você lê com `__init__`.product()
  • @Input({ required: true })requer uma asserção não nula ( !) — input.required()lida com isso de forma limpa, sem sintaxe extra.
  • @Input()não se integra ao sistema de reatividade de sinais do Angular — input()o que faz com que funcione naturalmente com computed()eeffect()

Ambas as sintaxes funcionam no Angular atualmente, mas a sintaxe baseada em sinais @Input()está obsoleta e a equipe do Angular indica a intenção de removê-la na versão 22. A sintaxe baseada em sinais input()é a abordagem recomendada atualmente para todo o código novo, e é essa que usaremos ao longo deste curso.

Renderizando os dados de entrada no modelo

Agora vamos substituir o conteúdo de espaço reservado no modelo do cartão por dados reais.

Arquivo:product-card.html

<mat-card class="product-card">
  <mat-card-header>
    <mat-card-title>{{ product().name }}</mat-card-title>
    <mat-card-subtitle>Product</mat-card-subtitle>
  </mat-card-header>

  <mat-card-content>
    <p>{{ product().description }}</p>
    <p class="price">{{ product().price | currency }}</p>
  </mat-card-content>

  <mat-card-actions>
    <button matButton>Add to Cart</button>
  </mat-card-actions>
</mat-card>

Observe que chamamos `in` product()no modelo. Isso ocorre porque ` input()in` retorna um sinal de entrada, e os sinais são lidos com parênteses — o mesmo padrão que temos usado desde a lição 5.

Transferência de dados do pai para o filho

Agora, conectamos o modelo pai à entrada filha.

Arquivo:products-grid.html

<div class="products-grid">
  @for (product of products(); track product.id) {
    <app-product-card [product]="product"></app-product-card>
  } @empty {
    <div class="empty-state">
      <h3>No products available</h3>
      <p>Check back later for new arrivals!</p>
    </div>
  }
</div>

O elo que une tudo:

[product]="product"

O lado esquerdo representa o nome do campo de entrada filho. O lado direito representa a variável de loop do elemento pai. Agora, cada ProductCardinstância recebe um objeto de produto da lista.

Entradas opcionais com valores padrão

Os campos obrigatórios são perfeitos para dados indispensáveis, como [exemplo de dado] product. Mas, às vezes, queremos um comportamento configurável que possa recorrer a um valor padrão.

Vamos adicionar um campo de entrada opcional para controlar o texto do rótulo do botão.

Arquivo:product-card.ts

import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { Product } from '../product';

@Component({
  selector: 'app-product-card',
  imports: [MatCardModule, MatButtonModule],
  templateUrl: './product-card.html',
  styleUrl: './product-card.scss',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProductCard {
  readonly product = input.required<Product>();
  readonly addButtonLabel = input('Add to Cart');
}

Aqui, input('Add to Cart')significa:

  • Este campo é opcional.
  • O valor padrão é'Add to Cart'
  • O pai pode sobrescrevê-lo quando necessário.

Agora atualize o modelo para usá-lo:

Arquivo:product-card.html

<mat-card class="product-card">
  <mat-card-header>
    <mat-card-title>{{ product().name }}</mat-card-title>
    <mat-card-subtitle>Product</mat-card-subtitle>
  </mat-card-header>

  <mat-card-content>
    <p>{{ product().description }}</p>
    <p class="price">{{ product().price | currency }}</p>
  </mat-card-content>

  <mat-card-actions>
    <button matButton>{{ addButtonLabel() }}</button>
  </mat-card-actions>
</mat-card>

E se alguma vez você quiser substituir o texto do botão pelo texto do elemento pai:

<app-product-card [product]="product" [addButtonLabel]="'View Details'"></app-product-card>

Isso torna o componente mais reutilizável sem complexidade adicional.

Armadilhas comuns

Dois erros comuns a evitar:

  1. Esquecer os parênteses no modelo — escrever product.nameem vez de product().name. Como as entradas são sinais, você deve chamá-las com ()para ler o valor.
  2. Marcar dados importantes como opcionais quando deveriam ser obrigatórios — se o componente não funcionar sem um dado, use ` input.required<T>(). Se for uma configuração com um valor padrão adequado, use ` input(defaultValue).

Uma boa regra:

  • Se o componente não funcionar sem ele →input.required<T>()
  • Se for configuração →input(defaultValue)

Código-fonte

O código-fonte completo do curso está disponível no GitHub: loiane/modern-angular .

Próximo passo

Na próxima lição, vamos aprofundar esse conceito adicionando uma interface de usuário condicional no cartão, usando ` @ifand` @elsepara exibir preços promocionais e selos.

Assista ao vídeo

Este post faz parte da série do Curso de Angular Moderno . Confira a página do curso para ver a lista completa de episódios.