Capítulo 09: Injeção de Dependências Hierárquica e a Função inject()
Capítulo 09: Injeção de Dependências Hierárquica e a Função inject()
Especialização em Front-End com Angular • Angular 18+ • Signals, Standalone Architecture, RxJS, NgRx e SSR
🗺️ Mapa Conceitual da Árvore Hierárquica de Injeção de Dependências
flowchart TD
Platform["PlatformInjector (Plataforma Compartilhada)"] --> Root["EnvironmentInjector (providedIn: 'root')"]
Root --> Route["Route / Lazy-Loaded EnvironmentInjector"]
Route --> CompParent["ElementInjector (Componente Pai - providers: [...])"]
CompParent --> CompChild["ElementInjector (Componente Filho)"]
subgraph INJECTION_API["Modern Dependency Injection (Angular 18+)"]
A["inject(Token, { optional: true })"]
B["InjectionToken<T>"]
C["Functional Injection (Composables / Custom Hooks)"]
end
CompChild -.-> INJECTION_API
style Platform fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style Root fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style Route fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style CompParent fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
style INJECTION_API fill:#fce4ec,stroke:#e91e63,stroke-width:2px
🏛️ 1. O Sistema de Injeção de Dependências (DI) do Angular
O sistema de Injeção de Dependências do Angular é um dos mais sofisticados da indústria de software. Ele é hierárquico, o que significa que diferentes partes da árvore de componentes podem receber instâncias isoladas ou compartilhadas de um mesmo serviço, dependendo do local onde o provedor (Provider) foi declarado.
1.1 A Revolução da Função inject()
Tradicionalmente, a injeção era feita via parâmetros no construtor da classe (constructor(private svc: MeuService)). No Angular 14 e consolidado no Angular 18+, a função inject() assumiu o protagonismo:
- Redução de Boilerplate: Não exige repetição de modificadores de acesso nem construtores vazios.
- Herança Simples: Elimina a necessidade de passar serviços pelo
super(svc1, svc2, ...)em classes derivadas. - Injeção Funcional Componível: Permite criar funções reutilizáveis (semelhantes a composables no Vue ou Custom Hooks no React) que usam DI internamente.
⚙️ 2. Prática com inject() e Modificadores de Resolução
A função inject() aceita a classe do serviço ou um InjectionToken, além de opções que controlam a busca na árvore hierárquica:
import { Component, inject, InjectionToken, Host, Optional, Self, SkipSelf } from '@angular/core';
// 1. Criação de um InjectionToken tipado para configurações da aplicação
export interface AppConfig {
apiUrl: string;
timeoutMs: number;
}
export const APP_CONFIG = new InjectionToken<AppConfig>('APP_CONFIG', {
providedIn: 'root',
factory: () => ({ apiUrl: 'https://api.empresa.com/v1', timeoutMs: 5000 })
});
// 2. Serviço com escopo Global (Singleton)
import { Injectable, signal } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class SessaoService {
usuario = signal<{ nome: string; role: string } | null>({ nome: 'Carlos', role: 'admin' });
}
// 3. Componente consumindo com inject()
@Component({
selector: 'app-dashboard-header',
standalone: true,
template: `
<header>
<h1>Painel de Controle</h1>
<p>Usuário Conectado: {{ sessao.usuario()?.nome }}</p>
<small>Conectado a: {{ config.apiUrl }}</small>
</header>
`
})
export class DashboardHeaderComponent {
// Injeção limpa e direta como propriedade de classe
readonly sessao = inject(SessaoService);
readonly config = inject(APP_CONFIG);
// Injeção opcional: não quebra se o serviço não estiver presente
private logOpcional = inject(ConsoleLogService, { optional: true });
}
class ConsoleLogService {}
🧩 3. Padrão de Injeção Funcional (Custom Composable Functions)
Com a função inject(), é possível extrair comportamentos complexos para fora das classes de componentes, criando utilitários reativos modulares:
// composables/use-window-size.ts
import { inject, signal, DestroyRef } from '@angular/core';
export function useWindowSize() {
const destroyRef = inject(DestroyRef);
const width = signal(window.innerWidth);
const height = signal(window.innerHeight);
const handler = () => {
width.set(window.innerWidth);
height.set(window.innerHeight);
};
window.addEventListener('resize', handler);
destroyRef.onDestroy(() => window.removeEventListener('resize', handler));
return { width: width.asReadonly(), height: height.asReadonly() };
}
Consumo em qualquer componente com uma única linha:
@Component({
selector: 'app-responsivo',
standalone: true,
template: `<p>Largura Atual: {{ windowSize.width() }}px</p>`
})
export class ResponsivoComponent {
readonly windowSize = useWindowSize(); // Injeta e gerencia ciclo de vida automaticamente!
}
🌳 4. Os Níveis Hierárquicos de Injeção
| Nível Hierárquico | Onde é Declarado | Ciclo de Vida da Instância |
|---|---|---|
| Root (Environment) | @Injectable({ providedIn: 'root' }) ou appConfig.providers |
Singleton Único por toda a vida da aplicação |
| Route Scope | No array providers de uma rota no routes.ts |
Criado ao navegar na rota e destruído ao sair dela |
| Component Level | @Component({ providers: [MeuService] }) |
Instância Única por Componente (compartilhada com filhos) |
| ViewProviders | @Component({ viewProviders: [...] }) |
Isolada da projeção de conteúdo (<ng-content>) |
⚠️ 5. Armadilhas Comuns e Anti-Patterns
- Chamar
inject()Fora do Injection Context:- Erro: Tentar chamar
inject(MeuService)dentro de um método de evento comoonClick(). - Causa: O Angular só disponibiliza o contexto de injeção durante a instanciação da classe (no construtor ou na inicialização de campos).
- Solução: Guarde a referência em um campo da classe:
private meuSvc = inject(MeuService);e use no método:this.meuSvc.executar().
- Erro: Tentar chamar
- Duplicação Acidental de Instâncias com
providersno Componente:- Se você colocar um serviço singleton no array
providers: [MeuService]de um componente, aquele componente criará uma nova instância independente, quebrando o estado compartilhado da aplicação.
- Se você colocar um serviço singleton no array
🛡️ 6. Boas Práticas e Arquitetura Corporativa
- Prefira
providedIn: 'root'para Quase Tudo: Ele habilita tree-shaking automático: se nenhum componente usar o serviço, ele é removido do bundle final pelo compilador. - Use
InjectionTokenpara Configurações e Ambientes: Nunca consuma objetos de ambiente (environment.ts) diretamente em serviços de baixo nível. Injete-os viaInjectionTokenpara viabilizar testes unitários e mocks independentes.
📋 7. Cheat-Sheet: Resumo Executivo
// 1. Injeção direta
const svc = inject(MeuService);
// 2. Modificadores
const opcional = inject(LoggerService, { optional: true });
const proprio = inject(LocalService, { self: true });
const pularProprio = inject(ParentService, { skipSelf: true });
// 3. InjectionToken
const API_URL = new InjectionToken<string>('API_URL');
const url = inject(API_URL);