CodeForge

Angular 21 de Cero a Experto / Servicios y DI

inject(): la forma moderna de pedir dependencias

Teoría20 min20 XP

Ya pediste un servicio con inject(). Hoy entiendes esa función a fondo: qué hace, por qué reemplazó a la vieja inyección por constructor, y qué desbloquea que antes era imposible. inject() parece un detalle de sintaxis, pero es uno de los cambios que más limpió el código de Angular moderno —y es la puerta a funciones reutilizables como guards e interceptores funcionales que verás más adelante—.

Una función que dice "dame esto"

inject() es una función que recibe una clase (o token) y devuelve la instancia que Angular tiene registrada para ella:

lista.component.ts
import { Component, inject } from '@angular/core';
import { BolsilloService } from './bolsillo.service';
import { Router } from '@angular/router';

@Component({ selector: 'app-lista', template: `...` })
export class ListaComponent {
private readonly bolsillo = inject(BolsilloService);
private readonly router = inject(Router);   // servicios del propio Angular, igual

irADetalle(id: number) {
  this.router.navigate(['/gasto', id]);
}
}

Se lee como lenguaje natural: inject(BolsilloService) = "dame el servicio de bolsillo". Angular busca en su árbol de inyectores, encuentra la instancia registrada y te la entrega.

Antes (constructor) vs Ahora (inject())

Dónde se puede llamar inject(): el contexto de inyección

inject() solo funciona dentro del "contexto de inyección": el momento en que Angular está construyendo algo y tiene el árbol de inyectores disponible. En la práctica:

  • ✅ En inicializadores de propiedades de una clase (x = inject(Y)).
  • ✅ Dentro del constructor.
  • ✅ En funciones de fábrica de providers, guards funcionales, interceptores, resolvers.
  • ❌ Dentro de un método que corre después (un onClick, un setTimeout) —ahí ya no hay contexto—. Inyecta en el campo y guarda la referencia.
contexto.ts
export class Ej {
private bolsillo = inject(BolsilloService);   // ✅ inicializador de campo

guardar() {
  // ❌ inject(BolsilloService) aquí falla: fuera del contexto de inyección
  this.bolsillo.agregar('Café', 4500);        // ✅ usa la referencia guardada
}
}

La regla práctica: inyecta arriba (en los campos), usa abajo (en los métodos). Si necesitas una dependencia en un método, ya la tienes como propiedad.

inject() fuera de las clases: el gran desbloqueo

Como inject() es una función, se puede usar en OTRAS funciones que corren en contexto de inyección. Eso permite factorías de estado reutilizables:

factoria.ts
import { inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

// una función reutilizable que inyecta lo que necesita
export function crearCargadorDeGastos() {
const http = inject(HttpClient);
return () => http.get<Gasto[]>('/api/gastos');
}
// la usarás en guards, resolvers e interceptores en el módulo de routing

Este patrón —funciones que inyectan— es la base de todo lo funcional en Angular moderno. Cuando llegues a los guards e interceptores, verás cómo inject() los volvió simples funciones en lugar de clases enteras.

¿Por qué `inject()` no funciona dentro de un método `onClick` que corre tras un clic?

Mini-reto

Reescribe mentalmente este componente de la sintaxis vieja a la nueva: un DetalleComponent que en su constructor recibía private bolsillo: BolsilloService y private router: Router. Conviértelo a inject() como inicializadores de campo. Luego identifica: si tuviera un método borrar() que necesita el router, ¿dónde inyectas y dónde usas?

Qué sigue

Dominas inject() y la mecánica de la DI. Ahora la unimos con lo mejor del módulo anterior: un servicio que expone SEÑALES se convierte en un store reactivo compartido por toda la app. La próxima lección construye ese patrón —el corazón del manejo de estado en Angular moderno— y lo pruebas en vivo.