Si les scopes d'AngularJS peuvent être créés manuellement, ils vont surtout être créés automatiquement par le framework lors de l'utilisation de certaines directives ou la déclaration d'un nouveau contrôleur. Ils sont reliés sous la forme d'un arbre hiérarchique avec héritage par prototype. Cette organisation en arbre peut être gênante lorsque l'on veut effectuer une communication directe entre des scopes qui ne sont pas reliés par une relation père/fils.
Comment faire passer des informations d'un scope à un autre quand il n'existe pas de relation directe entre eux?
Nous allons voir comment utiliser l'API des scopes fournie par AngularJS pour envoyer et intercepter des évènements personnalisés.
Le scope : KEZAKO!
Dans le cas d'un scope directement relié à un contrôleur (avec l'utilisation de la directive ngController), ce dernier permet d'assurer une persistance des données par son unicité et son partage entre le contrôleur et la portion de vue associée.
Les scopes sont comme je vous l'ai dit plus haut reliés de manière hiérarchique. AngularJS ayant pour vocation d'étendre et de renforcer le langage HTML, la structure d'organisation de ces scopes sous forme d'arbre ressemble très sensiblement à la structure HTML des vues de notre application.
Au sommet de l'arbre, il y a ce que l'on appelle le rootScope. AngularJS met à notre disposition un service $rootScope qui nous permet d'accéder d'injecter dans n'importe quel contrôleur de notre application le scope racine de notre arbre. Ce service $rootScope est un objet qui regroupe directement ou indirectement sous la forme d'un arbre tous les objets scopes initialisés dans notre application.
Une application va posséder plusieurs scopes. Ces derniers sont créés par l'utilisation de certaines directives (ngController, ngRepeat, etc...). A chaque création d'un scope, AngularJS utilise la méthode $new, ce qui a pour effet de créer un scope et de l'ajouter à la liste des enfants du scope père. La création de ce scope met en place la technique de l'héritage par prototype connu en Javascript. Il sera donc possible d'appeler une méthode du parent depuis le scope enfant.
Passons maintenant aux choses sérieuses!
Les évènements
- Une étiquette qui devra être unique dans notre application pour éviter toute interférence,
- Une liste d'arguments (cette liste peut être null ou compter un nombre indéfini d'argument).
- $routeChangeStart
- $routeChangeSuccess
- $routeChangeError
- $routeUpdate
- $locationChangeStart
- $locationChangeSuccess
Je vais maintenant vous présenter les méthodes disponibles dans l'API que nous fournit l'objet scope d'AngularJS.
Emettre un évènement
Comme je vous l'ai dit précédemment, AngularJS nous donne la possibilité d'envoyer des messages à travers les différents scopes de notre application.
Nous avons deux méthodes qui nous permettent d'émettre un évènement: $emit (cf spec angularjs.org) et $broadcast (cf spec angularjs.org).
Méthode $emit
Cette méthode $emit disponible sur les objets de type scope, permet d'envoyer un message à toute la chaîne parente du scope émetteur. La propagation de cet évènement se fait donc de façon ascendante uniquement entre les scopes qui sont directement reliés entre eux.
La propagation de cet évènement peut être stoppée dès lors qu'il est intercepté en utilisant une des méthodes de l'API Event : stopPropagation() (nous verrons plus tard dans cet article comment écouter un évènement et accéder à l'objet Event associé).
Le schéma ci-dessous montre la propagation de l'évènement via la méthode $emit.
Méthode $broadcast
Cette méthode $broadcast est également disponible sur les objects de type scope. Elle permet, comme la méthode précédente, d'envoyer un évènement, mais cette fois, cet évènement ne peut être stoppé et est envoyé à travers tous l'arbre descendant des scopes instanciés dans notre application en partant du scope émetteur.
Le schéma ci-dessous montre la propagation de l'évènement via la méthode $broadcast.
L'envoi de l'évènement
Comme expliqué précédemment, les évènements dans AngularJS possèdent une étiquette ainsi qu'une liste optionnelle de paramètres.
Si par exemple vous voulez lever un évènement lorsque des données sont arrivées après un appel AJAX, il est possible de passer le résultat de l'appel en paramètre. On sera alors capable dans l'interception de cet évènement de manipuler le résultat de la requête.
Intercepter un évènement
La fonction permet de récupérer l'event courant (qui permet notamment de stopper la propagation, de récupérer les scopes sources, etc.) et les arguments passés lors de l'émission de l'évènement.
Un exemple concret
Pour notre exemple, je vais créer un loader.
Le contrôleur et la vue s'occupent uniquement d'afficher la barre de chargement.
Le service Loader s'occupe du chargement et de l'envoi des évènements.
Voilà, j'espère avoir correctement expliqué le fonctionnement des évènements dans AngularJS.
N'hésitez pas à réagir de façon constructive pour faire avancer cet article et ce blog!
Bon dév'
Bonjour,
RépondreSupprimerMerci pour cet exemple.
Mais quel serait l'intérêt d'utiliser les événements de la sorte, comparé à un usage classique des controllers :
http://jsfiddle.net/KSyk6/
L'intérêt est de communiquer entre controllers ($scope), dans ton exemple, il n'y en a qu'un seul.
SupprimerCet article est vraiment très utile!
Et quels sont les évènements natif d'angular ? Par exempe j'ai un objet dans un scope et je veux exécuter une fonction à chaque changement de cet object : $on('change') de tel objet du scope ?
RépondreSupprimerPour executer une fonction à chaque changement d'un objet, il faut utiliser $watch. Exemple :
Supprimer$scope.flag = true;
$scope.$watch('flag', function(newVal){
console.log("nouvelle valeur de flag = ", newVal);
});
// autre manière :
$scope.$watch(function(){ return $scope.flag;}, function(newVal){
console.log("nouvelle valeur de flag = ", newVal);
});
En espérant que ce soit Utile ! :)
Pour executer une fonction à chaque changement d'un objet, il faut utiliser $watch. Exemple :
Supprimer$scope.flag = true;
$scope.$watch('flag', function(newVal){
console.log("nouvelle valeur de flag = ", newVal);
});
// autre manière :
$scope.$watch(function(){ return $scope.flag;}, function(newVal){
console.log("nouvelle valeur de flag = ", newVal);
});
En espérant que ce soit Utile ! :)