La communication entre contrôleurs avec AngularJS grâce à l’héritage

La communication entre contrôleurs avec AngularJS et l'héritage

Le modèle d’AngularJS permet de séparer les contrôleurs et les vues, ce qui facilite beaucoup le développement.

Les contrôleurs sont également cloisonnés entre eux, mais il existe plusieurs moyens pour les faire communiquer.

L’héritage et les contrôleurs

Pour faire communiquer les contrôleurs entre eux,  il existe plusieurs solutions :

  • l’émission d’évènements
  • l’utilisation d’un singleton dans un service
  • l’héritage par $rootScope
  • l’héritage depuis un contrôleur parent

Comme je découvre encore AngularJS, j’imagine qu’il doit en exister d’autres. Je vous propose  de détailler comment faire communiquer des contrôleurs AngularJS entre eux par l’héritage de scope.

L’héritage avec $rootScope

Le scope $rootScope porte plutôt bien son nom, en (presque) français : le scope racine.

En un peu plus clair : tous les scopes de tous les contrôleurs de l’application AngularJS héritent de celui-ci.

Bien sûr, les méthodes que vous définissez dans $rootScope sont aussi héritées.

Pour définir des propriétés pour $rootScope, un bonne solution est de passer par la méthode run() de l’application, pour les déclarer au plus tôt dans son déroulement :

angular.module('myApp')
  .run(function($rootScope) {
    $rootScope.age = 5;
    $rootScope.user = {'firstName': 'Maurice', 'lastName': 'Moss' };
  });

Son utilisation est simple : les propriétés sont accessibles directement dans le scope de tous les autres contrôleurs, aussi bien en HTML :

<div>Utilisateur connecté : {{user.firstName}} {{user.lastName}}</div>

…qu’en JavaScript :

$scope.age = 7;

Attention toutefois à ce dernier exemple : age ne sera pas modifié dans $rootScope, ni dans les autres contrôleurs qui en héritent automatiquement, contrairement à cet exemple :

$scope.firstName = 'Jen';

Ici, la modification est bien répercutée dans tous les contrôleurs.

Ce qui nous amène à une règle importante à retenir : comme dans la plupart des langages, les variables de type simple (entiers, chaines, …) sont passées par valeur, alors que les objets sont passés par référence. Et cela vaut pour l’héritage de contrôleurs, depuis $rootScope ou non.

En clair, la véritable règle à retenir est : pour être sûr que les propriétés de $scope soient partagées et mises à jour dans tous les scopes qui la référencent, utilisez des objets.

Par exemple, ne faites pas :

$rootScope.userLogin = 'mmoss';
$rootScope.userMail = 'm.moss@the-it.com';

Mais plutôt :

$rootScope.user = {};
$rootScope.user.login = 'mmoss';
$rootScope.user.mail = 'm.moss@the-it.com';

Ou tout pareil, mais dans une autre forme :

$rootScope.user = {'login': 'mmoss', 'mail': 'm.moss@the-it.com'};

Dans ce cas, changer l’adresse mail par $rootScope ou dans l’un des contrôleurs qui en héritent répercutera les modifications partout, comme ils partagent le même objet.

Depuis un contrôleur parent

Une autre solution, sur le même principe, permet de cibler les héritages, et découper l’application par sous-parties, en utilisant un contrôleur parent et d’autres contrôleurs qui vont partager ses propriétés et ses méthodes.

Pour cela, il suffit d’inclure un contrôleur dans un autre contrôleur.

Une façon simple pour le faire :

<div ng-controller="ParentCtrl">
  <div ng-controller="ChildCtrl"></div>
</div>

Ici, ChildCtrl contiendra automatiquement les méthodes et les propriétés du scope de ParentCtrl.

Aussi, la règle que nous avons vue pour $rootScope à propos du partage des propriétés est valable ici : seuls les objets sont partagés et peuvent être propagées en cas de modification.

Et cet héritage fonctionne aussi pour les vues.

Avec une vue dans index.html :

<div ng-view=""></div>

… ce routage :

angular.module('myApp')
  .config(function ($routeProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/main.html',
        controller: 'MainCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });
    });

Et ce code dans la vue main.html :

<div ng-include="'views/users.html'"  ng-controller="UserCtrl"></div>

Comme la vue main.html est gérée par le contrôleur MainCtrl et qu’elle comprend la vue users.html elle-même gérée par UserCtrl, UserCtrl hérite de MainCtrl.

Enfin, si utiliser $rootScope pour initialiser les autres contrôleurs ne vous convient pas, vous pouvez simuler son usage avec un contrôleur global.

<body ng-app="myApp" ng-controller="GlobalCtrl">
  <ng-include src="'views/top-bar.html'" ng-controller="LoginCtrl" /></ng-include>
  <div ng-view></div>
</body>

Ici, comme le contrôleur GlobalCtrl est déclaré au même niveau que ng-app, c’est-à-dire au plus haut niveau du DOM accessible par AngularJS, tous les autres contrôleurs en hériteront et partageront ses objets.

Pour le moment, le seul avantage que je vois à ce contrôleur global est d’écrire le contrôleur parent dans un… véritable contrôleur.

Le mot de la fin

Bien plus qu’un simple héritage, les propriétés de type objet des contrôleurs sont partagées entre les contrôleurs parents et les contrôleurs enfants (ou entre $rootScope et les autres contrôleurs).

Pour finir, si vous n’étiez pas au courant, je vous conseille de visionner les conférences AngularJS, nommées ng-conf, qui se sont déroulées il y a quelques semaines à Salt Lake City, et qui sont disponibles le site officiel.

Cet article vous a été utile ? Partage it !

2 réflexions au sujet de « La communication entre contrôleurs avec AngularJS grâce à l’héritage »

  1. Les ressources en français sur angular sont rares sur le web. Même si l’on a l’habitude de lire l’anglais dans l’informatique, appréhender de nouveaux concepts est plus aisé dans sa langue natale. Merci, pour ce billet 🙂

    J’écrivais avant tout pour signaler ce que je pense être une coquille dans l’une des lignes de code :

    $rootScope.user = {'login': 'mmoss', 'mail': 'm.moss@the-it.com');
    , ne devrait-il pas être
    $rootScope.user = {'login': 'mmoss', 'mail': 'm.moss@the-it.com'};
    ?

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Si vous le souhaitez, renseignez le champ 'Nom' de cette façon : 'VotreNom@VotreMotClef' pour obtenir une ancre optimisée pour les moteurs de recherche.