Angular 21 de Cero a Experto / Servicios y DI
inject(): la forma moderna de pedir dependencias
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:
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, unsetTimeout) —ahí ya no hay contexto—. Inyecta en el campo y guarda la referencia.
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:
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 routingEste 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.