Les scopes
Je vous ai déjà parlé des scopes. Je ne vous en avais
pas dit grand chose étant donné le peu d'élements que j'avais alors
évoqués. On va revenir maintenant sur ce point important. Quand vous
créez une application AngularJS il se crée un scope racine. Lorsque vous
créez un contrôleur vous générez un scope enfant du scope racine. Ce
scope enfant hérite du scope racine à la mode Javascript, c'est à dire
par prototype, donc sans classe. Pour celui (ou celle) qui est habitué à
l'héritage classique c'est un peu dépaysant. Un prototype n'est pas une
classe mais un objet bien vivant. Donc en Javascript un objet hérite
tout simplement d'un autre objet.
Lorsque dans du code Javascript on tombe sur une propriété d'un
objet, cette propriété est évidemment recherchée dans cet objet. Si elle
n'est pas trouvée on remonte la chaîne des prorotypes jusqu'à la
trouver. Si on ne la trouve pas on génère une erreur. Je vous raconte
tout ça parce que les scopes justement répondent à ce fonctionnement
avec une chaîne de prototypes. Je vais prendre un exemple simple pour
vous montrer les conséquences de cela (JSFiddle) :
<style> .ng-scope { border-style: dotted solid; border-width: thin; border-color: red; margin: 1em; padding: 1em; } label { color: red; } </style> <div ng-app> <label for="root">Scope racine</label> <input type="texte" id="root" placeholder="Entrez du texte" ng-model="message"> <div ng-controller="Controleur"> <label for="ctrl">Scope enfant</label> <input type="texte" id="ctrl" placeholder="Entrez du texte" ng-model="message"> </div> <script> function Controleur($scope) {} </script> <div>
J'ai ajouté du style pour visualiser les scopes avec un petit liseré
rouge. Vous allez faire le premier test suivant : entrez du texte dans
la première zone de saisie. Vous constatez que la seconde se remplit
avec le même texte, pourquoi ? J'ai créé un contrôleur mais je n'ai pas
mis de code d'initialisation. Au départ dans le scope enfant la variable
message n'existe pas. Hors il y a un lien au niveau du contrôle. La variable message
n'est pas trouvée au niveau du scope enfant, elle est donc recherchée
en remontant la chaîne des prototypes, donc au niveau du scope racine.
Ici elle existe parce qu'on l'a crée en faisant notre saisie. Tant que
vous tapez du texte dans le premier contrôle le second en est l'image
fidèle. Maintenant tapez un peu de texte dans le second contrôle et
revenez en entrer dans le premier. Maintenant vous vous rendez compte
que les deux contrôles sont devenus indépendants. Le fait de saisir du
texte dans le second contrôle a créé la variable message au niveau du scope enfant.
Si vous modifiez le code en initialisant la variable message dans le contrôleur, alors les deux contrôles sont immédiatement indépendants. Il y a une variable message dans le scope racine et une variable du même nom dans le scope enfant :
function Controleur($scope) { $scope.message = "mon message"; }
Nous allons poursuivre notre expérimentation des scopes en modifiant légèrement le code précédent (JSFiddle) :
<style> .ng-scope { border-style: dotted solid; border-width: thin; border-color: red; margin: 1em; padding: 1em; } label { color: red; } </style> <div ng-app> <label for="root">Scope racine</label> <input type="texte" id="root" placeholder="Entrez du texte" ng-model="objet.message"> <div ng-controller="controleur"> <label for="ctrl">Scope enfant</label> <input type="texte" id="ctrl" placeholder="Entrez du texte" ng-model="objet.message"> </div> <script> function controleur($scope) {} </script> <div>
Si vous ne voyez pas la différence je vous le dis. Au lieu de définir une simple variable message j'utilise une propriété d'un objet objet.message. Qu'est-ce que cela change ? Si vous faites les tests comme précédemment vous ne verrez aucune différence dans la première partie : toute saisie dans le premier contrôle entraine le remplissage automatique du second.
C'est la suite qui devient intéressante : si vous entrez du texte dans le second contrôle le premier le suit fidèlement ! Il n'y a aucun moyen de découpler les deux contrôles, sauf si vous commencez votre saisie au niveau du second ! Comment cela se fait-il ? Etant donné qu'il n'y a aucune initialisation dans le code l'objet est créé à la première saisie. Si vous la faite au niveau du scope parent c'est lui qui contient l'objet. Quand vous faites ensuite une saisie au niveau du scope enfant comme l'objet n'est pas trouvé à ce niveau on remonte le chercher dans le scope racine. Par contre si vous commencez la saisie dans le scope enfant l'objet est créé à ce niveau. Lorsque vous faites ensuite une saisie au niveau du scope racine un nouvel objet est créé la aussi et vos deux contrôles sont bien indépendants !
Nous avons vu qu'un scope est généré pour chaque contrôleur. Il y a des directives qui aussi génèrent des scopes, comme ngRepeat. Voici un exemple visualisé (JSFiddle) :
Nous avons vu qu'un scope est généré pour chaque contrôleur. Il y a des directives qui aussi génèrent des scopes, comme ngRepeat. Voici un exemple visualisé (JSFiddle) :
<style> .ng-scope { border-style: dotted solid; border-width: thin; border-color: red; margin: 1em; padding: 1em; } label { color: red; } </style> <div ng-app ng-controller="controleur"> <form ng-submit="ajouter()"> Nom : <input type="text" name="nom" ng-model="nom"> <button type="submit">Ajouter</button> </form> <br> <ul> <li ng-repeat="nom in noms">{{nom}}</li> </ul> <script> function controleur($scope) { $scope.noms = new Array(); $scope.ajouter = function() { $scope.noms.push($scope.nom); $scope.nom = ''; }; } </script> </div>
- {{nom}}