編集: ここでこの質問を再定式化しました: angular 1.5 コンポーネント、ui-router resolve、$onChanges ライフサイクル フック
私の質問を3つの連続した例で説明します.
最初のものでは、いわゆる外部コンポーネントがnameフィールドを持つmyDataオブジェクトを所有しています。このオブジェクトは、一方向バインディングを使用して内部コンポーネントに渡されます。外側のコンポーネントは、そのオブジェクトを更新するための 2 つのメソッドを公開します。nameフィールドの値を変更するmodifyと、それを新しいオブジェクトに置き換える replaceです。一方向バインディングは参照によって行われるため、modify が呼び出されると、内部コンポーネントに表示されるオブジェクトが更新されます。交換時が呼び出されると、一方向バインディングが参照の変更を監視し、$onChanges フックが呼び出されるため、内部コンポーネントに表示されるオブジェクトも更新されます。ここではすべて問題ありません:-)
angular
.module('app', [])
.component('outer', {
controller: function () {
this.myData = {
name: 'initial'
};
this.modify = (value) => { this.myData.name = value; };
this.replace = (value) => { this.myData = { name: value }; };
},
template: `
<p> outer value : {{ $ctrl.myData.name }} </p>
<input type="text" ng-model="value">
<button ng-click="$ctrl.modify(value)"> modify object </button>
<button ng-click="$ctrl.replace(value)"> replace object </button>
<hr>
<inner my-data="$ctrl.myData"></inner>
`
})
.component('inner', {
bindings: {
myData: '<'
},
controller: function () {
this.nbOnChangesCalls = 0;
this.$onChanges = (changes) => { this.nbOnChangesCalls++; }
},
template: `
<p> inner value : {{ $ctrl.myData.name }} (nb onChanges calls : {{ $ctrl.nbOnChangesCalls }}) </p>
`
});
2 つ目 ( https://plnkr.co/edit/uEp6L4LuWqTJhwn6uoTN?p=preview ) では、外側のコンポーネントはmyDataオブジェクトを宣言しなくなりましたが、ui-router コンポーネントの解決に由来する一方向バインディングとして受け取ります。ルーテッド状態。外側のコントローラー内で変更および置換メソッドが同じ方法で行われるため、すべてが前の例のように動作します。
angular
.module('app', ['ui.router'])
.config(($urlRouterProvider, $stateProvider) => {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('root', {
url: '/',
component: 'outer',
resolve: {
myData: () => ({ name: 'initial' })
}
});
})
.component('outer', {
bindings: {
myData: '<'
},
controller: function () {
this.nbOnChangesCalls = 0;
this.$onChanges = (changes) => { this.nbOnChangesCalls++; }
this.modify = (value) => { this.myData.name = value; };
this.replace = (value) => { this.myData = { name: value }; };
},
template: `
<p> outer value : {{ $ctrl.myData.name }} (nb onChanges calls : {{ $ctrl.nbOnChangesCalls }}) </p>
<input type="text" ng-model="value">
<button ng-click="$ctrl.modify(value)"> modify object </button>
<button ng-click="$ctrl.replace(value)"> replace object </button>
<hr>
<inner my-data="$ctrl.myData"></inner>
`
})
.component('inner', {
bindings: {
myData: '<'
},
controller: function () {
this.nbOnChangesCalls = 0;
this.$onChanges = (changes) => { this.nbOnChangesCalls++; }
},
template: `
<p> inner value : {{ $ctrl.myData.name }} (nb onChanges calls : {{ $ctrl.nbOnChangesCalls }}) </p>
`
});
3 番目の例 ( https://plnkr.co/edit/nr9fL9m7gD9k5vZtwLyM?p=preview ) では、 myData オブジェクトを所有し、modify メソッドと replace メソッドを公開するサービスが導入されています。ui-router 状態の解決は、サービスを呼び出してデータ オブジェクトを取得するようになりました。外側のコントローラーは、サービスの変更および置換メソッドを呼び出します。replaceメソッドへの最初の呼び出しまで、すべて問題ありません。サービス内のオブジェクトの参照が変更されたときに外部コンポーネントの $onChanges フックがトリガーされることを期待していましたが、そうではありません。
angular
.module('app', ['ui.router'])
.factory('DataService', () => {
let data = { name: 'initial' };
return {
getData: () => data,
modify: (value) => { data.name = value; },
replace: (value) => { data = { name: value }; }
}
})
.config(($urlRouterProvider, $stateProvider) => {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('root', {
url: '/',
component: 'outer',
resolve: {
myData: (DataService) => DataService.getData()
}
});
})
.component('outer', {
bindings: {
myData: '<'
},
controller: function (DataService) {
this.nbOnChangesCalls = 0;
this.$onChanges = (changes) => { this.nbOnChangesCalls++; }
this.modify = (value) => { DataService.modify(value); };
this.replace = (value) => { DataService.replace(value); };
},
template: `
<p> outer value : {{ $ctrl.myData.name }} (nb onChanges calls : {{ $ctrl.nbOnChangesCalls }}) </p>
<input type="text" ng-model="value">
<button ng-click="$ctrl.modify(value)"> modify object </button>
<button ng-click="$ctrl.replace(value)"> replace object </button>
<hr>
<inner my-data="$ctrl.myData"></inner>
`
})
.component('inner', {
bindings: {
myData: '<'
},
controller: function () {
this.nbOnChangesCalls = 0;
this.$onChanges = (changes) => { this.nbOnChangesCalls++; }
},
template: `
<p> inner value : {{ $ctrl.myData.name }} (nb onChanges calls : {{ $ctrl.nbOnChangesCalls }}) </p>
`
});
ここで何が間違っていますか?
精度:変更メソッドを使用すると問題なく動作します。ただし、コンポーネント内でバインドされたオブジェクトのローカル コピーを実行して、そこから元のオブジェクトを変更することを回避し (実際の一方向のデータ フローを確保)、$onChanges フックを使用してオブジェクトを通知できるようにしたいと考えています。変更(再割り当て)。
編集:解決でデータを取得するコンポーネントがサービスで変更をトリガーするコンポーネントではない、次のコード ( https://plnkr.co/edit/DZqgb4aIi09GWiblFcmR?p=preview )を思いつきました。問題は、解決されたデータ オブジェクトが再割り当てされたときに、コンシューマーコンポーネントで $onChanges フックがトリガーされないのはなぜですか? このコンポーネントに変更を通知するにはどうすればよいですか?
angular
.module('app', ['ui.router'])
.factory('DataService', () => {
let data = { name: 'initial' };
return {
getData: () => data,
modify: (value) => { data.name = value; },
replace: (value) => { data = { name: value }; }
}
})
.component('editor', {
controller: function (DataService) {
this.modify = (value) => { DataService.modify(value); };
this.replace = (value) => { DataService.replace(value); };
},
template: `
<input type="text" ng-model="value">
<button ng-click="$ctrl.modify(value)"> modify object </button>
<button ng-click="$ctrl.replace(value)"> replace object </button>
`
})
.config(($urlRouterProvider, $stateProvider) => {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('root', {
url: '/',
component: 'consumer',
resolve: {
myData: (DataService) => DataService.getData()
}
});
})
.component('consumer', {
bindings: {
myData: '<'
},
controller: function () {
this.nbOnChangesCalls = 0;
this.$onChanges = (changes) => { this.nbOnChangesCalls++; }
},
template: `
<p> value : {{ $ctrl.myData.name }} (nb onChanges calls : {{ $ctrl.nbOnChangesCalls }}) </p>
`
});
理想的には、消費者コンポーネントの $onChanges メソッドは次のように実装されます。
this.$onChanges = (changes) => {
if (changes.myData) this.myData = Object.assign({}, this.myData);
this.nbOnChangesCalls++;
}
この場合、変更バージョンは機能せず (コンポーネントにはサービス データ オブジェクトへの参照がもうないため、予想どおり)、$onChanges フックがトリガーされないため (これが私の主な質問です!) 置換バージョンは機能しますどちらも動作しません..