angular 1.5 componente, soluzione ui-router, $ onChanges hook ciclo di vita

Nell'esempio seguente ( plunker ), uno stato ui-router si avvia verso un componente app che dispone di un object dati e di un metodo di sostituzione che sostituisce questo object con un nuovo utilizzando il valore specificato. Nella sua maschera, ha:

  • un componente editor che innesca il metodo di sostituzione tramite un binding di richiamata ('&')
  • un componente di visualizzazione che riceve l'object dati tramite un legame a 1 senso ('<'), fa una copia locale quando viene triggersto il hook del ciclo di vita di $ onChanges e visualizza il contenuto dell'object

Tutto funziona bene e come previsto 🙂

angular .module('app', ['ui.router']) .config(($urlRouterProvider, $stateProvider) => { $urlRouterProvider.otherwise('/'); $stateProvider .state('root', { url: '/', component: 'app' }); }) .component('app', { controller: function () { this.data = { name: 'initial' }; this.replace = (value) => { this.data = { name: value }; }; }, template: ` <editor on-replace="$ctrl.replace(value)"></editor> <display data="$ctrl.data"></display> ` }) .component('editor', { bindings: { onReplace: '&' }, controller: function () { this.replace = (value) => this.onReplace({value}); }, template: ` <input type="text" ng-model="value"> <button ng-click="$ctrl.replace(value)"> replace object </button> ` }) .component('display', { bindings: { data: '<' }, controller: function () { this.$onChanges = (changes) => { if (changes.data) { this.data = Object.assign({}, this.data); } }; }, template: ` <p> value : {{ $ctrl.data.name }} </p> ` }); 

Ho un problema con il secondo esempio seguente ( plunker ). È l'impostazione esatta, salvo che la componente app non gestisce più i dati in se, ma riceve un object di dati tramite un legame a 1 via ('<') definito come una soluzione dello stato ui-router (come può essere visto nei commenti, ho testato sia utilizzando un object e un metodo globali, sia interagendo con un servizio). Quando l'object risolto viene riassegnato, mi aspettavo che l'interruttore $ onChanges del componente dell'app per essere triggersto (come per il componente di visualizzazione quando l'object dati del componente dell'app è riassegnato), ma questo non è il caso. Qualcuno ha una spiegazione?

 let data = { name: 'initial' }; const replace = (value) => { data = { name: value }; }; angular .module('app', ['ui.router']) /*.factory('DataService', function () { let data = { name: 'initial' }; return { getData: () => data, replace: (value) => { data = { name: value }; } }; })*/ .config(($urlRouterProvider, $stateProvider) => { $urlRouterProvider.otherwise('/'); $stateProvider .state('root', { url: '/', component: 'app', resolve: { data: () => data /*(DataService) => DataService.getData()*/ } }); }) .component('app', { bindings: { data: '<' }, controller: function (/*DataService*/) { this.$onChanges = (changes) => { if (changes.data) { this.data = Object.assign({}, this.data); } }; this.replace = (value) => { replace(value); }; /*(value) => { DataService.replace(value); };*/ }, template: ` <editor on-replace="$ctrl.replace(value)"></editor> <display data="$ctrl.data"></consumer> ` }) .component('editor', { bindings: { onReplace: '&' }, controller: function () { this.replace = (value) => this.onReplace({value}); }, template: ` <input type="text" ng-model="value"> <button ng-click="$ctrl.replace(value)"> replace object </button> ` }) .component('display', { bindings: { data: '<' }, controller: function () { this.$onChanges = (changes) => { if (changes.data) { this.data = Object.assign({}, this.data); } }; }, template: ` <p> value : {{ $ctrl.data.name }} </p> ` }); 

Sotto il cofano, UI-ROUTER crea un HTML con un legame $resolve.data a $resolve.data sul field principale.

 <app data="::$resolve.data" class="ng-scope ng-isolate-scope"> <editor on-replace="$ctrl.replace(value)" class="ng-isolate-scope"> <input type="text" ng-model="value" class="ng-pristine ng-untouched ng-valid ng-empty"> <button ng-click="$ctrl.replace(value)"> replace object </button> </editor> <display data="$ctrl.data" class="ng-isolate-scope"> <p class="ng-binding"> value : initial </p> </display> </app> 

L'osservatore che invoca l'hook $onChanges nel componente app guarda la properties; $resolve.data del field di applicazione principale. Non reagisce alle modifiche apportte alla properties; $ctrl.data ctrl.data del field di isolamento. E poiché è un legame un tempo, il hook $onChanges viene richiamato solo quando la variabile viene inizializzata e non sulle modifiche successive.


Utilizzare un servizio con RxJZ

Invece di cercare di piegare $onChange per fare qualcosa per cui non è stato progettato, build un servizio con RxJS Extensions for Angular .

 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); } }; }); 

Quindi abbonarsi semplicemente alle modifiche.

 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(); }; }); 

Il DEMO su PLNKR .