Servicios e inyección de dependencias en Angular: ¿qué son y cómo funcionan?


La inyección de dependecias es un término que puede parecer aterrador para quienes se están iniciando en el área de la programación, pero no es una bestia de siete cabezas. Hoy acabaremos con el miedo a esta inyección a través de este artículo y aprenderemos de ejemplos sencillos:
- ¿Qué es la inyección de dependencias?
- Cómo usar la inyección de dependencias en Angular;
- ¿Cuáles son las ventajas de este patrón de diseño?
- Qué es y cómo crear un servicio de inyectables;
- Cómo inyectar servicios en componentes y otros servicios.
Si recién está comenzando, aprenda cómo crear su primera aplicación Angular y cuáles son los primeros pasos con este marco.
¿Qué es la inyección de dependencias?
Consideremos el siguiente ejemplo: si la clase A necesita información de la clase B para poder funcionar, entonces A depende de B, es decir, B es una dependencia de A. Mirando otro contexto: ¿sabes cuando te levantas súper temprano para entrenar y dependes de tu café para poder hacer los ejercicios?
Nayanne, entonces, ¿estás diciendo que el café es mi dependencia?
¡Así es, esa es la idea!
Más técnicamente, las dependencias son servicios, objetos, funciones o incluso un valor que una clase necesita para realizar su función. En nuestro ejemplo, la clase A puede ser responsable de crear una instancia de la clase B, o de lo contrario, esta dependencia se le puede pasar, o mejor dicho, inyectar en ella. Este proceso se denomina inyección de dependencias.
De acuerdo con la documentación de Angular, la inyección de dependencias es un patrón de diseño en el que una clase solicita dependencias de fuentes externas en lugar de crearlas.

¿Cómo usar la inyección de dependencias en Angular?
Para ejemplificar cómo funciona la inyección de dependencias en Angular, aprendamos cómo crear servicios inyectables y usarlos en un componente de nuestra aplicación. Pero primero, entendamos qué son los servicios.

¿Qué son los servicios?
Los archivos angulares tienen responsabilidades bien definidas. Es una buena práctica que el componente contenga solo la lógica para definir comportamientos y poder representar los archivos en la pantalla. Así, es necesario que exista un fichero para almacenar toda la lógica de negocio y que se encargue de comunicarse con el servidor. Ese archivo es el servicio.

Los servicios en Angular nos ayudan a separar cierta información importante del componente y también cómo la vamos a obtener, la lógica de negocio, así como los datos de las solicitudes al servidor. Son útiles porque el código contenido en ellos puede ser utilizado por toda la aplicación y no se repetirá en varios lugares diferentes, ya que estas funcionalidades se pueden compartir entre componentes.
¿Cómo crear un servicio de inyectables?
Tenemos una aplicación de delivery y vamos a crear un servicio llamado FoodService, utilizando el siguiente comando de Angular CLI:
ng generate service food
o la forma abreviada:
ng g s food
Se generará un fichero con la siguiente estructura:
food.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class FoodService {
constructor() { }
}
Analizando este archivo, nos damos cuenta de que es una clase que contiene un decorator llamado @Injectable(), importado del paquete @angular/core. Este decorador indica a Angular que esta clase es inyectable y se puede usar en otras clases.
Tipos de inyectores
El decorator @injectable(), de forma predeterminada, tiene metadatos llamados providedIn. Este nombre proviene de provider, que significa proveedor. Es responsable de proporcionar una instancia de esta clase a través de la inyección de dependencias. En este caso, el valor de esta propiedad: providedIn: 'root'
indica que Angular debe proporcionar el servicio en el inyector raíz, en otras palabras, significa que este servicio es visible para toda la aplicación y puede inyectarlo en cualquier lugar de su proyecto. Esta definición de provider root
ocurre cuando queremos tener una sola instancia de un servicio en toda la aplicación y puede llamarse Singleton.
Vale la pena señalar que Singleton es un patrón de diseño que busca limitar el número de instancias de una clase específica, de modo que todos los elementos dependientes accedan a una sola instancia compartida. Esta configuración como Singleton se puede lograr tanto en el nivel raíz de la jerarquía de inserción de dependencias como mediante el uso del modificador providedIn: 'root'
.

