Fala, comunidade dev! 👋
Na segunda parte da nossa série sobre a modernização de um sistema corporativo (saindo do Angular 16 e indo direto para o 21), vimos como o roteamento ficou muito mais limpo com o fim das antigas classes Guards. Mas eu deixei um gancho no ar: essa limpeza nas rotas só foi possível graças a uma pequena função que mudou para sempre a forma como escrevemos Angular.
Hoje vamos falar da verdadeira revolução silenciosa do framework: a função inject().
1. O Pesadelo do Construtor em Sistemas Corporativos
Quem trabalha com aplicações Enterprise sabe que uma única tela muitas vezes precisa se comunicar com diversas partes do sistema.
Imagine um cenário clássico que todo desenvolvedor já enfrentou: uma tela complexa de Cadastro de Usuários. Para essa tela funcionar, você precisa buscar dados do usuário, validar o CEP em uma API externa, checar permissões, montar o formulário e gerenciar alertas na tela.
No Angular 16, a única forma de trazer essas dependências para dentro do componente era através do constructor. O resultado era um bloco de código gigantesco, onde a injeção de dependência engolia a lógica de negócio:
O Antes (Poluição visual no Angular 16):
@Component({ ... })
export class CadastroUsuarioComponent {
constructor(
private usuarioService: UsuarioService,
private viaCepService: ViaCepService,
private permissoesService: PermissoesService,
private fb: FormBuilder,
private router: Router
) { }
}
Metade do arquivo do componente era apenas boilerplate (código repetitivo) para injetar dependências. Mas o problema real nem era a estética. O verdadeiro inferno começava quando precisávamos usar herança.
2. O Fim do "Inferno do super()"
Em arquiteturas limpas, é extremamente comum criarmos classes base para reaproveitar lógica. Um exemplo perfeito é um BaseFormComponent que concentra a lógica de exibição de toasts de erro, spinners de loading e validações genéricas.
A dor da herança antes do
inject():
Se o seuBaseFormComponentprecisasse injetar oMessageServicee oLoadingService, todos os componentes filhos que estendessem essa classe precisavam injetar esses serviços em seus próprios construtores, apenas para passá-los para cima através da chamada dosuper().
// O componente filho precisava resolver dependências que não eram dele!
constructor(
private usuarioService: UsuarioService,
private fb: FormBuilder,
// O componente filho nem usa esses dois, mas precisa injetar para o pai!
private messageService: MessageService,
private loadingService: LoadingService
) {
super(messageService, loadingService);
}
Se um dia você precisasse adicionar mais um serviço genérico na classe base, você quebrava a aplicação inteira. Era necessário abrir e refatorar o construtor de dezenas (ou centenas) de telas filhas.
3. A Chegada da Injeção Baseada em Propriedades
Com a maturidade da função inject(), a injeção de dependências deixou de ser uma exclusividade do construtor e passou a ser declarada diretamente na inicialização das propriedades da classe.
O Depois (Limpo e escalável no Angular 21):
@Component({ ... })
export class CadastroUsuarioComponent extends BaseFormComponent {
// A injeção agora vai direto na propriedade
private usuarioService = inject(UsuarioService);
private viaCepService = inject(ViaCepService);
private fb = inject(FormBuilder);
// O constructor sumiu!
// O BaseFormComponent se vira sozinho para buscar suas próprias dependências.
}
Nessa nova abordagem, a classe base (BaseFormComponent) usa o inject() internamente para pegar seus próprios serviços de alerta e loading. O componente filho não precisa mais saber (nem repassar) o que o pai está consumindo. O acoplamento despenca, e a refatoração vira uma tarefa segura e simples.
4. Fora das Classes: Interceptors Funcionais
Além de salvar a nossa sanidade na hora de estender classes, o inject() abriu as portas para o Angular ser muito mais funcional.
Assim como as rotas, os velhos HttpInterceptors (que interceptam requisições de API e também eram classes verbosas) viraram funções puras. Em qualquer sistema corporativo, injetar o Bearer Token de autenticação em todas as chamadas HTTP é regra. Olha como esse código ficou absurdamente simples e direto agora:
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const authService = inject(AuthService); // Injeção fora de uma classe!
const token = authService.getToken();
const requestModificada = req.clone({
setHeaders: { Authorization: `Bearer ${token}` }
});
return next(requestModificada);
};
Conclusão
Adotar o inject() em todo o projeto reduz drasticamente a quantidade de linhas de código, melhora a legibilidade e blinda o seu sistema contra refatorações dolorosas em classes base. É uma mudança de paradigma que separa os projetos legados engessados dos códigos modernos e fluidos.
E por falar em reduzir código, no nosso próximo artigo da série (Parte 4), vamos sair do TypeScript e olhar para o HTML. Vamos dar o adeus definitivo ao *ngIf e ao *ngFor, e entender como o novo Control Flow e a mágica do @defer otimizam a renderização de telas pesadas.
Como está a adoção do inject() nos projetos de vocês? Ainda preferem o bom e velho construtor ou já abraçaram as propriedades dinâmicas? Comentem aí! 👇
Top comments (0)