Servicios en Angular

Intermedio
Angular
Angular
Actualizado: 03/11/2024

¡Desbloquea el curso completo!

IA
Ejercicios
Certificado
Entrar

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.

¿Te está gustando esta lección?

Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.

Progreso guardado
Asistente IA
Ejercicios
Iniciar sesión gratis

Más de 25.000 desarrolladores ya confían en CertiDevs

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 } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-data',
  template: `
    <button (click)="addData()">Agregar Dato</button>
    <ul>
      <li *ngFor="let item of dataService.getData()">{{ item }}</li>
    </ul>
  `
})
export class DataComponent {
  constructor(private dataService: 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 en Angular 17+

En Angular 17+, la inyección de dependencias en componentes ha sido simplificada. Ahora puedes usar la sintaxis de inyección en el constructor de forma más concisa con inject():

import { Component, inject } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-data',
  standalone: true,
  imports: [NgFor],
  template: `
    <button (click)="addData()">Agregar Dato</button>
    <ul>
      <li *ngFor="let item of dataService.getData()">{{ item }}</li>
    </ul>
  `
})
export class DataComponent {
  dataService = inject(DataService);

  addData(): void {
    this.dataService.addData('Nuevo dato');
  }
}

Nota que este componente está definido como standalone, una característica introducida en versiones recientes de Angular que permite crear componentes sin necesidad de declararlos en un módulo.

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

providedIn: 'any': Una nueva opción que crea una instancia separada del servicio para cada módulo lazy-loaded que lo inyecte.

providedIn: SomeModule: Esta opción indica que el servicio se proporciona en el nivel del módulo especificado (en este ejemplo, SomeModule). Angular crea una instancia única del servicio para cada módulo que lo requiera. Al utilizar esta opción, se debe importar y agregar el servicio a la lista de providers del módulo correspondiente.

Ejemplo de un servicio proporcionado a nivel de módulo

Supongamos que hay un servicio llamado AuthService que maneja la autenticación de usuarios en una aplicación.

Se busca que este servicio esté disponible solo para un módulo específico llamado AuthModule, y no para toda la aplicación.

Aquí está el ejemplo:

// auth.service.ts
import { Injectable } from '@angular/core';

@Injectable({
    providedIn: AuthModule // Se especifica la provisión dentro del módulo AuthModule
})
export class AuthService {
}
// auth.module.ts
import { NgModule } from '@angular/core';
import { AuthService } from './auth.service'; 

@NgModule({
    providers: [AuthService], // Se agrega el servicio al arreglo de proveedores del módulo
    // Otros metadatos y configuraciones del módulo
})
export class AuthModule { }

Para lograr que el servicio esté disponible para ser inyectado en otros componentes, se debe proporcionar un proveedor para el servicio. Esto se hace en el metadato providers del módulo raíz o de un módulo específico, como en este ejemplo.

Proporcionar el servicio en un componente

Se puede proporcionar un servicio a nivel de componente agregándolo a la lista de providers en el decorador @Component.

Esto creará una nueva instancia del servicio para cada instancia del componente.

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

@Component({
selector: 'app-users',
templateUrl: './users.component.html',
providers: [UserService]
})
export class UsersComponent {

}

Para componentes standalone:

@Component({
  standalone: true,
  providers: [UserService]
})
export class UsersComponent {
  constructor(private userService: 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 } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-ejemplo',
  template: `
    <div *ngIf="datos">
      {{ datos }}
    </div>
  `
})
export class EjemploComponent implements OnInit {
  
  datos: any;

  constructor(private dataService: DataService) {}

  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 } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { DataService } from './data.service';

@Component({
  selector: 'app-ejemplo',
  standalone: true,
  imports: [AsyncPipe],
  template: `
    <div *ngIf="datos$ | async as datos">
      {{ datos }}
    </div>
  `
})
export class EjemploComponent {
  datos$ = this.dataService.obtenerDatos();

  constructor(private dataService: DataService) {}
}

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.

Aprendizajes de esta lección

  1. Aprender a crear servicios en Angular.
  2. Comprender el concepto de inyección de dependencias.
  3. Entender el ámbito y el ciclo de vida de un servicio.
  4. Conocer la configuración de proveedores de servicios.
  5. Familiarizarse con Observables en servicios.

Completa Angular y certifícate

Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.

Asistente IA

Resuelve dudas al instante

Ejercicios

Practica con proyectos reales

Certificados

Valida tus conocimientos

Más de 25.000 desarrolladores ya se han certificado con CertiDevs

⭐⭐⭐⭐⭐
4.9/5 valoración