Se recomienda proporcionar siempre su servicio en el inyector 'root'
a menos que haya un caso en el que desee que el servicio esté disponible solo si el consumidor importa un @NgModule
específico .
En este caso, puede especificar que se debe proporcionar un servicio en un @NgModule determinado. Por ejemplo, si desea que FoodService solo se pueda utilizar en los lugares donde se importó un módulo UserModule
que creó, puede especificar que el servicio se proporcione en el módulo de la siguiente manera:
import { Injectable } from '@angular/core';
import { UserModule } from './user.module';
@Injectable({
providedIn: UserModule
})
export class FoodService {
constructor() { }
}
Además, el provider también se puede establecer como any
, lo que indica que se debe crear una instancia de un servicio de manera diferente en función de si se está utilizando en módulos cargados inmediatamente (eager loading) o tardiamente (lazy loading). Si definimos any
en módulos cargados inmediatamente, todos comparten la misma instancia del servicio, tal y como lo harían con root
. Sin embargo, en los módulos de carga tardía, cada módulo recibe su propia instancia del servicio, lo que garantiza el aislamiento entre los diferentes módulos y evita conflictos.
Además, podemos definir el provider como platform
. En la jerarquía de inyección en módulos, platform
es "padre" de root
, por lo que es compartido por todas las aplicaciones de la página. Este enfoque es útil cuando tiene varias aplicaciones Angular ejecutándose en una sola página. Al proporcionar el servicio a nivel de plataforma, se asegura de que una sola instancia de ese servicio se comparta entre todas las aplicaciones Angular de la página.
Por último, existe la posibilidad de dejar el inyector como null
. En este caso, se deshabilita la funcionalidad de inserción de dependencias predeterminada para este servicio y se debe aprovisionar manualmente la instancia de servicio siempre que sea necesario.
Entonces, ahora que sabemos cómo crear servicios inyectables, ¿aprendamos a usarlos en nuestros componentes?
¿Cómo inyectar servicios en los componentes?
Para mostrar la inyección de dependencias en acción, usemos el siguiente ejemplo.
Imagina que durante un día ajetreado, no tienes tiempo para preparar una comida y decides ir a un restaurante. Hay que enfrentarse al tráfico para ir en bicicleta o en coche, conseguir el menú, esperar a que te sirvan, hacer el pedido y luego pagarlo. Pero, ¿y si hubiera una forma más práctica y fácil de hacerlo? ¿Qué pasaría si, en lugar de ir al restaurante, te llegara el pedido? Esto se puede resolver a través de un pedido de comidas a través de la aplicación, ¿verdad?
Tenga en cuenta que, al realizar un pedido a través de la aplicación, ya no es responsable de todo el proceso y lo deja en manos del servicio de entrega, ¿verdad? Pues bien, este es otro ejemplo de inyección de dependencias y es con este contexto que ejemplificaremos el uso de servicios en componentes.

En nuestro proyecto, tenemos un componente llamado delivery, que se utiliza para mostrar en la pantalla las opciones de pedido en un restaurante. Este componente necesita el servicio que creamos anteriormente, FoodService
, para elegir el tipo de comida.
Algunos días de la semana, el restaurante hace una promoción para las personas que quieran pedir, además de la comida, un postre o una bebida. En este caso,FoodService
tiene la necesidad de comunicarse con otros dos servicios, DessertService
y DrinkService
.

Ahora, ¡vayamos al código para ver cómo resolver esto!
Sin la inyección de dependencias, tendríamos que instanciar manualmente todos los servicios que necesitamos en el componente, además de tener que pasar todos los parámetros posibles que utilizan estos servicios. Así:
delivery.component.ts
export class DeliveryComponent {
//declaracion de los atributos de los services
drinkService: DrinkService;
dessertService: DessertService;
foodService: FoodService;
constructor() {
this.drinkService = new DrinkService();
this.dessertService = new DessertService();
this.foodService = new FoodService(this.dessertService, this.drinkService);
}
//métodos de la classe
}
¿Te imaginas el trabajo? Además del acoplamiento de clases, la repetición de código y la dificultad en las pruebas que esto causaría en su aplicación? Y este componente utiliza solo tres servicios. Definitivamente no queremos crear estas clases manualmente, queremos que el servicio nos proporcione eso.
Veamos el ejemplo con la inyección de dependencias:
delivery.component.ts
export class DeliveryComponent {
constructor(private foodService: FoodService) { }
//métodos de la classe
}
¡Sí, eso es todo! En Angular, la inyección de dependencias se puede hacer a través de un constructor, donde especificamos un parámetro con el tipo de la dependencia (foodService: FoodService) y, al colocar el modificador de acceso privado, hacemos que este atributo se declare automáticamente como un atributo de esta clase.
Además, a partir de la versión 14 de Angular, obtenemos la nueva función inject
, que también nos permite inyectar dependencias de una manera diferente. Con él, podemos hacer la misma inyección que hicimos en el bloque anterior, pero de la siguiente manera:
import { Component, inject } from '@angular/core';
export class DeliveryComponent {
foodService = inject(FoodService);
//métodos de la classe
}
En este caso, usamos inject
para inyectar FoodService
en la clase DeliveryComponent
, asignando el servicio a la propiedad foodService
.
La inyección de dependencias se puede realizar a través del constructor o a través dela función inject()
. La elección entre ellos depende de usted, pero es crucial recordar que al usar inject
es necesario recordar importar @angular/core
.
Por lo tanto, DeliveryComponent
está pidiendo para FoodService
ser inyectado, en lugar de crear su propia instancia de esta clase de servicio.
De acuerdo, pero ¿cómo FoodService
consume los otros dos servicios? ¡Vamos al siguiente tema!
Uso de servicios en otros servicios
Para usar servicios en otros servicios, el enfoque es similar al que adoptamos para inyectar estas clases en los componentes. A continuación, tenemos el archivo FoodService
con los otros dos servicios que se inyectan a través del constructor y algunos métodos de ejemplo que consumen datos referentes a las clases inyectadas, solo para ver en el navegador que todo está bien.
food.service.ts
import { DrinkService } from '../drink/drink.service';
import { DessertService } from '../dessert/dessert.service';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class FoodService {
constructor(
private dessertService: DessertService,
private drinkService: DrinkService
) { }
food!: string;
selectFood(food: string) {
this.food = food;
console.log(this.food);
}
selectFoodAndDessert(food: string, dessert: string) {
this.selectFood(food);
this.dessertService.selectDessert(dessert);
}
selectFoodAndDrink(food: string, drink: string) {
this.selectFood(food);
this.drinkService.selectDrink(drink);
}
}
En resumen, inyectamos los servicios DrinkService
y DessertService
en FoodService
e inyectamos solo este último en nuestro componente. En la imagen de abajo, desde los mensajes en la consola del navegador, vemos que en el componente de entrega podemos consumir información de los tres servicios. ¡Todo esto, a través de la inyección de dependencia! Qué increíble, ¿verdad?

