CodeForge

Angular 21 de Cero a Experto / Servicios y DI

Tokens de inyección: inyectar lo que no es una clase

Teoría20 min20 XP

Hasta ahora inyectabas clases: inject(BolsilloStore). Pero a veces necesitas inyectar algo que NO es una clase: la URL base de tu API, una bandera de configuración, un valor del entorno, una función. No puedes hacer inject("https://api..."). Para eso están los TOKENS de inyección: llaves con nombre que registran cualquier valor en el sistema de DI. Hoy los dominas y cierras tu comprensión completa de la inyección de dependencias.

Una llave con nombre para cualquier cosa

Crear y usar un InjectionToken

config.tokens.ts
import { InjectionToken } from '@angular/core';

export interface AppConfig {
apiUrl: string;
moneda: string;
maxGastos: number;
}

// 1. crea el token (la "etiqueta única"), tipado
export const APP_CONFIG = new InjectionToken<AppConfig>('config.app');
app.config.ts
import { ApplicationConfig } from '@angular/core';
import { APP_CONFIG } from './config.tokens';

// 2. registra el VALOR del token en el arranque de la app
export const appConfig: ApplicationConfig = {
providers: [
  { provide: APP_CONFIG, useValue: {
    apiUrl: 'https://api.mibolsillo.com',
    moneda: 'COP',
    maxGastos: 1000,
  }},
],
};
api.service.ts
import { Injectable, inject } from '@angular/core';
import { APP_CONFIG } from './config.tokens';

@Injectable({ providedIn: 'root' })
export class ApiService {
// 3. inyecta el valor con el token como llave
private config = inject(APP_CONFIG);

cargarGastos() {
  return fetch(`${this.config.apiUrl}/gastos`);   // usa la config inyectada
}
}

El patrón es siempre el mismo: crear el token (tipado), proveer su valor, inyectarlo. El tipo <AppConfig> hace que inject(APP_CONFIG) devuelva un AppConfig con autocompletado y verificación completa.

¿Por qué no simplemente importar una constante?

Podrías escribir export const API_URL = "https://..." y import donde la necesites. ¿Por qué molestarse con un token?

Tokens con valor de fábrica

Un token también puede calcular su valor con una función que, a su vez, inyecta otras cosas:

factory-token.ts
export const ES_MOVIL = new InjectionToken<boolean>('es-movil', {
providedIn: 'root',
factory: () => window.matchMedia('(max-width: 767px)').matches,
});
// se inyecta igual:  private esMovil = inject(ES_MOVIL);

El factory corre en contexto de inyección (por eso podría usar inject() dentro), y el providedIn: 'root' lo hace tree-shakable. Angular usa este patrón internamente para muchas de sus configuraciones.

¿Cuándo necesitas un `InjectionToken` en lugar de inyectar una clase?

Mini-reto

Diseña la configuración de "Mi Bolsillo" con tokens: 1) crea un InjectionToken<AppConfig> con apiUrl, moneda y alertaSaldoBajo (un número); 2) escribe el provider que le da valor en el arranque; 3) inyéctalo en un BolsilloStore y úsalo para decidir si el saldo está "bajo". Luego explica cómo lo sustituirías en un test para probar la alerta sin depender de la config real.

Qué sigue

Cierras la teoría de servicios y DI: sabes crear servicios, inyectarlos con inject(), controlar su alcance con la jerarquía, exponerlos como stores de señales e inyectar valores con tokens. Es hora del reto del módulo: construir el BolsilloStore completo —el store reactivo que será el cerebro de tu app durante todo el curso— y probarlo en vivo.