Угловой компонент 1.5, разрешение пользовательского интерфейса, $onChanges ловушка жизненного цикла
В следующем примере ( plunker) состояние ui-router направляется к компоненту приложения, у которого есть объект данных, и к методу замены, который заменяет этот объект новым, используя заданное значение. В своем шаблоне он имеет:
- компонент редактора, который вызывает метод замены через привязку обратного вызова ('&')
- компонент отображения, который получает объект данных через одностороннюю привязку ('<'), создает локальную копию при срабатывании ловушки жизненного цикла $ 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>
`
});
У меня проблема со следующим вторым примером ( плункер). Это точно такой же параметр, за исключением того, что компонент приложения больше не управляет самими данными, а получает объект данных через одностороннюю привязку ('<'), которая определяется как разрешение состояния маршрутизатора ui (как может). в комментариях я тестировал как глобальный объект и метод, так и взаимодействие через службу). Когда этот разрешенный объект переназначается, я ожидал, что будет запущен хук $ onChanges компонента приложения (как и для компонента отображения, когда объект данных компонента приложения переназначается), но это не так. У кого-нибудь есть объяснение?
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>
`
});
1 ответ
Под капотом, 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>
Наблюдатель, который вызывает $onChanges
зацепить app
компонент следит за $resolve.data
свойство родительской области. Он не реагирует на изменения в $ctrl.data
Свойство области изоляции. И поскольку это однократное связывание, $onChanges
Hook вызывается только тогда, когда переменная инициализируется, а не при последующих изменениях.
Используйте Сервис с 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();
};
});