---
title: "NG0950: lendo um input obrigatório antes do Angular preencher"
description: "Por que o NG0950 do Angular dispara quando um signal input obrigatório é lido antes do binding, quando os inputs ficam disponíveis de fato (no ngOnInit, e não no construtor), e os três consertos: ler no ngOnInit / computed / effect, dar um valor padrão e tirar o required, ou fazer o binding no pai."
deck: "O `NG0950: Required input is accessed before a value is set` aparece quando você lê um signal [`input.required()`](https://angular.dev/api/core/input) cedo demais. A [doc](https://angular.dev/errors/NG0950) é exata: os inputs só estão garantidos a partir do [`ngOnInit`](https://angular.dev/guide/components/lifecycle), nunca no construtor. Então leia o input obrigatório no `ngOnInit`, num `computed` ou num `effect`, ou conserte o componente pai que esqueceu o binding."
author: "André Ramos"
url: "https://andreramos.dev/pt/angular/angular-ng0950-required-input-not-set/"
lang: "pt-BR"
type: "article"
---

# NG0950: lendo um input obrigatório antes do Angular preencher

> O `NG0950: Required input is accessed before a value is set` aparece quando você lê um signal [`input.required()`](https://angular.dev/api/core/input) cedo demais. A [doc](https://angular.dev/errors/NG0950) é exata: os inputs só estão garantidos a partir do [`ngOnInit`](https://angular.dev/guide/components/lifecycle), nunca no construtor. Então leia o input obrigatório no `ngOnInit`, num `computed` ou num `effect`, ou conserte o componente pai que esqueceu o binding.

## O erro, em uma linha

A mensagem completa é `NG0950: Required input is accessed before a value is set`, e a descrição oficial é uma frase só: um input obrigatório foi acessado mas nenhum valor foi vinculado. O signal input obrigatório, declarado com `input.required<T>()`, não tem valor inicial por desenho. Quando você lê um deles antes do binding do pai ser aplicado, não há nada pra retornar, e o Angular lança o erro em vez de te entregar `undefined`.

Os signal inputs chegaram no Angular 17.1 (em developer preview na época), e o `input.required()` é a variante sem fallback. O ganho é justamente esse: o tipo é `T`, nunca `T | undefined`, e o framework garante que o valor existe depois que o componente está montado. O NG0950 é o framework mantendo essa promessa honesta. Você leu o valor num momento em que a garantia ainda não vale, e ele te avisa em vez de deixar um `undefined` silencioso vazar pelos seus tipos.

## Quando o input está disponível de verdade

A doc declara o timing sem rodeio: os inputs estão garantidos no hook `ngOnInit` e dali pra frente. O construtor roda antes de o Angular aplicar os bindings, e os inicializadores de campo rodam como parte da construção, então os dois executam enquanto o input obrigatório ainda está vazio. Ler ali é ler antes de o valor existir.

Essa é a parte que pega quem vem da era dos decorators. Com o `@Input()`, um campo ficava `undefined` até ser setado, e ler cedo devolvia `undefined` calado. O signal input obrigatório não tem esse estado intermediário, então a mesma leitura precoce que antes passava em silêncio agora lança o erro. O conserto nunca é enfraquecer o input, e sim lê-lo num ponto em que o binding já rodou.

| Onde você lê | O binding já rodou? | Seguro ler um input obrigatório? |
|---|---|---|
| Inicializador de campo | Não, roda durante a construção | Não, lança NG0950 |
| construtor | Não, roda antes dos bindings | Não, lança NG0950 |
| computed(() => ...) | Sim, a derivação roda na primeira leitura | Sim |
| effect(() => ...) | Sim, roda durante o change detection | Sim |
| ngOnInit e hooks seguintes | Sim, garantido disponível | Sim |
| Expressão no template | Sim, avaliada no change detection | Sim |

## Duas causas que parecem idênticas

O NG0950 tem exatamente duas raízes, e a mensagem é a mesma pras duas, e é por isso que confunde. A primeira é timing: o binding está lá no pai, mas o seu código lê o input antes de ele ser aplicado. A doc nomeia o caso mais comum direto, que isso costuma acontecer quando o input é lido como parte da construção da classe. O conserto é mudar o lugar da leitura.

A segunda é binding ausente: o template do pai esqueceu o atributo, então nenhum valor foi vinculado e não há nada pra esperar. Aqui, mudar o lugar da leitura não resolve nada, porque o input segue vazio a vida inteira do componente. Antes de reorganizar o código de ciclo de vida, confirme que o pai de fato passa o input. As duas causas pedem consertos opostos, então dizer qual delas você tem é o primeiro passo real.

*O mesmo input lido cedo demais vs lido no lugar certo*

```ts
@Component({ selector: 'order-summary', /* ... */ })
export class OrderSummary {
  // Input obrigatório: o tipo é Order, nunca Order | undefined
  readonly order = input.required<Order>();

  // Lança NG0950: o inicializador de campo roda na construção,
  // antes de o Angular aplicar o binding [order] do pai
  readonly totalNoInit = this.order().total;

  // Seguro: o computed roda na primeira leitura, com o binding já aplicado
  readonly total = computed(() => this.order().total);

  constructor() {
    // Lança NG0950: o construtor roda antes dos bindings
    console.log(this.order().id);

    // Seguro: o effect roda no change detection, depois do binding
    effect(() => console.log('pedido mudou', this.order().id));
  }

  ngOnInit() {
    // Seguro: os inputs estão garantidos aqui e dali pra frente
    this.carregarHistorico(this.order().id);
  }
}
```

## O conserto que a doc dá, e o que ela não dá

Pra um bug real de timing, os consertos documentados são dois. Leia o input num contexto reativo: o template, um `computed` ou um `effect`, que rodam todos depois do binding. Ou leia no `ngOnInit` ou mais pra frente. Um `computed` em cima de um input obrigatório é a saída que eu mais uso, porque expressa a derivação uma vez e continua correta conforme o input muda, sem nenhum hook de ciclo de vida pra lembrar.

Tem um terceiro conserto que a página do erro não cita, porque é uma decisão de desenho, não de timing. Se "ainda sem valor" é um estado real em que o seu componente pode estar, o input não devia ser obrigatório. Dê um valor padrão com `input<T>(fallback)` e tire o `.required`. Aí um pai sem binding passa a ser válido, a leitura precoce devolve o padrão, e o NG0950 não tem como disparar. Optar por isso é um julgamento: deixe o input obrigatório quando um valor ausente é um erro de programação que você quer pegar no build, e deixe opcional com padrão quando a ausência é um estado que o componente foi feito pra renderizar.

## Consertando o binding ausente no pai

Quando a causa é a segunda, o trabalho está no template do pai, não no filho. O Angular já cobra os inputs obrigatórios em tempo de build quando o componente é usado num template, então um binding simplesmente ausente costuma aparecer primeiro como erro de compilação. O NG0950 em runtime tende a significar que o binding existe mas resolve pra algo inútil, ou que o elemento é criado por um caminho que o checador de template não cobre, como uma chamada dinâmica de `createComponent`.

Num componente criado dinamicamente, o binding não vem de um template, então nada te obriga a passar. Você mesmo tem que setar o input na referência do componente criado, e setar antes de qualquer leitura. A regra é a mesma: o valor precisa estar no lugar antes de o `ngOnInit` ou a primeira leitura de `computed`/`effect` rodar.

*Fazendo o binding do input obrigatório num componente criado na mão*

```ts
// Caminho via template: o compilador cobra [order]; sem ele, o
// build quebra antes de o NG0950 ter chance de rodar
// <order-summary [order]="selecionado()" />

// Caminho dinâmico: sem template, você seta o input na mão
const ref = this.container.createComponent(OrderSummary);
ref.setInput('order', this.selecionado());
// setInput antes de o change detection ler o valor; senão a
// primeira leitura ainda cai num input obrigatório vazio e lança
```

## Artefato reaproveitável: Onde é seguro ler um input obrigatório

- Inicializador de campo ou construtor: nunca leia um input obrigatório aqui; os dois rodam antes do binding e lançam NG0950.
- Precisa do valor uma vez, no init: leia no ngOnInit ou num hook posterior, onde os inputs estão garantidos.
- Precisa de um valor derivado dele: use computed(() => input()); a derivação roda depois do binding e acompanha as mudanças.
- Precisa de um efeito colateral sobre o valor: use effect(() => ...); roda no change detection, depois do binding.
- A ausência é um estado real pra renderizar: tire o .required e use input<T>(padrão) em vez de mexer no timing do ciclo de vida.
- O erro continua com a leitura movida: é binding ausente; conserte o template do pai ou use setInput no componente dinâmico.

## Perguntas frequentes

### O que o NG0950 quer dizer no Angular?

Quer dizer que um signal input obrigatório, declarado com input.required(), foi lido antes de o Angular vincular um valor a ele. Inputs obrigatórios não têm valor inicial, então ler um deles antes do binding do pai não tem nada pra retornar, e o Angular lança o erro em vez de devolver undefined.

### Por que ler um input obrigatório no construtor lança NG0950?

O construtor e os inicializadores de campo rodam como parte da construção do componente, antes de o Angular aplicar os bindings do pai. A doc garante os inputs só no ngOnInit e dali pra frente, então uma leitura no construtor acontece com o input obrigatório ainda vazio.

### Onde é seguro ler um input obrigatório?

Dentro do template, de um computed ou de um effect, que rodam todos depois do binding, ou dentro do ngOnInit ou de um hook posterior. Um computed em cima do input obrigatório é uma escolha comum porque deriva uma vez e segue correto conforme o input muda.

### É só dar um valor padrão pro input?

Só se 'ainda sem valor' for um estado real que o seu componente deva renderizar. Aí use input<T>(padrão) e tire o required. Se um valor ausente é um erro de programação que você quer pegar no build, mantenha obrigatório e mova a leitura pra um ponto seguro.

## Fontes consultadas

- https://angular.dev/errors/NG0950
- https://angular.dev/api/core/input
- https://angular.dev/guide/components/inputs
- https://angular.dev/guide/signals
- https://angular.dev/guide/components/lifecycle
- https://angular.dev/api/core/computed
- https://angular.dev/api/core/effect
- https://github.com/angular/angular/releases/tag/17.1.0

## Leia também

- [Como eu adotaria Signal Forms agora que são estáveis no Angular 22](https://andreramos.dev/pt/angular/signal-forms-not-my-default-yet/)
- [OnPush é o default no Angular 22: o que quebra e o que fazer](https://andreramos.dev/pt/angular/onpush-default-angular-22/)
- [NG0203: por que o inject() falha fora do contexto de injeção](https://andreramos.dev/pt/angular/angular-ng0203-inject-outside-injection-context/)
