angular 1.5 组件,ui-路由器解析,$onChanges 生命周期钩子
angular 1.5 component, ui-router resolve, $onChanges lifecycle hook
在以下示例 (plunker) 中,ui-路由器状态路由到具有 data 的 app 组件 对象和 replace 方法,该方法使用给定值将此对象替换为新对象。在它的模板中,它有:
- 一个 editor 组件,它通过回调绑定 ('&')
触发 replace 方法
- a display 组件通过单向绑定('<')接收 data 对象,当$onChanges 生命周期钩子被触发,并显示对象的内容
一切正常,符合预期:-)
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>
`
});
我对以下第二个示例 (plunker) 有疑问。这是完全相同的设置,只是 app 组件不再管理 data 本身,而是接收一个 data 对象通过定义为 ui-路由器状态的解析的单向绑定('<')(如评论中所示,我使用全局对象和方法进行了测试,并通过服务进行交互)。当这个已解析的对象被重新分配时,我期待 app 组件的 $onChanges 挂钩被触发(因为它是 display 组件app 组件的数据对象被重新分配),但事实并非如此。有人有解释吗?
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>
`
});
在幕后,UI-ROUTER
创建 HTML,并一次性绑定到父作用域上的 $resolve.data
。
<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>
调用 app
组件中的 $onChanges
挂钩的观察者观察父作用域的 $resolve.data
属性。它不会对隔离范围的 $ctrl.data
属性 的更改做出反应。由于它是一次性绑定,因此 $onChanges
挂钩仅在变量初始化时被调用,而不会在后续更改时被调用。
使用 RxJZ 服务
与其尝试弯曲 $onChange
来做一些其设计目的之外的事情,不如使用 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);
}
};
});
然后只需订阅更改即可。
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();
};
});
在以下示例 (plunker) 中,ui-路由器状态路由到具有 data 的 app 组件 对象和 replace 方法,该方法使用给定值将此对象替换为新对象。在它的模板中,它有:
- 一个 editor 组件,它通过回调绑定 ('&') 触发 replace 方法
- a display 组件通过单向绑定('<')接收 data 对象,当$onChanges 生命周期钩子被触发,并显示对象的内容
一切正常,符合预期:-)
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>
`
});
我对以下第二个示例 (plunker) 有疑问。这是完全相同的设置,只是 app 组件不再管理 data 本身,而是接收一个 data 对象通过定义为 ui-路由器状态的解析的单向绑定('<')(如评论中所示,我使用全局对象和方法进行了测试,并通过服务进行交互)。当这个已解析的对象被重新分配时,我期待 app 组件的 $onChanges 挂钩被触发(因为它是 display 组件app 组件的数据对象被重新分配),但事实并非如此。有人有解释吗?
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>
`
});
在幕后,UI-ROUTER
创建 HTML,并一次性绑定到父作用域上的 $resolve.data
。
<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>
调用 app
组件中的 $onChanges
挂钩的观察者观察父作用域的 $resolve.data
属性。它不会对隔离范围的 $ctrl.data
属性 的更改做出反应。由于它是一次性绑定,因此 $onChanges
挂钩仅在变量初始化时被调用,而不会在后续更改时被调用。
使用 RxJZ 服务
与其尝试弯曲 $onChange
来做一些其设计目的之外的事情,不如使用 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);
}
};
});
然后只需订阅更改即可。
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();
};
});