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
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.