AngularJS
Portées angulaires
Recherche…
Remarques
Angular utilise une arborescence de portées pour lier la logique (depuis les contrôleurs, les directives, etc.) à la vue et constitue le principal mécanisme de détection des modifications dans AngularJS. Une référence plus détaillée pour les étendues peut être trouvée à docs.angularjs.org
La racine de l'arbre est accessible via un service injectable $ rootScope . Tous les enfants $ scope héritent des méthodes et des propriétés de leur étendue parent $, permettant aux enfants d'accéder aux méthodes sans utiliser les services angulaires.
Exemple de base de l'héritage $ scope
angular.module('app', [])
.controller('myController', ['$scope', function($scope){
$scope.person = { name: 'John Doe' };
}]);
<div ng-app="app" ng-conroller="myController">
<input ng-model="person.name" />
<div ng-repeat="number in [0,1,2,3]">
{{person.name}} {{number}}
</div>
</div>
Dans cet exemple, la directive ng-repeat crée une nouvelle portée pour chacun des enfants nouvellement créés.
Ces étendues créées sont des enfants de leur étendue parente (dans ce cas, la portée créée par myController) et, par conséquent, elles héritent de toutes ses propriétés, telles que personne.
Éviter d'hériter des valeurs primitives
En javascript, l'attribution d'une valeur non primitive (telle que Object, Array, Function et bien d' autres) permet de conserver une référence (une adresse dans la mémoire) à la valeur attribuée.
L'affectation d'une valeur primitive (String, Number, Boolean ou Symbol) à deux variables différentes et à leur modification ne changera pas les deux:
var x = 5;
var y = x;
y = 6;
console.log(y === x, x, y); //false, 5, 6
Mais avec une valeur non primitive, puisque les deux variables sont simplement les références à gardent le même objet, en changeant une variable va changer l'autre:
var x = { name : 'John Doe' };
var y = x;
y.name = 'Jhon';
console.log(x.name === y.name, x.name, y.name); //true, John, John
En mode angulaire, lorsqu'une étendue est créée, toutes les propriétés de son parent lui sont affectées. Cependant, modifier les propriétés par la suite n'affectera la portée parent que s'il s'agit d'une valeur non primitive:
angular.module('app', [])
.controller('myController', ['$scope', function($scope){
$scope.person = { name: 'John Doe' }; //non-primitive
$scope.name = 'Jhon Doe'; //primitive
}])
.controller('myController1', ['$scope', function($scope){}]);
<div ng-app="app" ng-controller="myController">
binding to input works: {{person.name}}<br/>
binding to input does not work: {{name}}<br/>
<div ng-controller="myController1">
<input ng-model="person.name" />
<input ng-model="name" />
</div>
</div>
Rappelez-vous: dans les étendues angulaires, les portées peuvent être créées de plusieurs manières (telles que les directives intégrées ou personnalisées, la fonction $scope.$new()
) et le suivi de l'arborescence est probablement impossible.
Utiliser uniquement des valeurs non primitives comme propriétés de portée vous permettra de rester en sécurité (sauf si vous avez besoin d'une propriété pour ne pas hériter ou d'autres cas où vous connaissez l'héritage de la portée).
Une fonction disponible dans toute l'application
Attention, cette approche peut être considérée comme une mauvaise conception pour les applications angulaires, car elle nécessite que les programmeurs se souviennent à la fois de l'emplacement des fonctions dans l'arborescence et de la connaissance de l'héritage de la portée. Dans de nombreux cas, il serait préférable d'injecter un service ( pratique angulaire - utilisation de l'héritage de la portée par rapport à l'injection) .
Cet exemple montre seulement comment l'héritage de la portée peut être utilisé pour nos besoins et comment vous pouvez en tirer parti, et non les meilleures pratiques de conception d'une application complète.
Dans certains cas, nous pourrions tirer parti de l'héritage de la portée et définir une fonction en tant que propriété du rootScope. De cette façon, toutes les étendues de l'application (à l'exception des étendues isolées) hériteront de cette fonction et pourront être appelées de n'importe où dans l'application.
angular.module('app', [])
.run(['$rootScope', function($rootScope){
var messages = []
$rootScope.addMessage = function(msg){
messages.push(msg);
}
}]);
<div ng-app="app">
<a ng-click="addMessage('hello world!')">it could be accsessed from here</a>
<div ng-include="inner.html"></div>
</div>
inner.html:
<div>
<button ng-click="addMessage('page')">and from here to!</button>
</div>
Créer des événements $ scope personnalisés
Comme pour les éléments HTML normaux, $ scopes peut avoir ses propres événements. Les événements $ scope peuvent être abonnés de la manière suivante:
$scope.$on('my-event', function(event, args) {
console.log(args); // { custom: 'data' }
});
Si vous devez annuler l'enregistrement d'un écouteur d'événement, la fonction $ on renverra une fonction de dissociation. Pour continuer avec l'exemple ci-dessus:
var unregisterMyEvent = $scope.$on('my-event', function(event, args) {
console.log(args); // { custom: 'data' }
unregisterMyEvent();
});
Il existe deux manières de déclencher votre propre événement $ scope personnalisé $ broadcast et $ emit . Pour notifier le (s) parent (s) d'une portée d'un événement spécifique, utilisez $ emit
$scope.$emit('my-event', { custom: 'data' });
L'exemple ci-dessus déclenchera tous les écouteurs d'événement pour my-event
sur la portée parent et continuera jusqu'à l'arborescence des étendues vers $ rootScope, sauf si un écouteur appelle stopPropagation
sur l'événement. Seuls les événements déclenchés avec $ stopPropagation
peuvent appeler stopPropagation
L'inverse de $ emit est $ broadcast , ce qui déclenchera tous les écouteurs d'événement sur toutes les étendues enfants de l'arborescence qui sont des enfants de la portée appelée $ broadcast .
$scope.$broadcast('my-event', { custom: 'data' });
Les événements déclenchés avec $ broadcast ne peuvent pas être annulés.
Utiliser les fonctions $ scope
Bien que la déclaration d'une fonction dans $ rootscope présente des avantages, nous pouvons également déclarer une fonction $ scope toute partie du code injectée par le service $ scope. Contrôleur, par exemple.
Manette
myApp.controller('myController', ['$scope', function($scope){
$scope.myFunction = function () {
alert("You are in myFunction!");
};
}]);
Vous pouvez maintenant appeler votre fonction depuis le contrôleur en utilisant:
$scope.myfunction();
Ou via HTML qui se trouve sous ce contrôleur spécifique:
<div ng-controller="myController">
<button ng-click="myFunction()"> Click me! </button>
</div>
Directif
Une directive angulaire est un autre endroit où vous pouvez utiliser votre champ d'application:
myApp.directive('triggerFunction', function() {
return {
scope: {
triggerFunction: '&'
},
link: function(scope, element) {
element.bind('mouseover', function() {
scope.triggerFunction();
});
}
};
});
Et dans votre code HTML sous le même contrôleur:
<div ng-controller="myController">
<button trigger-function="myFunction()"> Hover over me! </button>
</div>
Bien sûr, vous pouvez utiliser ngMouseover pour la même chose, mais ce qui est spécial avec les directives, c'est que vous pouvez les personnaliser comme vous le souhaitez. Et maintenant, vous savez comment utiliser vos fonctions $ scope en leur sein, soyez créatif!
Comment pouvez-vous limiter la portée d'une directive et pourquoi le feriez-vous?
Scope est utilisé comme "colle" pour communiquer entre le contrôleur parent, la directive et le modèle de directive. Chaque fois que l'application AngularJS est amorcée, un objet rootScope est créé. Chaque portée créée par des contrôleurs, des directives et des services est héritée de manière systématique de rootScope.
Oui, nous pouvons limiter la portée d'une directive. Nous pouvons le faire en créant un champ d’intervention isolé.
Il existe 3 types de champs d'application:
- Portée: Faux (la directive utilise sa portée parente)
- Portée: True (la directive a une nouvelle portée)
- Portée: {} (la directive obtient une nouvelle portée isolée)
Directives avec la nouvelle étendue isolée: Lorsque nous créons une nouvelle étendue isolée, elle ne sera pas héritée de la portée parente. Cette nouvelle étendue est appelée étendue isolée car elle est complètement détachée de sa portée parente. Pourquoi? devrions-nous utiliser une portée isolée: nous devrions utiliser une portée isolée lorsque nous souhaitons créer une directive personnalisée car elle s'assurera que notre directive est générique et placée n'importe où dans l'application. La portée parent ne va pas interférer avec la portée de la directive.
Exemple de portée isolée:
var app = angular.module("test",[]);
app.controller("Ctrl1",function($scope){
$scope.name = "Prateek";
$scope.reverseName = function(){
$scope.name = $scope.name.split('').reverse().join('');
};
});
app.directive("myDirective", function(){
return {
restrict: "EA",
scope: {},
template: "<div>Your name is : {{name}}</div>"+
"Change your name : <input type='text' ng-model='name'/>"
};
});
Il existe 3 types de préfixes que AngularJS fournit pour la portée isolée:
- "@" (Liaison de texte / liaison unidirectionnelle)
- "=" (Liaison directe du modèle / liaison bidirectionnelle)
- "&" (Liaison de comportement / liaison de méthode)
Tous ces préfixes reçoivent des données des attributs de l'élément directive comme:
<div my-directive
class="directive"
name="{{name}}"
reverse="reverseName()"
color="color" >
</div>