Skip to content
Aprende a hacer apps móviles con Ionic 2 

UPDATED – Introducción a Angular 2 (parte III) – Servicios

Siguiendo con la introducción a Angular 2 que empecé hace unos días, hoy le llega el turno a los servicios.

Servicios

Los servicios -pieza fundamental de AngularJS- siguen siendo imprescindibles en Angular, si bien en Angular se definen a través de simples clases. Todo valor, función o característica que nuestra aplicación necesita, desde constantes a la lógica de negocio, se encapsula dentro de un servicio.

Los Componentes son grandes consumidores de servicios. No recuperan datos del servidor, ni validan inputs de usuario, ni logean nada directamente en consola. Delegan todo este tipo de tareas a los Servicios.

Los servicios deberían contener/hacer algo muy específico. Por ejemplo, serían susceptibles de encapsular en un servicio:

  • Servicio de logging
  • Servicio de datos
  • Bus de mensajes
  • Cálculo de Impuestos
  • Configuración de la app

A diferencia de AngularJS, en Angular los servicios no tienen una sintaxis específica, son simples Clases

Veamos un ejemplo de servicio de logging muy simple, que proporciona varios métodos para mostrar información por consola:

//app/shared/logger.service.ts
export class Logger {
  log(msg: any)   { console.log(msg); }
  error(msg: any) { console.error(msg); }
  warn(msg: any)  { console.warn(msg); }
}

Definamos ahora otro servicio, que gestiona nuestra lista de tareas pendientes y que además hace uso del anterior:

//app/todos/shared/todo.service.ts
import {Injectable} from '@angular/core';
import { Todo } from './todo.model';
import { Logger } from '../../shared/logger.service';

@Injectable()
export class TodoService{

    todos:Todo[] = [];

    constructor(public logger: Logger){}

    addTodo(todo:Todo){
        this.todos = [...this.todos, todo];
        this.logger.log(this.todos);
    }

    getTodos(){
        this.logger.log(this.todos);
        return this.todos;
    }

}

Las dos piezas clave aquí son:

  • @Injectable: Este decorador avisa a Angular de que el servicio espera utilizar otros servicios y genera los metadatos que necesita el servicio para detectar la Inyección de Dependencias (DI) en el constructor. No es necesario ponerlo si nuestro servicio no tiene DI de otros servicios, pero es recomendable para evitar errores si en el futuro queremos añadirle alguna dependencia.

  • Dependency Injection (DI) en el Constructor: Aquí aprovechamos las bondades de TypeScript para pasar explícitamente un objeto tipo Logger en el constructor de TodoService. De este modo, Angular es capaz de inferir la Inyección de Dependencias. En el apartado Dependency Injection te explicaré más en detalle este tema.

Los Guide Lines de Angular recomiendan utilizar siempre el decorador @Injectable al definir nuestros servicios.

Finalmente hay que aclarar que para que el código anterior funcione, faltará indicarle al Inyector de Angular quien es el provider del servicio Logger para saber como instanciarlo. De nuevo, entraré en más detalle en el apartado de Dependency Injection, pero de momento, voy a mostrarte 2 opciones para registrar providers:

En el NgModule

Puedes pasar un array con los providers en los metadatos del módulo que contiene el servicio (normalmente el principal).

//src/app/app.module.ts

//...some imports ...
import { Logger } from '../shared/logger.service';


@NgModule({
    //..some stuff...
  providers: [ Logger ]
})

export class AppModule { }

En los componentes

También puedes usar el metadato providers del componente para indicar los providers que necesita el componente o cualquiera de sus subcomponentes.

@Component({
  //...some metadata params...
  providers:   [TodoService]
})
export class TodoListComponent {
  //TodoList component stuff
}

Haciendo un breve resumen, acabo de mostrarte como crear servicios en Angular 2 (como ves es mucho más simple que en su predecesor), y como inyectarlos en otros servicios y en componentes.

No obstante, el tema de la inyección es suficientemente potente como para merecer su propio artículo.

Recuerda que puedes ver el código completo en el repositorio de GitHub.

¡Saludos!

Published inAngular 2ES6JavascriptTypeScript

2 Comments

  1. Saludos Enrique. Estaba implementando un servicio tal como enseñas aqui pero recibo el siguiente error:

    EXCEPTION: Uncaught (in promise): Error: No provider for ApiTest!

    Este es mi servicio:

    //apitest.service.ts
    import { Injectable } from ‘@angular/core’;

    @Injectable()
    export class ApiTest {
    constructor(){}

    log(msg:string){
    console.log(msg);
    }
    }

    Está en el módulo que tiene el componente en el que quiero usar el servicio:

    import { NgModule } from ‘@angular/core’;
    import { RouterModule } from ‘@angular/router’;
    import { AuthModule } from ‘../auth.module’;
    import {ApiTest} from ‘./../shared/apiTest/apitest.service’;
    import { Auth } from ‘../auth.service’;
    import { AuthGuard } from ‘../auth-guard.service’;

    import { MODULE_COMPONENTS, MODULE_ROUTES } from ‘./dashboard.routes’;

    @NgModule({
    imports: [
    RouterModule.forChild(MODULE_ROUTES),
    AuthModule
    ],
    declarations: [ MODULE_COMPONENTS ],
    providers: [ApiTest, Auth, AuthGuard ]
    })

    export class DashboardModule{

    }

    En mi componente:

    import {Component, OnInit,trigger,state,style,transition,animate,keyframes, group, Inject} from ‘@angular/core’;
    import initDemo = require(‘../../../assets/js/charts.js’);
    import initNotify = require(‘../../../assets/js/notify.js’);
    import { Http, Response} from “@angular/http”;
    import { AuthHttp } from ‘angular2-jwt’;
    import {ApiTest} from ‘./../../shared/ApiTest/apitest.service’;
    declare var $:any;

    @Component({
    moduleId: module.id,
    selector: ‘calendario’,
    templateUrl: ‘calendario.component.html’
    })

    export class CalendarioComponent{
    thing: any;
    constructor(private http: Http, public authHttp: AuthHttp, private apitest:ApiTest) {}
    this.apitest.log(“Hola desde servicio”);
    }
    }

    Ahora, si en el decorador @Component coloco providers: [ApiTest] funciona… pero tu has enseñado que hay dos formas de declarar el servicio y esta ultima es para cuando quiero usar un servicio solo en un componente… ¿Estoy haciendo algo mal? Desde ya muchas gracias.

  2. Yaro Yaro

    Hola Enrique!

    En primer lugar, te felicito por tus posts! La verdad es que se aprende un montón!

    Tengo una duda, cuando dices que los componentes no validan los inputs sino que se encargan de ello los servicios. Entiendo que tenga su lógica que ya se podría re-aprovechar el servicio para validar otros inputs en otro componente, ¿cierto?. En casi que sea así, ¿como se haría de forma mas concreta? para que sea dinámico y sea valido para otros campos o formularios.

    Saludos 😉

Deja un comentario