Angular - Les services : Introduction

Un service Angular, qu'est-ce que c'est ? Et comment s'en servir correctement ?

Un service c'est quoi ?

En terme de concept

Un service est une classe TS composée d'attributs et de méthodes, dont l'instanciation est gérée par Angular.

Un service est, sauf cas spécifique, un singleton (design pattern) : une seule instance de l’objet est utilisée à travers toute l’application.

Une fois instancié, il est injectable dans n'importe lequel de vos composants ou dans un autre service.

Ils permettent de :

  • réutiliser du code entre différents composants
  • faciliter l'échange des données
  • centraliser les appels de service
  • séparer les responsabilités visuelles (component) et fonctionnelles/techniques (service)

Cas d’utilisation

  • Communication avec une API
  • Communication avec une base de données
  • Implémentation d’un cache d’objets
  • Gestion de la session utilisateur côté client
  • ...

Pour faire simple, la responsabilité des @Component se limite à afficher et formater la donnée, toute autre opération doit être déléguée à un service.

Créer un service

Pour générer un nouveau service, utilisez votre angular-cli en tapant simplement dans votre console :

ng g s todo

Assurez-vous d'avoir bien installé Angular CLI.

Voici un exemple de service, qui est capable d'ajouter une tâche ToDo, à une liste de tâches stockée dans le service lui-même.

import { Injectable } from '@angular/core';
import { Todo } from './todo';

@Injectable({
    providedIn: 'root'
})
export class TodoService {
    todos: Todo[];

    constructor() {}

    create(todo: Todo) {
        this.todos.push(todo);
    }
}
Service TodoService

L’annotation @Injectable est nécessaire pour la prise en compte du service par l’injection de dépendance. La propriété “provideIn” spécifie l'injecteur de dépendance utilisé. Ici ‘root’ donc le service est injectable dans tous les composants de l’application.

Vous pouvez spécifier un module à la place de root, pour utiliser l’injecteur de ce module (voir ci-dessous L'injecteur d'Angular).

Utiliser un service

Un service peut être injecté dans un autre service, ou dans un composant, en l’ajoutant en paramètre du constructeur.

Si le concept de composant Angular ne vous est pas familier, référez-vous à cet article : Angular - Les composants : Introduction.
@Component({...})
export class TodoListComponent {
    // liste des todos à afficher
    todos: Todo[];

    // todo à créer
    todo: Todo = new Todo();

    constructor(private service: TodoService) { }

    /**
    * Ajout d'un élément à la todo list
    */
    add() {
        this.service.create(this.todo);
        this.todo = new Todo();
    }
    [...]
}
Injection du service TodoService dans le composant TodoList

Observez que l’injection du service se fait en spécifiant le type TS du service dans le constructeur.

Une fois le service injecté, toutes les méthodes définies “public” dans le service deviennent accessibles. On peut donc faire appel dans la méthode add à this.service.create().

Injection de dépendances

Pour bien comprendre comment fonctionne ce passage d'une instance de service à travers l'application, revoyons ensemble comment fonctionne un injecteur de dépendances.

Le principe

L'injection de dépendances (DI) est un design pattern.

Un élément A a besoin d’un élément B. On veut fournir alors à A l’élément B. Ce n’est pas au codeur de fournir la dépendance, c’est au framework, on parle alors d’inversion de contrôle.

Inversion de contrôle (IoC)

Mettre en place l'injection de dépendances vous permet de déléguer à un objet tiers la création des objets dont les différentes parties de l'application (ex : modules, composants, services) vont avoir besoin.

L’injecteur va donc se charger d'instancier les objets demandés et de les envoyer aux objets qui en ont besoin.

L’injection de dépendance vous permet d'optimiser le nombre d'instances créées, leur gestion au sein de l'application (et le temps de code !), en déléguant toutes ces tâches au framework.

L'injecteur de dépendances d'Angular

Il existe plusieurs injecteurs de dépendance dans Angular qui interviennent à différents niveaux :

  • L’injecteur root
  • L’injecteur par module
  • L’injecteur par composant

L'injecteur root

Il s'agit de l’injecteur principal, accessible à travers tous les modules, services, components, .etc de votre application.

@Injectable({
    // le service sera injectable dans tous les modules
    providedIn: 'root' 
})
export class TodoService {
    [...]
}
L'injecteur root

L'injecteur par module

L’injecteur par module : lors de la création d’un service la propriété “providedIn” permet de spécifier le module qui sera chargé de l’injection. Le service ne sera alors pas accessible en dehors du module (sauf si import du module)

@Injectable({
    // le service sera injectable dans UserModule
    providedIn: UserModule 
})  
export class TodoService {
    [...]
}
Injecteur par module

L'injecteur par composant

L’injecteur par composant permet de spécifier quels sont les services à injecter pour un composant, en précisant la propriété “providers” au niveau du component :

@Injectable() // pas de 'providedIn' spécifier
export class TodoService {
    [...]
}
todo.service.ts

Les services à injecter ne seront instanciés qu’à l’instanciation du component. Notez que dans ce cas, les services ne sont plus des singletons. Car il peut en exister plusieurs instances au sein d'une même exécution.

@Component({
    [...]
    providers: [TodoService]   // injection 
})
export class TodoListComponent { 
    [...] 
    constructor(private service: TodoService) { }
}
todo-list.component.ts

J'espère que ça vous a été utile. N'hésitez pas à me faire vos retours en commentaire !

La prochaine étape, quand vous êtes à l'aise avec le concept de services : Les pipes.

Sources