¿Qué son los servicios en Angular?
Un servicio es una clase diseñada para encapsular lógica o funcionalidad que debe ser compartida entre diferentes partes de la aplicación. Se utiliza principalmente para:
- Separar lógica de negocio del componente.
- Compartir datos y métodos entre componentes.
- Facilitar pruebas unitarias al desacoplar funcionalidades.
- Gestionar recursos externos como APIs o almacenamiento local.
Angular proporciona el mecanismo de inyección de dependencias (DI) para gestionar servicios, lo que garantiza que siempre se trabaje con una única instancia compartida (Patrón Singleton) a menos que se configure de otra manera.
Cómo se implementa un servicio
Crear e implementar un servicio es sencillo gracias al Cliente Angular y al patrón de inyección de dependencias. Veamos los pasos necesarios para realizar esta implementación.
1. Crear un servicio
Utiliza el comando de Angular CLI para generar un servicio:
ng generate service nombre-del-servicio
Este comando genera dos archivos:
nombre-del-servicio.service.ts
(código del servicio).nombre-del-servicio.service.spec.ts
(archivo de pruebas).
El archivo generado podría verse así:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // Proveedor en el nivel raíz
})
export class NombreDelServicio {
constructor() { }
}
2. Proveer el servicio
Angular registra automáticamente el servicio en el nivel raíz mediante el decorador @Injectable({ providedIn: 'root' })
. Esto significa que estará disponible en toda la aplicación como un Singleton.
Si necesitas limitar el alcance del servicio, puedes especificarlo manualmente en los providers
de un módulo o componente.
En Angular, además del nivel raíz, podemos registrar el servicio a nivel de módulo o de componente.
Casos de uso de los servicios
1. Lógica de negocio compartida
Por ejemplo, nuestra aplicación de adopción de mascotas puede necesitar filtrar o procesar datos sobre mascotas disponibles:
export class MascotaService {
filtrarPorEdad(mascotas: Mascota[], edad: number): Mascota[] {
return mascotas.filter(m => m.edad === edad);
}
}
2. Gestión de estados o datos globales
Los servicios permiten compartir datos entre componentes sin necesidad de usar un patrón de emisión de eventos.
export class SesionService {
private usuarioLogeado: Usuario | null = null;
setUsuario(usuario: Usuario): void {
this.usuarioLogeado = usuario;
}
getUsuario(): Usuario | null {
return this.usuarioLogeado;
}
}
Para gestión de estados simples, usar servicios es posible y puede ser una solución aplicable.
3. Consumo de APIs
Este es uno de los casos más comunes. Un servicio interactúa con un backend para enviar o recibir datos.
Vamos a entrar en detalle para este caso de aplicación.
Uso de servicios con consumos de APIs
Para consumir APIs en Angular, utilizamos el módulo HttpClientModule
y las herramientas de RxJS. A continuación, veamos cómo integrarlos.
1. Configuración inicial
Primero, asegúrate de importar el módulo HttpClientModule
en el archivo principal del módulo de tu aplicación (app.module.ts
):
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule // Importar aquí
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
2. Crear el servicio para la API
Supongamos que estamos construyendo una aplicación de adopción de mascotas y queremos consumir una API que proporciona una lista de mascotas. El servicio podría verse así:
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
export interface Mascota {
id: number;
nombre: string;
edad: number;
tipo: string;
}
@Injectable({
providedIn: 'root' // Disponible en toda la aplicación
})
export class MascotaService {
private apiUrl = 'http:/localhost:5000/mascotas';
constructor(private http: HttpClient) {}
// Obtener la lista de mascotas
getMascotas(): Observable<Mascota[]> {
return this.http.get<Mascota[]>(this.apiUrl).pipe(
map(data => data), // Transforma los datos si es necesario
catchError(this.handleError) // Manejo de errores
);
}
// Manejo de errores
private handleError(error: HttpErrorResponse) {
let mensaje = 'Ocurrió un error inesperado.';
if (error.error instanceof ErrorEvent) {
// Error del cliente
mensaje = `Error del cliente: ${error.error.message}`;
} else {
// Error del servidor
mensaje = `Error del servidor: ${error.status}, mensaje: ${error.message}`;
}
return throwError(() => mensaje);
}
}
3. Consumir el servicio en un componente
Ahora que tenemos el servicio, podemos utilizarlo en un componente para mostrar la lista de mascotas.
import { Component, OnInit } from '@angular/core';
import { MascotaService, Mascota } from './mascota.service';
@Component({
selector: 'app-lista-mascotas',
template: `
<div *ngIf="mascotas; else cargando">
<ul>
<li *ngFor="let mascota of mascotas">{{ mascota.nombre }} - {{ mascota.tipo }}</li>
</ul>
</div>
<ng-template #cargando>
<p>Cargando mascotas...</p>
</ng-template>
`
})
export class ListaMascotasComponent implements OnInit {
mascotas: Mascota[] | null = null;
constructor(private mascotaService: MascotaService) {}
ngOnInit() {
this.mascotaService.getMascotas().subscribe({
next: (data) => (this.mascotas = data),
error: (error) => console.error('Error al cargar mascotas:', error),
});
}
}
4. Ventajas de usar Observables
Al usar Observables en Angular:
- Puedes cancelar suscripciones automáticamente con operadores como
takeUntil
. - Es posible combinar flujos de datos con operadores como
combineLatest
. - Tienes más control sobre el manejo de errores y la transformación de datos.
Los servicios son una herramienta fundamental en Angular para estructurar aplicaciones bien organizadas y escalables. Al integrarlos con HttpClient
y RxJS, puedes manejar flujos de datos asíncronos de manera eficiente, aprovechar los operadores para transformar datos y garantizar una mejor experiencia del usuario.
Practicar la implementación de servicios con APIs reales es una excelente manera de dominar este concepto y sacarle el máximo provecho a Angular.
Te dejo un video donde de forma práctica verificamos los conceptos que hemos revisado anteriormente.