Inyección con inject()

Intermedio
Angular
Angular
Actualizado: 04/05/2026

Inyección con inject()

flowchart LR
    Old["Constructor injection<br/>constructor private svc: UserService"] --> OldNotes["Acoplado a clase<br/>requiere clase TS<br/>menos flexible"]
    New["inject(UserService)<br/>en field initializer"] --> Plus1["Funcional<br/>guards y resolvers"]
    New --> Plus2["Sin constructor<br/>menos boilerplate"]
    New --> Plus3["Permite herencia limpia<br/>sin super(...args)"]
    New --> Opts["inject("token, opts")"]
    Opts --> O1["{ optional: true }<br/>devuelve null"]
    Opts --> O2["{ self: true }<br/>solo injector local"]
    Opts --> O3["{ skipSelf: true }<br/>salta injector actual"]
    Opts --> O4["{ host: true }<br/>limita al host"]
    style New fill:#e8f5e9,stroke:#2e7d32
    style Old fill:#fff3e0,stroke:#ef6c00
    style Opts fill:#e3f2fd,stroke:#1565c0

Angular introdujo la función inject() como una alternativa moderna y mas flexible a la inyección de dependencias por constructor. En Angular 21, inject() es el enfoque preferido para inyectar dependencias. Esta función permite obtener instancias de servicios de manera funcional dentro de componentes, servicios y otros elementos del framework.

La función inject() se importa desde @angular/core y se puede utilizar durante la fase de construcción del contexto de inyección, lo que incluye constructores, inicializadores de propiedades, y funciones que se ejecutan durante la creación del componente.

Sintaxis básica

Para utilizar la función inject(), simplemente la llamamos pasando el token del servicio que deseamos inyectar:

import { Component, inject } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app-profile',
  template: `
    <h2>{{ user.name }}</h2>
    <p>{{ user.email }}</p>
  `
})
export class ProfileComponent {
  private userService = inject(UserService);
  
  user = this.userService.getCurrentUser();
}

Inyección en inicializadores de propiedades

Una de las ventajas principales de inject() es que permite la inyección directamente en inicializadores de propiedades, haciendo el código más conciso y legible:

import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { NotificationService } from './notification.service';

@Component({
  selector: 'app-data',
  template: `
    @for (item of data; track item.id) {
      <div>{{ item.name }}</div>
    }
  `
})
export class DataComponent {
  private http = inject(HttpClient);
  private notifications = inject(NotificationService);
  
  data = this.http.get<any[]>('/api/data');
  
  showMessage(message: string) {
    this.notifications.show(message);
  }
}

Inyección en funciones auxiliares

La función inject() también permite crear funciones auxiliares reutilizables que encapsulan lógica de inyección. Esto es especialmente útil para patrones comunes:

import { inject } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from './auth.service';

// Función auxiliar reutilizable
function createAuthGuard() {
  const auth = inject(AuthService);
  const router = inject(Router);
  
  return () => {
    if (auth.isAuthenticated()) {
      return true;
    }
    router.navigate(['/login']);
    return false;
  };
}

@Component({
  selector: 'app-dashboard',
  template: `<h1>Panel de control</h1>`
})
export class DashboardComponent {
  private authGuard = createAuthGuard();
  
  ngOnInit() {
    this.authGuard();
  }
}

Inyección con opciones

Al igual que la inyección por constructor, inject() soporta opciones avanzadas para controlar el comportamiento de la inyección:

import { Component, inject } from '@angular/core';
import { ConfigService } from './config.service';

@Component({
  selector: 'app-settings',
  template: `
    @if (config) {
      <div>{{ config.appName }}</div>
    } @else {
      <div>Configuración no disponible</div>
    }
  `
})
export class SettingsComponent {
  // Inyección opcional - no falla si el servicio no está disponible
  private config = inject(ConfigService, { optional: true });
  
  // Inyección con self - solo busca en el inyector del componente actual
  private localService = inject(LocalService, { self: true });
}

Casos de uso típicos

La función inject() es especialmente útil en los siguientes escenarios:

1 - Componentes con múltiples dependencias:

@Component({
  selector: 'app-complex',
  template: `<!-- template content -->`
})
export class ComplexComponent {
  private router = inject(Router);
  private route = inject(ActivatedRoute);
  private http = inject(HttpClient);
  private dialog = inject(MatDialog);
  private snackBar = inject(MatSnackBar);
  
  // Lógica del componente más limpia sin constructor largo
}

2 - Inicialización condicional:

@Component({
  selector: 'app-conditional',
  template: `<!-- template -->`
})
export class ConditionalComponent {
  private analytics = inject(AnalyticsService, { optional: true });
  
  trackEvent(event: string) {
    // Solo trackea si el servicio está disponible
    this.analytics?.track(event);
  }
}

La función inject() representa una evolución natural en el sistema de inyección de dependencias de Angular, ofreciendo mayor flexibilidad y un código más expresivo mientras mantiene toda la robustez del sistema de DI tradicional.

Diferencias constructor vs inject()

Angular ofrece dos enfoques principales para la inyección de dependencias: el método tradicional por constructor y la función moderna inject(). Cada uno tiene sus características específicas y casos de uso apropiados.

Enfoque tradicional por constructor

