Les composants AngularJS 1.5+ ne supportent pas les Watchers, quel est le travail?

J’ai mis à niveau mes directives personnalisées vers la nouvelle architecture de composants . J’ai lu que les composants ne prennent pas en charge les observateurs. Est-ce correct? Si oui, comment détectez-vous les changements sur un object? Pour un exemple élémentaire, j’ai un composant personnalisé myBox qui possède un jeu de composants enfant avec une liaison sur le jeu. S’il y a un jeu de changement dans le composant de jeu, comment afficher un message d’alerte dans la boîte aux lettres? Je comprends qu’il existe la méthode RXJS est-il possible de le faire purement en angle? Mon JS FIDDLE JS FIDDLE

JS

 var app = angular.module('myApp', []); app.controller('mainCtrl', function($scope) { $scope.name = "Tony Danza"; }); app.component("myBox", { bindings: {}, controller: function($element) { var myBox = this; myBox.game = 'World Of warcraft'; //IF myBox.game changes, show alert message 'NAME CHANGE' }, controllerAs: 'myBox', templateUrl: "/template", transclude: true }) app.component("game", { bindings: {game:'='}, controller: function($element) { var game = this; }, controllerAs: 'game', templateUrl: "/template2" }) 

HTML

 
Your Favourite game is: {{myBox.game}}

Change Game
Hi {{name}}

Écriture de composants sans observateurs

Cette réponse décrit cinq techniques à utiliser pour écrire des composants AngularJS 1.5 sans utiliser les observateurs.

  • Utilisez la directive ng-change
  • Utilisez le crochet de cycle de vie $onChanges
  • Utilisez le $doCheck de cycle de vie $doCheck
  • Communication intercomposant avec requirejs
  • Valeurs Push d’un service avec RxJS

Utilisez la directive ng-change

Quelles méthodes alt disponibles pour observer les changements d’état obj sans utiliser watch en préparation pour AngularJs2?

Vous pouvez utiliser la directive ng-change pour réagir aux modifications apscopes aux entrées.

  

Et pour propager l’événement à un composant parent, le gestionnaire d’événement doit être ajouté en tant qu’atsortingbut du composant enfant.

  

JS

 app.component("game", { bindings: {game:'=', gameChange: '&'}, controller: function() { var game = this; game.textChange = function (value) { game.gameChange({$value: value}); }); }, controllerAs: 'game', templateUrl: "/template2" }); 

Et dans le composant parent:

 myBox.gameChange = function(newValue) { console.log(newValue); }); 

C’est la méthode préférée pour l’avenir. La stratégie d’AngularJS consistant à utiliser $watch n’est pas évolutive car il s’agit d’une stratégie d’interrogation. Lorsque le nombre de $watch watchers atteint environ 2000, l’interface utilisateur devient lente. La stratégie dans Angular 2 consiste à rendre le framework plus réactif et à éviter de placer $watch sur $scope .


Utilisez le crochet de cycle de vie $onChanges

Avec la version 1.5.3 , AngularJS a ajouté le hook de cycle de vie $comstack service $comstack .

De la documentation:

Le contrôleur peut fournir les méthodes suivantes qui agissent comme des crochets de cycle de vie:

  • $ onChanges (changesObj) – Appelé chaque fois que des liaisons unidirectionnelles ( < ) ou interpolation ( @ ) sont mises à jour. Le changesObj est un hachage dont les clés sont les noms des propriétés liées qui ont changé, et les valeurs sont un object de la forme { currentValue: ..., previousValue: ... } . Utilisez ce hook pour déclencher des mises à jour dans un composant, telles que le clonage de la valeur liée pour empêcher toute mutation accidentelle de la valeur externe.

- Référence de l'API AngularJS Comprehensive - Crochets de cycle de vie

Le crochet $onChanges est utilisé pour réagir aux changements externes dans le composant avec < liaisons unidirectionnelles. La directive ng-change est utilisée pour propager les modifications du contrôleur ng-model dehors du composant avec des liaisons.


Utilisez le $doCheck de cycle de vie $doCheck

Avec la version 1.5.8 , AngularJS a ajouté le hook de cycle de vie $doCheck au service $comstack .

De la documentation:

Le contrôleur peut fournir les méthodes suivantes qui agissent comme des crochets de cycle de vie:

  • $doCheck() - Appelé à chaque tour du cycle de digestion. Fournit une opportunité pour détecter et agir sur les changements. Toutes les actions que vous souhaitez entreprendre en réponse aux modifications que vous détectez doivent être appelées à partir de ce hook; implémenter ceci n'a aucun effet sur l'appel de $onChanges . Par exemple, ce hook pourrait être utile si vous souhaitez effectuer une vérification d'égalité profonde ou vérifier un object Date, dont les modifications ne seraient pas détectées par le détecteur de modifications d'Angular et ne déclencheraient donc pas $onChanges . Ce hook est appelé sans arguments. Si vous détectez des modifications, vous devez enregistrer la ou les valeurs précédentes pour les comparer aux valeurs actuelles.

- Référence de l'API AngularJS Comprehensive - Crochets de cycle de vie


Communication inter-composants avec require

Les directives peuvent exiger que les contrôleurs d'autres directives permettent la communication entre eux. Cela peut être réalisé dans un composant en fournissant un mappage d'object pour la propriété require . Les clés d'object spécifient les noms de propriété sous lesquels les contrôleurs requirejs (valeurs d'object) seront liés au contrôleur du composant requirejs.

 app.component('myPane', { transclude: true, require: { tabsCtrl: '^myTabs' }, bindings: { title: '@' }, controller: function() { this.$onInit = function() { this.tabsCtrl.addPane(this); console.log(this); }; }, templateUrl: 'my-pane.html' }); 

