Los servicios en Angular son clases que encapsulan la lógica y los datos específicos de una aplicación. Se utilizan para compartir información y funcionalidades entre componentes a través de la inyección de dependencias.
flowchart TD
Root["Root injector<br/>app.config.ts<br/>providedIn: 'root'"]
Root --> RouteInj["Route-level injector<br/>provideRouter + providers en route"]
RouteInj --> CompInj1["Component injector<br/>standalone -> providers en @Component"]
RouteInj --> CompInj2["Otro component<br/>injector aislado"]
Comp["Componente solicita<br/>UserService"] --> Lookup[Inject lookup ascendente]
Lookup --> CompInj1
CompInj1 -->|no encontrado| RouteInj
RouteInj -->|no encontrado| Root
Root -->|no encontrado| Null[NullInjectorError]
Lookup -->|encontrado en compInj| Local["Instancia local<br/>scope componente"]
Lookup -->|encontrado en root| Global["Singleton global<br/>una sola instancia"]
style Root fill:#e3f2fd,stroke:#1565c0
style RouteInj fill:#fff3e0,stroke:#ef6c00
style CompInj1 fill:#e8f5e9,stroke:#2e7d32
style Null fill:#ffebee,stroke:#c62828
Creación de un servicio
Los servicios se crean una vez y se pueden inyectar en varios componentes, lo que promueve la reutilización del código y la separación de responsabilidades.
Para crear un servicio en Angular, primero se debe generar una clase TypeScript decorada con el decorador @Injectable(). Esto le permite a Angular saber que el servicio puede ser inyectado en otros componentes o servicios.
Comandos para crear un servicio
Usando Angular CLI:
ng generate service nombre-del-servicio
o la versión abreviada:
ng g s nombre-del-servicio
Para crear un servicio dentro de una carpeta específica:
ng generate service ruta/nombre-del-servicio
Estos comandos generarán automáticamente un archivo de servicio con la estructura básica y el decorador @Injectable().
A continuación se presenta un ejemplo de código que define un servicio llamado DataService:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class DataService {
private data: string[] = [];
addData(item: string): void {
this.data.push(item);
}
getData(): string[] {
return this.data;
}
}
En este ejemplo, el servicio DataService encapsula la lógica y el estado para agregar y recuperar datos.
La opción providedIn: 'root' dentro del decorador @Injectable() garantiza que Angular cree una única instancia del servicio a nivel de aplicación, lo que permite compartir los mismos datos y métodos entre múltiples componentes que lo utilicen.
En Angular, incluyendo las versiones más recientes, es técnicamente posible crear servicios sin el decorador @Injectable() en algunos casos. Sin embargo, se recomienda siempre usar @Injectable() como buena práctica, incluso si el servicio no tiene dependencias inicialmente. Esto mantiene la consistencia del código y facilita futuras modificaciones.
Inyección de dependencias
Una vez que el servicio ha sido definido, puede ser inyectado en componentes que lo requieran.
La inyección de dependencias es una práctica en la que se proporcionan instancias de clases necesarias en lugar de crearlas directamente.
Angular se encarga de crear una sola instancia del servicio y la comparte entre todos los componentes que lo soliciten.
A continuación, se muestra cómo se realiza la inyección del servicio DataService en un componente:
import { Component, inject } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-data',
template: `
<button (click)="addData()">Agregar Dato</button>
<ul>
@for (item of dataService.getData(); track item) {
<li>{{ item }}</li>
}
</ul>
`
})
export class DataComponent {
dataService = inject(DataService);
addData(): void {
this.dataService.addData('Nuevo dato');
}
}
En este ejemplo, la inyección de dependencias se logra a través del constructor del componente. Al interactuar con el servicio DataService, el componente administra los datos sin involucrarse en la implementación subyacente.
Inyección de dependencias con inject() (enfoque moderno)
En Angular 21, la forma preferida de inyectar dependencias es mediante la función inject(). Este enfoque es mas conciso y no requiere declarar parámetros en el constructor:
import { Component, inject } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-data',
template: `
<button (click)="addData()">Agregar Dato</button>
<ul>
@for (item of dataService.getData(); track item) {
<li>{{ item }}</li>
}
</ul>
`
})
export class DataComponent {
dataService = inject(DataService);
addData(): void {
this.dataService.addData('Nuevo dato');
}
}
En Angular 21, los componentes son standalone por defecto (no es necesario indicar standalone: true), y se usa la sintaxis de control de flujo @for / @if en lugar de las directivas estructurales *ngFor / *ngIf de versiones anteriores.
Ámbito y ciclo de vida de un servicio
El ámbito y el ciclo de vida de un servicio en Angular están determinados por la propiedad providedIn en el objeto de metadatos del decorador @Injectable().
Esta propiedad indica dónde se proporciona el servicio y cómo se administran las instancias del servicio.
providedIn: 'root': Esta opción indica que el servicio se proporciona a nivel de la aplicación y se crea una única instancia (singleton) para toda la aplicación. Es la opción predeterminada y es recomendable para la mayoría de los servicios que requieren un estado compartido a nivel de la aplicación.
En versiones anteriores de Angular existian opciones como providedIn: 'any' o providedIn: SomeModule, pero en Angular 21 con la arquitectura standalone, la recomendación estandar es usar providedIn: 'root' para la mayoria de servicios.
Proporcionar el servicio en un componente
Se puede proporcionar un servicio a nivel de componente agregandolo a la lista de providers en el decorador @Component. Esto creara una nueva instancia del servicio para cada instancia del componente:
import { Component, inject } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-users',
templateUrl: './users.component.html',
providers: [UserService]
})
export class UsersComponent {
private userService = inject(UserService);
}
Observables en servicios
Los Observables son una característica proveniente de la biblioteca RxJS (Reactive Extensions for JavaScript). Un Observable representa una fuente de valores, o eventos, que uno puede "observar".
Los servicios en Angular a menudo se utilizan para recuperar, procesar y enviar datos. Cuando estos procesos son asincrónicos, como la recuperación de datos de una API, los Observables son una herramienta valiosa.
A continuación, se muestra un ejemplo sencillo de cómo se puede crear un servicio en Angular que utiliza Observables para recuperar datos de una API:
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class DataService {
private http = inject(HttpClient);
private apiUrl = 'https://api.ejemplo.com/datos';
obtenerDatos(): Observable<any> {
return this.http.get(this.apiUrl);
}
}
En este ejemplo, el servicio DataService utiliza el módulo HttpClient para hacer una solicitud GET a una API.
La función obtenerDatos() devuelve un Observable que emitirá los datos cuando estén disponibles.
Suscripción a Observables
Para "escuchar" o "observar" los valores que emite un Observable, es necesario "suscribirse" a él.
La suscripción se realiza utilizando el método subscribe() del Observable.
import { Component, OnInit, inject } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-ejemplo',
template: `
@if (datos) {
<div>{{ datos }}</div>
}
`
})
export class EjemploComponent implements OnInit {
private dataService = inject(DataService);
datos: any;
ngOnInit() {
this.dataService.obtenerDatos().subscribe({
next: (data) => this.datos = data,
error: (error) => console.error('Ocurrió un error:', error)
});
}
}
En el componente EjemploComponent, se suscribe al Observable devuelto por obtenerDatos(). Cuando los datos estén disponibles, se asignarán al miembro datos del componente.
En las últimas versiones de Angular se ha introducido una nueva sintaxis para trabajar con Observables en las plantillas, eliminando la necesidad de suscribirse manualmente en muchos casos
import { Component, inject } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { DataService } from './data.service';
@Component({
selector: 'app-ejemplo',
imports: [AsyncPipe],
template: `
@if (datos$ | async; as datos) {
<div>{{ datos }}</div>
}
`
})
export class EjemploComponent {
private dataService = inject(DataService);
datos$ = this.dataService.obtenerDatos();
}
En este ejemplo, se usa el pipe async en la plantilla para manejar la suscripción al Observable, lo que simplifica el código y evita la necesidad de gestionar manualmente la suscripción y la limpieza.
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
- Aprender a crear servicios en Angular.
- Comprender el concepto de inyección de dependencias.
- Entender el ámbito y el ciclo de vida de un servicio.
- Conocer la configuración de proveedores de servicios.
- Familiarizarse con Observables en servicios.