# Aula 4.3 - Composição, inputs e outputs

## Identificação

- **Duração:** 2 horas
- **Tipo:** teoria aplicada e laboratório
- **Entrega:** biblioteca de componentes de tarefas
- **Laboratório:** [`../exemplos/aula-4.3/index.html`](../exemplos/aula-4.3/index.html)

## Introdução

### O que é composição de componentes?

Composição é construir uma tela juntando componentes menores. Em vez de concentrar
lista, cartão, filtros e ações em um único arquivo, cada parte recebe uma
responsabilidade clara.

```text
TaskList (pai)
├── TaskCard (filho)
├── TaskCard (filho)
└── TaskCard (filho)
```

No Angular, um componente usa outro ao importá-lo e inserir seu seletor no template.
A aplicação passa a formar uma árvore de componentes.

### O que significam componente pai e componente filho?

Pai é o componente cujo template cria outro componente. Filho é a instância criada
dentro desse template. Esses nomes descrevem a posição na árvore, não importância.

```html
<app-task-card />
```

Se essa linha está no template de `TaskList`, então `TaskList` é o pai e
`TaskCard` é o filho.

### Como os componentes conversam?

Usaremos um fluxo explícito:

```text
dados:   pai ──input──▶ filho
eventos: pai ◀─output── filho
```

O pai mantém o estado das tarefas. Cada filho recebe uma tarefa e informa ao pai
quando o usuário solicita uma ação. O pai decide como atualizar o estado e envia os
novos dados aos filhos.

### O que é um input?

Input é uma propriedade pública pela qual um componente recebe dados de quem o usa.
Na API Angular atual, `input()` cria um `InputSignal` somente para leitura:

```ts
readonly position = input(0);
readonly task = input.required<Task>();
```

No template do pai, colchetes conectam uma expressão ao input:

```html
<app-task-card [task]="task" [position]="$index" />
```

`input.required<Task>()` informa ao compilador que o pai precisa fornecer uma
tarefa. `input(0)` define zero como valor padrão.

### Input é um atributo HTML?

Não necessariamente. `task` é parte da API do componente Angular. O binding
`[task]="task"` passa o valor da expressão; não transforma o objeto em texto no
HTML. Um atributo estático, como `appearance="compact"`, passa texto quando existe
um input com esse nome.

### Input é automaticamente bidirecional?

Não. O input comum leva dados do pai para o filho. Ele não cria sincronização de
volta. Para comunicar uma ação, o filho emite um output. O Angular também possui
`model()` para componentes que realmente precisam de two-way binding, como alguns
controles de formulário, mas esse não é o padrão para qualquer dado.

### O que é um output?

Output é um evento personalizado da API de um componente. `output<T>()` define o
tipo do valor emitido:

```ts
readonly toggleRequested = output<number>();

requestToggle(): void {
  this.toggleRequested.emit(this.task().id);
}
```

O pai escuta o evento com parênteses e recebe o valor em `$event`:

```html
<app-task-card
  [task]="task"
  (toggleRequested)="toggleTask($event)"
/>
```

### Output é igual a evento do navegador?

Os dois usam event binding no template, mas têm origens diferentes. `(click)` vem
do elemento DOM. `(toggleRequested)` foi definido pelo componente. Outputs Angular
personalizados não fazem bubbling pelo DOM; somente quem conectou o output recebe a
emissão.

### Todo componente deve ter input e output?

Não. Um componente pode apenas apresentar conteúdo, controlar estado interno ou
obter dados por um serviço. Inputs e outputs são usados quando fazem parte do
contrato entre componentes.

### `@Input()` e `@Output()` estão errados?

Não. Você encontrará estas APIs em muitos projetos:

```ts
@Input({ required: true }) task!: Task;
@Output() toggleRequested = new EventEmitter<number>();
```

Elas continuam suportadas. Para código novo, a documentação do Angular recomenda
as funções `input()` e `output()`, que serão o padrão deste curso.

### Como isso entra na Knowledge AI?