Pour plus d'informations, voir AngularJS Developer Guide - Intercomponent Communicatation


Valeurs Push d'un service avec RxJS

Qu'en est-il dans une situation où vous avez un service qui contient l'état par exemple. Comment puis-je envoyer des modifications à ce service et que d'autres composants aléatoires de la page prennent conscience de cette modification? Nous avons eu du mal à résoudre ce problème récemment

Créez un service avec les extensions RxJS pour Angular .

    
 var app = angular.module('myApp', ['rx']); app.factory("DataService", function(rx) { var subject = new rx.Subject(); var data = "Initial"; return { set: function set(d){ data = d; subject.onNext(d); }, get: function get() { return data; }, subscribe: function (o) { return subject.subscribe(o); } }; }); 

Alors, abonnez-vous simplement aux modifications.

 app.controller('displayCtrl', function(DataService) { var $ctrl = this; $ctrl.data = DataService.get(); var subscription = DataService.subscribe(function onNext(d) { $ctrl.data = d; }); this.$onDestroy = function() { subscription.dispose(); }; }); 

Les clients peuvent s'abonner aux modifications avec DataService.subscribe et les producteurs peuvent envoyer des modifications avec DataService.set .

La DEMO sur PLNKR .

$watch object $watch est disponible dans l’object $scope , vous devez donc append $scope dans la fonction fabrique de votre contrôleur, puis placer l’observateur sur la variable.

 $scope.$watch(function(){ return myBox.game; }, function(newVal){ alert('Value changed to '+ newVal) }); 

Démo Ici

Remarque: Je sais que vous avez converti directive en component , pour supprimer la dépendance de $scope afin de vous rapprocher d’Angular2. Mais il semble que cela n’a pas été supprimé pour ce cas.

Mettre à jour

Fondamentalement angular 1.5 fait append .component méthode jus différencie deux fonctionnalités différentes. Comme le component .stands pour effectuer un comportement particulier en ajoutant un selector , où directive un comportement spécifique à DOM. Directive est juste une méthode wrapper sur .directive DDO (Objet de définition de directive). Seulement ce que vous pouvez voir, c’est qu’ils ont supprimé la fonction link/comstack en utilisant la méthode .component où vous pouviez avoir un DOM compilé angularment.

Utilisez le crochet de cycle de vie $doCheck / $doCheck du crochet de cycle de vie des composants angulars, ceux-ci seront disponibles après la version Angular 1.5.3+.

$ onChanges (changesObj) – Appelé à chaque mise à jour des liaisons. Le changesObj est un hachage dont les clés sont les noms des propriétés liées.

$ doCheck () – Appelé à chaque tour du cycle de digestion lorsque la liaison change. Fournit une opportunité pour détecter et agir sur les changements.

