编辑:我在这里重新阐述了这个问题:angular 1.5 component, ui-router resolve, $onChanges 生命周期钩子
我将用 3 个连续的例子来说明我的问题。
在第一个中,所谓的外部组件拥有一个myData对象,该对象具有一个名称字段。该对象使用单向绑定传递给内部组件。外部组件公开了两种更新其对象的方法:修改名称字段的值,以及用新对象替换它的替换。当调用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>
`
});
在第二个(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>
`
});
在第三个示例(https://plnkr.co/edit/nr9fL9m7gD9k5vZtwLyM?p=preview)中,引入了一个拥有 myData 对象并公开修改和替换方法的服务。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 挂钩(这是我的主要问题!)替换版本确实也不行。。