A lista será dona das tarefas. Cada cartão receberá uma tarefa tipada e emitirá
intenções de alternar ou remover. Assim, o cartão poderá ser reutilizado sem conhecer
como a página armazena dados ou conversa com uma API.

## Objetivos

Ao final da aula, o aluno deverá conseguir:

1. explicar composição e árvore de componentes;
2. identificar relações de pai e filho;
3. importar um componente standalone;
4. declarar inputs opcionais, padrão e obrigatórios;
5. ler um `InputSignal`;
6. declarar e emitir outputs tipados;
7. receber o payload por `$event`;
8. diferenciar evento DOM e output de componente;
9. manter o estado no componente responsável;
10. atualizar objetos e arrays de modo imutável.

## Pré-requisitos

- Aulas 4.1 e 4.2 concluídas;
- componentes standalone e templates;
- bindings, eventos, signals e `@for`;
- interfaces, arrays e funções TypeScript.

## Pergunta orientadora

> Como dividir uma tela sem perder o controle sobre a origem dos dados e das ações?

## Roteiro sugerido

| Etapa | Duração |
|---|---:|
| Árvore e responsabilidades | 20 min |
| Inputs | 30 min |
| Outputs | 30 min |
| Fluxo e imutabilidade | 20 min |
| Laboratório | 15 min |
| Revisão | 5 min |

## 1. Comece pelas responsabilidades

Antes de criar arquivos, descreva o papel de cada componente:

| Componente | Responsabilidade | Não deve decidir |
|---|---|---|
| `TaskList` | manter, filtrar e atualizar tarefas | detalhes visuais do cartão |
| `TaskCard` | apresentar uma tarefa e capturar ações | armazenamento da lista |

Separar apenas para reduzir o tamanho do arquivo não basta. A fronteira deve ter um
contrato compreensível.

## 2. Modelo compartilhado

```ts
export type TaskStatus = "todo" | "doing" | "done";

export interface Task {
  readonly id: number;
  readonly title: string;
  readonly status: TaskStatus;
}
```

Pai e filho usam o mesmo contrato. `readonly` documenta que uma tarefa recebida não
deve ser alterada diretamente.

## 3. Criando o componente filho

```ts
import { Component, input, output } from "@angular/core";
import { Task } from "../task.model";

@Component({
  selector: "app-task-card",
  templateUrl: "./task-card.html",
  styleUrl: "./task-card.css",
})
export class TaskCard {
  readonly task = input.required<Task>();
  readonly position = input(0);
  readonly toggleRequested = output<number>();
  readonly removeRequested = output<number>();
}
```

As propriedades formam a API pública do cartão.

## 4. Input obrigatório

```ts
readonly task = input.required<Task>();
```

O Angular verifica em tempo de compilação se o pai forneceu `[task]`. Não existe
valor inicial e, quando o contrato é atendido, a leitura não inclui `undefined`.

## 5. Input com valor padrão

```ts
readonly position = input(0);
```

O tipo `number` é inferido. Se o pai não passar o valor, o componente usa zero.

## 6. Input opcional sem padrão

```ts
readonly description = input<string>();
```

O tipo lido será `string | undefined`. O template e a classe precisam tratar a
ausência.

## 7. Lendo um InputSignal

```html
<span>{{ position() + 1 }}</span>
<h3>{{ task().title }}</h3>
```

Inputs criados com `input()` são signals. Por isso, são lidos com `()`. Eles são
somente para leitura no filho: não chame `set` ou `update`.

## 8. Somente leitura não congela objetos

O `InputSignal` não permite substituir o valor, mas um objeto JavaScript ainda pode
ser mutado se o tipo permitir. Evite:

```ts
this.task().status = "done";
```

Além de contrariar `readonly`, isso esconde uma mudança no componente errado. Emita
uma intenção e deixe o pai criar o próximo estado.

## 9. Importando o filho no pai

```ts
@Component({
  selector: "app-task-list",
  imports: [TaskCard],
  templateUrl: "./task-list.html",
})
export class TaskList {}
```

Componentes atuais são standalone por padrão. O import torna o seletor disponível
no template daquele componente.

## 10. Passando dados do pai