La inyección por constructor ha sido el método estándar desde las primeras versiones de Angular. Los servicios se declaran como parámetros del constructor y se asignan automáticamente a propiedades de la clase:

import { Component } from '@angular/core';
import { UserService } from './user.service';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-traditional',
  template: `<p>{{ user.name }}</p>`
})
export class TraditionalComponent {
  constructor(
    private userService: UserService,
    private http: HttpClient
  ) {}
  
  loadUser() {
    return this.userService.getCurrentUser();
  }
}

Diferencias fundamentales

Ubicación de la inyección:

  • Constructor: Las dependencias se declaran como parámetros y están disponibles después de super()
  • inject(): Las dependencias se pueden obtener en inicializadores de propiedades y métodos

Sintaxis y verbosidad:

  • Constructor: Requiere declarar parámetros y asignarlos a propiedades
  • inject(): Permite inyección directa en una sola línea
// Constructor - más verboso
class ComponentWithConstructor {
  constructor(
    private userService: UserService,
    private router: Router,
    private dialog: MatDialog
  ) {}
}

// inject() - más conciso
class ComponentWithInject {
  private userService = inject(UserService);
  private router = inject(Router);
  private dialog = inject(MatDialog);
}

Flexibilidad en el momento de inyección

La función inject() ofrece mayor flexibilidad sobre cuándo y cómo se obtienen las dependencias:

@Component({
  selector: 'app-flexible',
  template: `<!-- template -->`
})
export class FlexibleComponent {
  // Inyección condicional
  private analytics = this.isProduction() 
    ? inject(AnalyticsService) 
    : inject(MockAnalyticsService);
  
  // Inyección en propiedades computadas
  private config = inject(ConfigService);
  private apiUrl = this.config.getApiUrl();
  
  private isProduction(): boolean {
    return inject(EnvironmentService).isProd();
  }
}

Herencia y clases base

Constructor requiere llamar a super() en clases derivadas, mientras que inject() funciona de forma independiente:

// Con constructor
abstract class BaseComponent {
  constructor(
    protected logger: LoggerService,
    protected router: Router
  ) {}
}

class DerivedComponent extends BaseComponent {
  constructor(
    logger: LoggerService,
    router: Router,
    private userService: UserService // Nueva dependencia
  ) {
    super(logger, router); // Obligatorio
  }
}

// Con inject() - más simple
abstract class BaseComponentInject {
  protected logger = inject(LoggerService);
  protected router = inject(Router);
}

class DerivedComponentInject extends BaseComponentInject {
  private userService = inject(UserService); // Sin super() necesario
}

Compatibilidad con funciones

La función inject() permite crear funciones auxiliares que encapsulen lógica de inyección, algo imposible con constructores:

// Función auxiliar reutilizable
function createNotificationHandler() {
  const notifications = inject(NotificationService);
  const logger = inject(LoggerService);
  
  return (message: string, type: 'success' | 'error') => {
    notifications.show(message, type);
    logger.log(`Notification: ${message}`);
  };
}

@Component({
  selector: 'app-reusable',
  template: `<button (click)="showSuccess()">Mostrar éxito</button>`
})
export class ReusableComponent {
  private notify = createNotificationHandler();
  
  showSuccess() {
    this.notify('Operación completada', 'success');
  }
}

Cuándo usar cada enfoque

Usar constructor (enfoque clásico) cuando:

  • Trabajas en proyectos existentes que ya usan el patrón tradicional
  • El equipo mantiene la convención por coherencia con el código existente
  • Las dependencias son obligatorias y se usan en el constructor

Usar inject() (recomendado en Angular 21) cuando:

  • Inicias un proyecto nuevo o migras a patrones modernos
  • Quieres código mas conciso y legible
  • Necesitas inyección condicional u opcional
  • Trabajas con herencia compleja de componentes
  • Creas funciones auxiliares que requieren servicios

Migración gradual

Ambos enfoques pueden coexistir en el mismo componente, permitiendo migración gradual:

@Component({
  selector: 'app-hybrid',
  template: `<!-- template -->`
})
export class HybridComponent {
  // Constructor para servicios críticos
  constructor(
    private coreService: CoreService
  ) {}
  
  // inject() para servicios opcionales o auxiliares
  private analytics = inject(AnalyticsService, { optional: true });
  private logger = inject(LoggerService);
  
  performAction() {
    this.coreService.execute();
    this.analytics?.track('action_performed');
    this.logger.info('Action completed');
  }
}

La elección entre constructor e inject() depende del contexto específico del proyecto. En Angular 21, inject() es el enfoque recomendado para proyectos nuevos, mientras que la inyección por constructor sigue siendo valida y es importante conocerla para trabajar con proyectos existentes. Ambos enfoques mantienen la robustez del sistema de inyección de dependencias de Angular.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Angular es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Angular

Explora más contenido relacionado con Angular y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Comprender el uso básico de la función inject() para obtener servicios en Angular. Identificar las ventajas de inject() frente a la inyección por constructor. Aprender a usar inject() en inicializadores de propiedades y funciones auxiliares. Conocer las opciones avanzadas de inyección con inject(), como inyección opcional y self. Saber cuándo usar inject() o el constructor según el contexto y necesidades del proyecto.