En utilisant la même fonction à l’intérieur du composant, vous vous assurez que votre code soit compatible avec le déplacement angular 2.

Pour toute personne intéressée par ma solution, je finis par avoir recours à RXJS Observables, que vous devrez utiliser lorsque vous arriverez à Angular 2. Voici un violon de travail pour les communications entre les composants, cela me donne plus de contrôle sur ce qu’il faut regarder.

JS FIDDLE RXJS Observables

 class BoxCtrl { constructor(msgService) { this.msgService = msgService this.msg = '' this.subscription = msgService.subscribe((obj) => { console.log('Subscribed') this.msg = obj }) } unsubscribe() { console.log('Unsubscribed') msgService.usubscribe(this.subscription) } } var app = angular .module('app', ['ngMaterial']) .controller('MainCtrl', ($scope, msgService) => { $scope.name = "Observer App Example"; $scope.msg = 'Message'; $scope.broadcast = function() { msgService.broadcast($scope.msg); } }) .component("box", { bindings: {}, controller: 'BoxCtrl', template: `Listener:  {{$ctrl.msg}} Unsubscribe A` }) .factory('msgService', ['$http', function($http) { var subject$ = new Rx.ReplaySubject(); return { subscribe: function(subscription) { return subject$.subscribe(subscription); }, usubscribe: function(subscription) { subscription.dispose(); }, broadcast: function(msg) { console.log('success'); subject$.onNext(msg); } } }]) 

Une petite mise en garde concernant l’utilisation de ng-change , comme recommandé avec la réponse acceptée, avec un composant angular de 1,5.

Si vous devez regarder un composant que ng-model et ng-change ne fonctionnent pas, vous pouvez transmettre les parameters sous la forme:

Balisage dans lequel le composant est utilisé:

   

Composant js:

 angular .module('myComponent') .component('myComponent', { bindings: { onChange: '&', fieldValue: '=' } }); 

Marquage de composant:

  

Disponible dans IE11, MutationObserver https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver . Vous devez injecter le service $ element dans le contrôleur, ce qui interrompt la séparation DOM / contrôleur, mais j’estime qu’il s’agit d’une exception fondamentale (c’est-à-dire une faille) dans angularjs. Comme hide / Show est asynchrone, nous avons besoin de callback sur show, ce que ne fournissent pas angularjs & angular-bootstrap-tab. Cela nécessite également que vous sachiez quel élément spécifique DOM vous voulez observer. J’ai utilisé le code suivant pour que le contrôleur angularjs déclenche la redissortingbution des graphiques Highcharts lors de l’exposition.

 const myObserver = new MutationObserver(function (mutations) { const isVisible = $element.is(':visible') // Requires jquery if (!_.isEqual(isVisible, $element._prevIsVisible)) { // Lodash if (isVisible) { $scope.$broadcast('onReflowChart') } $element._prevIsVisible = isVisible } }) myObserver.observe($element[0], { atsortingbutes: true, atsortingbuteFilter: ['class'] }) 

Really Nice a accepté la réponse, mais je pourrais append que vous pouvez également utiliser la puissance des événements (un peu comme dans les signaux / slots de Qt si vous voulez).

Un événement est diffusé: $rootScope.$broadcast("clickRow", rowId) par n’importe quel parent (ou même le contrôleur des enfants). Ensuite, dans votre contrôleur, vous pouvez gérer l’événement comme ceci:

 $scope.$on("clickRow", function(event, data){ // do a refresh of the view with data == rowId }); 

Vous pouvez également append des informations de connexion comme ceci (sockets ici: https://stackoverflow.com/a/34903433/3147071 )

 var withLogEvent = true; // set to false to avoid events logs app.config(function($provide) { if (withLogEvent) { $provide.decorator("$rootScope", function($delegate) { var Scope = $delegate.constructor; var origBroadcast = Scope.prototype.$broadcast; var origEmit = Scope.prototype.$emit; Scope.prototype.$broadcast = function() { console.log("$broadcast was called on $scope " + this.$id + " with arguments:", arguments); return origBroadcast.apply(this, arguments); }; Scope.prototype.$emit = function() { console.log("$emit was called on $scope " + this.$id + " with arguments:", arguments); return origEmit.apply(this, arguments); }; return $delegate; }); } });