```html
@for (task of tasks(); track task.id; let position = $index) {
  <app-task-card [task]="task" [position]="position" />
}
```

Cada repetição cria uma instância de `TaskCard` com valores próprios.

## 11. Valor estático e valor dinâmico

```html
<app-badge appearance="compact" />
<app-task-card [position]="position" />
```

Sem colchetes, `compact` é texto literal. Com colchetes, `position` é avaliado como
expressão TypeScript do template.

## 12. Criando outputs tipados

```ts
readonly toggleRequested = output<number>();
readonly removeRequested = output<number>();
```

O tipo informa o payload exigido por `emit` e recebido pelo pai.

## 13. Emitindo uma intenção

```ts
requestToggle(): void {
  this.toggleRequested.emit(this.task().id);
}

requestRemoval(): void {
  this.removeRequested.emit(this.task().id);
}
```

O nome descreve algo que aconteceu ou foi solicitado. O cartão não precisa conhecer
o array, o banco ou a API.

## 14. Do clique DOM ao output

```html
<button type="button" (click)="requestToggle()">
  Alternar status
</button>
```

O clique é um evento DOM recebido pelo filho. O método converte essa interação em
um evento da API do componente.

## 15. O pai escuta o output

```html
<app-task-card
  [task]="task"
  (toggleRequested)="toggleTask($event)"
  (removeRequested)="removeTask($event)"
/>
```

Aqui, `$event` é o `number` emitido, não um objeto `Event` do navegador.

## 16. O pai atualiza o estado

```ts
toggleTask(id: number): void {
  this.tasks.update((tasks) =>
    tasks.map((task) =>
      task.id === id
        ? { ...task, status: task.status === "done" ? "todo" : "done" }
        : task,
    ),
  );
}
```

O pai cria um novo array e um novo objeto para o item alterado. Depois, o binding
atualiza o input da instância correspondente.

## 17. Fluxo completo

```text
1. Pai fornece [task]
2. Filho apresenta task()
3. Pessoa clica no botão
4. Filho recebe (click)
5. Filho emite toggleRequested com o id
6. Pai recebe $event
7. Pai atualiza tasks imutavelmente
8. Angular atualiza o input do filho
```

Este ciclo deixa claro quem possui o estado e quem apenas pede uma mudança.

## 18. Estado local legítimo

Nem todo estado precisa subir ao pai. Um cartão pode manter algo estritamente
visual, como estar expandido, desde que isso não seja informação de negócio
necessária em outros componentes.

## 19. Evitando outputs genéricos

Evite nomes como `clicked`, `changed` ou `event`. Prefira o significado para quem
usa o componente:

```ts
readonly removalRequested = output<number>();
```

Nomes de outputs usam camelCase, não começam com `on` e não devem colidir com
eventos DOM como `click`.

## 20. Output não executa a regra do pai

O filho não recebe uma função arbitrária para chamar nem importa a classe do pai.
Ele emite um evento. Isso reduz acoplamento e torna o contrato visível no template.

## 21. Quando considerar `model()`

`model()` é apropriado quando o próprio componente edita seu valor principal e deve
suportar `[(value)]`, como um seletor ou controle de formulário. Em uma lista com
estado centralizado, input + output costuma expressar melhor a intenção.

## 22. Laboratório guiado

Abra o [laboratório de fluxo entre componentes](../exemplos/aula-4.3/index.html).

### Etapa 1 — Observe a árvore

Relacione a lista pai aos cartões filhos.

### Etapa 2 — Filtre no pai

O filtro muda quais filhos são compostos, sem alterar o contrato do cartão.

### Etapa 3 — Solicite uma alternância

Clique em uma ação do cartão e acompanhe o registro: clique DOM, output, tratamento
no pai e novo input.

### Etapa 4 — Solicite uma remoção

O filho envia somente o identificador. O pai decide remover a tarefa.

### Etapa 5 — Examine a fonte

Compare o componente pai, o filho e o template que conecta ambos.

## 23. Sobre o laboratório estático

