Injeção de Dependências Avançada no Angular 2026: Providers, Tokens e Perguntas de Entrevista
Dominar a injeção de dependências no Angular com InjectionToken, injetores hierárquicos, modificadores de resolução e multi-providers. Inclui perguntas de entrevista e padrões de produção.

O sistema de injeção de dependências do Angular oferece controle refinado sobre a instanciação de serviços através de providers, tokens e injetores hierárquicos. Dominar esses conceitos diferencia desenvolvedores Angular experientes dos juniores em entrevistas técnicas e bases de código em produção.
O Angular mantém duas árvores de injetores paralelas: a árvore ModuleInjector para serviços fornecidos no nível do módulo, e a árvore ElementInjector para dependências com escopo de componente. A resolução começa no nível do elemento e sobe até a raiz.
Estratégias de Configuração de Providers no Angular DI
O Angular oferece múltiplas estratégias de configuração de providers, cada uma adequada para diferentes casos de uso. As configurações mais comuns usam useClass, useValue, useFactory e useExisting.
import { ApplicationConfig, InjectionToken } from '@angular/core';
import { LoggerService } from './services/logger.service';
import { DebugLoggerService } from './services/debug-logger.service';
import { API_CONFIG, ApiConfig } from './config/api.config';
export const appConfig: ApplicationConfig = {
providers: [
// useClass: fornecer uma implementação diferente
{ provide: LoggerService, useClass: DebugLoggerService },
// useValue: fornecer um objeto de configuração estático
{
provide: API_CONFIG,
useValue: { baseUrl: 'https://api.example.com', timeout: 5000 }
},
// useFactory: criar dependência com lógica em tempo de execução
{
provide: 'FEATURE_FLAGS',
useFactory: () => {
const env = import.meta.env.MODE;
return { debugMode: env === 'development', analytics: env === 'production' };
}
},
// useExisting: criar um alias para outro provider
{ provide: 'Logger', useExisting: LoggerService }
]
};A estratégia useClass troca implementações sem alterar o código consumidor. A estratégia useValue fornece objetos estáticos como configuração. A estratégia useFactory lida com decisões em tempo de execução, e useExisting cria aliases para acesso polimórfico.
InjectionToken para Dependências Não-Classe com Type Safety
Enquanto @Injectable funciona para serviços baseados em classes, valores não-classe como objetos de configuração, primitivos ou funções requerem InjectionToken. Este token age como uma chave única no registro DI do Angular.
import { InjectionToken } from '@angular/core';
export interface ApiConfig {
baseUrl: string;
timeout: number;
retryAttempts: number;
}
// O parâmetro genérico garante type safety no ponto de injeção
export const API_CONFIG = new InjectionToken<ApiConfig>('api.config', {
providedIn: 'root',
factory: () => ({
baseUrl: 'https://api.sharpskill.dev',
timeout: 30000,
retryAttempts: 3
})
});
// Token para valores primitivos
export const MAX_UPLOAD_SIZE = new InjectionToken<number>('max.upload.size', {
providedIn: 'root',
factory: () => 10 * 1024 * 1024 // 10MB
});O parâmetro de tipo genérico em InjectionToken<ApiConfig> se propaga para a chamada inject(). O TypeScript sabe que o valor injetado corresponde ao tipo declarado, detectando erros de uso em tempo de compilação ao invés de tempo de execução.
A Função inject() vs Injeção por Construtor
O Angular 14 introduziu a função inject() como alternativa à injeção baseada em construtor. No Angular 20+, inject() se tornou a abordagem preferida, especialmente em componentes standalone e contextos funcionais.
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { API_CONFIG } from '../tokens/config.tokens';
@Injectable({ providedIn: 'root' })
export class UserService {
// Abordagem moderna: inject() no nível do campo
private readonly http = inject(HttpClient);
private readonly config = inject(API_CONFIG);
getUser(id: string) {
return this.http.get(`${this.config.baseUrl}/users/${id}`);
}
}
// Alternativa: injeção por construtor (ainda válida)
@Injectable({ providedIn: 'root' })
export class UserServiceLegacy {
constructor(
private readonly http: HttpClient,
@Inject(API_CONFIG) private readonly config: ApiConfig
) {}
}A função inject() elimina o boilerplate de decoradores para tokens e habilita a injeção de dependências em contextos não-classe como guards, resolvers e interceptors funcionais.
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from '../services/auth.service';
// Guard funcional usando inject()
export const authGuard: CanActivateFn = () => {
const authService = inject(AuthService);
const router = inject(Router);
if (authService.isAuthenticated()) {
return true;
}
return router.createUrlTree(['/login']);
};A função inject() só funciona dentro de um contexto de injeção: durante a construção da classe, em funções factory, ou em construções funcionais do Angular. Chamá-la fora desses contextos lança um erro em tempo de execução.
Pronto para mandar bem nas entrevistas de Angular?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Injetores Hierárquicos: ElementInjector vs EnvironmentInjector
O Angular mantém duas hierarquias de injetores paralelas que determinam o escopo do serviço e a ordem de resolução. Entender esta arquitetura é essencial para controlar o tempo de vida e a visibilidade dos serviços.
import { Injectable } from '@angular/core';
// Singleton no nível raiz: uma única instância em toda a app
@Injectable({ providedIn: 'root' })
export class GlobalDataService {
private data = new Map<string, unknown>();
set(key: string, value: unknown) { this.data.set(key, value); }
get(key: string) { return this.data.get(key); }
}
// Com escopo de componente: nova instância por componente
@Injectable()
export class ComponentDataService {
private data = new Map<string, unknown>();
set(key: string, value: unknown) { this.data.set(key, value); }
get(key: string) { return this.data.get(key); }
}import { Component } from '@angular/core';
import { ComponentDataService } from './services/component-data.service';
@Component({
selector: 'app-dashboard',
standalone: true,
// Este componente e todos os filhos obtêm a mesma instância
providers: [ComponentDataService],
template: `
<app-widget />
<app-stats />
`
})
export class DashboardComponent {}Quando um componente declara um provider, o Angular cria uma nova instância com escopo no ElementInjector desse componente. Componentes filhos herdam acesso aos providers do pai a menos que declarem os seus próprios.
O algoritmo de resolução segue este caminho:
- Verificar o ElementInjector do componente solicitante
- Subir pela árvore ElementInjector até os ancestrais
- Verificar o EnvironmentInjector (módulo ou providers standalone)
- Subir até o EnvironmentInjector raiz
- Chegar ao NullInjector e lançar um erro se
@Optional()não foi usado
Modificadores de Resolução: @Self, @SkipSelf, @Optional, @Host
Os modificadores de resolução alteram como o Angular busca na hierarquia de injetores. Esses decoradores funcionam tanto com injeção por construtor quanto com a função inject().
import { Component, Optional, SkipSelf, Self, inject } from '@angular/core';
import { PanelService } from './panel.service';
@Component({
selector: 'app-panel',
standalone: true,
providers: [PanelService],
template: `<ng-content />`
})
export class PanelComponent {
// @Self: só buscar no injetor deste componente
private readonly localService = inject(PanelService, { self: true });
// @SkipSelf: pular este componente, iniciar busca a partir do pai
private readonly parentService = inject(PanelService, {
skipSelf: true,
optional: true
});
// @Optional: retornar null ao invés de lançar erro se não encontrado
private readonly optionalService = inject(PanelService, { optional: true });
constructor() {
// localService é sempre a instância própria do componente
// parentService é a instância do pai ou null
console.log('Local:', this.localService);
console.log('Parent:', this.parentService);
}
}O modificador @Host() restringe a resolução ao injetor do elemento host e para nos limites do componente. Isso é útil quando uma diretiva precisa acessar um serviço fornecido pelo seu componente host mas não deve alcançar mais acima na árvore.
import { Directive, inject, Host, Optional } from '@angular/core';
import { HighlightConfig } from './highlight.config';
@Directive({
selector: '[appHighlight]',
standalone: true
})
export class HighlightDirective {
// Só buscar nos providers do componente host
private readonly config = inject(HighlightConfig, {
host: true,
optional: true
});
constructor() {
// config é null se o componente host não forneceu HighlightConfig
const color = this.config?.color ?? 'yellow';
// Aplicar destaque...
}
}Multi-Providers para Sistemas Extensíveis
Os multi-providers permitem registrar múltiplos valores sob um único token. O Angular retorna todos os valores registrados como um array, habilitando arquiteturas de plugins e padrões de extensibilidade.
import { InjectionToken } from '@angular/core';
export interface Validator {
validate(value: string): string | null;
}
export const VALIDATORS = new InjectionToken<Validator[]>('validators');import { ApplicationConfig } from '@angular/core';
import { VALIDATORS } from './validators.tokens';
const requiredValidator = {
validate: (value: string) => value ? null : 'O campo é obrigatório'
};
const minLengthValidator = {
validate: (value: string) => value.length >= 3 ? null : 'Mínimo de 3 caracteres'
};
const emailValidator = {
validate: (value: string) =>
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) ? null : 'Formato de email inválido'
};
export const appConfig: ApplicationConfig = {
providers: [
{ provide: VALIDATORS, useValue: requiredValidator, multi: true },
{ provide: VALIDATORS, useValue: minLengthValidator, multi: true },
{ provide: VALIDATORS, useValue: emailValidator, multi: true }
]
};import { Injectable, inject } from '@angular/core';
import { VALIDATORS, Validator } from './validators.tokens';
@Injectable({ providedIn: 'root' })
export class ValidationService {
private readonly validators = inject(VALIDATORS);
validate(value: string): string[] {
// validators é um array de todos os validadores registrados
return this.validators
.map(v => v.validate(value))
.filter((error): error is string => error !== null);
}
}A flag multi: true indica ao Angular para coletar todos os providers para este token em um array. Sem ela, providers posteriores sobrescreveriam os anteriores.
Perguntas de Entrevista sobre Injeção de Dependências no Angular
Entrevistas técnicas frequentemente avaliam o entendimento do sistema DI do Angular. Estas são perguntas que distinguem candidatos com experiência em produção.
P: O que acontece quando se fornece o mesmo serviço tanto no nível do módulo quanto do componente?
O provider no nível do componente cria uma instância separada com escopo na subárvore desse componente. Serviços injetados nessa subárvore recebem a instância do componente, não o singleton do nível do módulo. Isso habilita isolamento de estado, por exemplo quando cada aba precisa do seu próprio estado de formulário.
P: Por que usar InjectionToken ao invés de uma string literal?
Tokens string correm risco de colisões entre bibliotecas ou diferentes partes de uma aplicação. InjectionToken cria uma referência única em tempo de execução que não pode entrar em conflito. O parâmetro de tipo genérico também fornece type safety em tempo de compilação que tokens string não possuem.
P: Quando inject() lança erro vs retorna undefined?
Por padrão, inject() lança quando a dependência não é encontrada. Passar { optional: true } muda o tipo de retorno para T | null e retorna null ao invés de lançar. Isso corresponde ao comportamento do decorador @Optional().
P: Explique a diferença entre providedIn: 'root' e fornecer no array providers de um módulo.
Ambos criam singletons, mas providedIn: 'root' habilita tree-shaking. O serviço só é incluído no bundle se realmente for injetado em algum lugar. Providers no nível do módulo são sempre incluídos independentemente do uso.
P: Como o lazy loading afeta o escopo do serviço?
Módulos lazy-loaded obtêm seu próprio EnvironmentInjector filho. Serviços fornecidos em um módulo lazy têm escopo nesse módulo e seus filhos. Um serviço com providedIn: 'root' permanece como um verdadeiro singleton através de todos os módulos, lazy ou não.
Para perguntas práticas sobre serviços Angular e padrões DI, confira as perguntas de entrevista Angular sobre serviços e injeção de dependências.
Padrões Práticos: Configuração e Feature Flags
Aplicações reais combinam esses conceitos DI para gerenciamento de configuração. Este padrão usa InjectionToken, factory providers e consciência do ambiente.
import { InjectionToken, inject, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
export interface FeatureFlags {
newCheckout: boolean;
darkMode: boolean;
betaFeatures: boolean;
}
export const FEATURE_FLAGS = new InjectionToken<FeatureFlags>('feature.flags', {
providedIn: 'root',
factory: () => {
const platformId = inject(PLATFORM_ID);
if (!isPlatformBrowser(platformId)) {
// SSR: retornar valores padrão seguros
return { newCheckout: false, darkMode: false, betaFeatures: false };
}
// Navegador: verificar localStorage ou config remota
const stored = localStorage.getItem('featureFlags');
if (stored) {
return JSON.parse(stored);
}
return { newCheckout: true, darkMode: false, betaFeatures: false };
}
});import { Directive, Input, TemplateRef, ViewContainerRef, inject } from '@angular/core';
import { FEATURE_FLAGS } from './feature-flags.config';
@Directive({
selector: '[appFeatureFlag]',
standalone: true
})
export class FeatureFlagDirective {
private readonly flags = inject(FEATURE_FLAGS);
private readonly templateRef = inject(TemplateRef<unknown>);
private readonly viewContainer = inject(ViewContainerRef);
@Input() set appFeatureFlag(flag: keyof typeof this.flags) {
if (this.flags[flag]) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
}
}Esta diretiva renderiza condicionalmente conteúdo baseado em feature flags, com a configuração de flags centralizada em um único token injetável.
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Práticas DI Angular Prontas para Produção
- Usar
providedIn: 'root'para singletons no nível da aplicação que se beneficiam de tree-shaking - Preferir
inject()sobre injeção por construtor no Angular 20+ para sintaxe mais limpa e compatibilidade funcional - Escopar serviços com estado para componentes quando isolamento é necessário, não no nível do módulo
- Criar
InjectionTokenpara dependências não-classe para garantir type safety e evitar colisões - Aplicar
@Optional()quando uma dependência pode não existir, especialmente para plugins ou recursos opcionais - Testar componentes com providers sobrescritos usando
TestBed.overrideComponent()para isolamento - Usar multi-providers para padrões de extensibilidade como validadores, interceptors e handlers
Você saberia encontrar o bug em Angular?
Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Escrito por
Anthony Fillion-MailletFundador da SharpSkill
Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.
Atualizado em 13 de setembro de 2026
Tags
Compartilhar
Artigos relacionados

Angular Signals e Computed em 2026: Reatividade Granular e Perguntas de Entrevista
Dominar Signals do Angular, computed, effect e linkedSignal no Angular 20+. Padrões de reatividade fina e preparação para entrevistas técnicas.

Angular HttpClient e Interceptors em 2026: Tratamento de Requisições e Perguntas de Entrevista
Guia completo sobre HttpClient e interceptors no Angular em 2026. Arquitetura de requisições HTTP, tratamento de erros, autenticação e perguntas frequentes em entrevistas técnicas.

Perguntas de entrevista Angular 19: Signals, SSR e conceitos essenciais
As perguntas de entrevista Angular 19 mais comuns: Signals, hidratação incremental, detecção de mudanças sem Zone.js e novas APIs reativas com exemplos de código e respostas esperadas.