Precauciones al usar la inyección de dependencias
- Sobrecarga de rendimiento: tratar con demasiadas dependencias en la inserción de dependencias puede sobrecargar el rendimiento, lo que requerirá más trabajo de supervisión y optimización. Por lo tanto, es necesario evaluar si los beneficios de la flexibilidad superan la posible pérdida de eficiencia en aplicaciones donde la velocidad de respuesta es relevante para la experiencia del usuario.
- Configuración compleja: En proyectos más pequeños, es importante equilibrar la simplicidad y la modularidad para evitar tener demasiada complejidad innecesaria en la configuración inicial de la inyección de dependencias.
- Dificultad en el seguimiento: Los proyectos grandes pueden dificultar el seguimiento de las dependencias del proyecto, lo que afecta al mantenimiento. Por lo tanto, es importante gestionar y organizar el proyecto para facilitar la resolución de problemas y mejorar la trazabilidad de la información.
- Experiencia en equipo: La inyección de dependencias presenta una curva de aprendizaje para los desarrolladores que no están acostumbrados a trabajar con esta técnica. Por lo tanto, es importante tener en cuenta la experiencia del equipo para evitar errores en la configuración y uso de la inyección de dependencias y hacer una implementación más efectiva.
Bonus: 10 razones para usar la inyección de dependencias
Hemos analizado las capacidades de Angular para proporcionar automáticamente una instancia del servicio que se utilizará sin preocuparse por instanciarlo manualmente. Además, te daré 10 razones más para empezar a usar la inyección de dependencias:
- aumentar la flexibilidad y la modularidad de sus aplicaciones;
- reducir el código repetitivo (código repetitivo, en varias partes del proyecto);
- hacer que su código sea más limpio y eficiente;
- ayuda con la gestión de dependencias;
- mejorar la reutilización del código;
- permitir una clara separación de responsabilidades entre los expedientes;
- reducir el acoplamiento entre clases;
- facilitar el mantenimiento del código;
- permitir cambiar el servicio sin afectar a los componentes dependientes;
- Favorecer el desarrollo de pruebas unitarias utilizando dependencias simuladas.
Conclusión
En este artículo, entendemos qué es dependencia, inyección de dependencias, servicios y aprendemos a usar estos conceptos de manera práctica en Angular. Ahora, ya sabe cómo crear servicios inyectables para que los consuman los componentes y otros servicios, organizando mejor su aplicación.
Querido Dev y querido Dev, ¡espero que hayas aprendido algo nuevo de este artículo y estés interesado en estudiar más sobre el tema! Por lo tanto, recomiendo el estudio sobre Angular.
Y si te interesa, profundiza aún más estudiando nuestras formaciones, cursos y artículos:
- Desde cero: HTML y CSS para proyectos web;
- Desarrollar aplicaciones web con JavaScript;
- Explorar o Framework Angular.
Espero verte pronto en nuevas inmersiones en Angular. ¡Nos vemos luego!
Artículo adaptado y traducido por Daysibel Cotiz

Nayanne Batista
Nayanne (Nay) es una mujer de Paraíba que hizo una transición profesional a la informática después de completar un doctorado en salud y ser profesora de educación superior. Graduada en Análisis y Desarrollo de Sistemas, ha trabajado como Frontend Dev en proyectos y ahora es Instructora en Alura. Cree completamente en el poder transformador de la educación y la tecnología en la vida de las personas. ¡Le encantan los libros y el café!