O laboratório abre sem instalar Angular e reproduz a comunicação com JavaScript. A
pasta `src/app` contém a implementação Angular equivalente. A simulação não substitui
o compilador; ela torna o fluxo observável.

## 24. Erros comuns

### Alterar o objeto recebido

O filho passa a controlar estado que pertence ao pai.

### Esquecer os parênteses na leitura

`task` é o signal; `task()` é o valor atual.

### Emitir o objeto inteiro sem necessidade

Um identificador costuma criar um contrato menor e mais previsível.

### Confundir `$event`

Em `(click)`, ele é um evento DOM. Em `(toggleRequested)`, ele é o payload definido
pelo output.

### Criar output com nome de evento nativo

Isso torna ambíguo se `(click)` pertence ao componente ou ao elemento host.

### Subir todo estado para o topo

Estado visual sem compartilhamento pode permanecer no componente que o utiliza.

## 25. Boas práticas

- componha por responsabilidade;
- defina interfaces para dados compartilhados;
- marque inputs essenciais como obrigatórios;
- mantenha inputs recebidos somente para leitura;
- emita intenções tipadas;
- nomeie outputs em camelCase e sem prefixo `on`;
- mantenha o dono do estado responsável pela atualização;
- prefira atualizações imutáveis;
- mantenha o template como mapa visível da comunicação;
- não dependa de bubbling para outputs.

## 26. Exercícios

### Exercício 1 — Input obrigatório

Crie `UserAvatar` com `user = input.required<User>()`.

### Exercício 2 — Input com padrão

Adicione `size = input<"small" | "large">("small")`.

### Exercício 3 — Output tipado

Emita `userSelected` contendo apenas o identificador.

### Exercício 4 — Composição

Crie uma lista pai que repita avatares e trate a seleção.

### Exercício 5 — Responsabilidade

Explique em qual componente deve ficar o usuário selecionado e por quê.

## 27. Desafio

Crie uma biblioteca de tarefas com:

- modelo compartilhado e somente para leitura;
- lista como dona do estado;
- cartão reutilizável;
- input obrigatório para tarefa;
- input com posição padrão;
- output de alternância;
- output de remoção;
- atualização imutável;
- `@for` com `track task.id`;
- nomes que não colidam com eventos DOM.

## 28. Lista de verificação de conclusão

- [ ] Identifico pai e filho na árvore.
- [ ] Sei quando criar um componente.
- [ ] Importo um componente standalone.
- [ ] Declaro input padrão, opcional e obrigatório.
- [ ] Leio `InputSignal` com `()`.
- [ ] Não altero diretamente dados recebidos.
- [ ] Declaro e emito output tipado.
- [ ] Recebo o payload em `$event`.
- [ ] Diferencio clique DOM e output Angular.
- [ ] Mantenho a atualização no dono do estado.

## 29. Critérios de avaliação

| Critério | Pontos |
|---|---:|
| Divisão de responsabilidades | 15 |
| Inputs e contratos tipados | 25 |
| Outputs e payloads | 25 |
| Fluxo de dados | 15 |
| Atualização imutável | 10 |
| Clareza e acessibilidade | 10 |
| **Total** | **100** |

## Resumo

Nesta aula, aprendemos que:

- composição transforma a aplicação em uma árvore de componentes;
- o pai fornece dados por inputs;
- o filho comunica intenções por outputs;
- `input()` produz um signal somente para leitura;
- `input.required()` torna o contrato obrigatório;
- `output<T>()` cria um evento tipado;
- outputs personalizados não fazem bubbling no DOM;
- o dono do estado realiza atualizações imutáveis;
- `@Input()` e `@Output()` continuam suportados, mas as funções atuais são o padrão
  para código novo.

## Próxima aula

Na Aula 4.4, retiraremos regras compartilhadas dos componentes e aprenderemos
serviços e injeção de dependência.

## Fontes oficiais

- [Anatomia e composição de componentes](https://angular.dev/guide/components)
- [Inputs de componentes](https://angular.dev/guide/components/inputs)
- [Eventos personalizados com outputs](https://angular.dev/guide/components/outputs)
- [API de `output`](https://angular.dev/api/core/output)
