ui-router: $onChanges does not trigger for 1-way binded resolved data
In the following example (also available in this plunker), I was expecting the $onChanges hook to be triggered when the object that is resolved for the consumer component is reassigned by the editor component but it does not. When using the modify method, the modifications are visible because 1-way binding is done by reference, but ideally, the consumer component would do a local clone of the object in the $onChanges hook so as to avoid any unwanted side-effect (like the component modifying the object). Could someone explain me what is happening here ? This second plunker is the same example, but using a service instead of a global variable.
let data = { name: 'initial' };
angular
.module('app', ['ui.router'])
.component('editor', {
controller: function () {
this.modify = (value) => { data.name = value; };
this.replace = (value) => { data = { name: 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: () => data
}
});
})
.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>
`
});
For the record, I have set up a StackOverflow question on the subject.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 4
- Comments: 22 (4 by maintainers)
I founded another, much more better solution for that issue:). Or, I think I found…
component conf:
In ui-router conf:
There is no use component: prop from newest ui-router.
There are bindings name, and onEvent. grandChild is stateless/dumb component, which get his bindings through ui-router conf in template prop in object configuration.
-onEvent binding can be fired in child stateless, and parent statefull component catch it. -name is passed down from parent statefull to stateless child -when binding name is changed in parent, then is changed in child also. -$onChange() in child will be fired when binding will be changed in parent.
Thanks! I thinking would be pretty cool if we could set a flag on a state or on a view to say something like rebindable: true or an option in state.go to say “rebind my resolves but do not reinitialize the component”
It seems that it is the fact that resolved bindings are 1-time binded and not 1-way binded that prevents the $onChanges hook to be invoked in the component when the resolved object is reassigned. Should it be considered as a bug? Would it be possible to do 1-way binding with the resolved properties?
@michalpanek In RC.1, I’ve implemented wiring bindings through the ui-view to the child component as has been discussed in this thread.
The Route to Components doc has been updated with details of how to wire through to the children.
That is now implemented as you describe in
rc.1
@michalpanek thanks for the dumb state hack, it works!
@timothyswt before route to component, updating of resolves was a non starter, because resolves had to be injected. Now that they can be bound to a component, I’m more open to alternatives that update the bindings dynamically.
It’s also worth noting that observables work great in the current paradigm and still allow the async logic to be outside the component in the resolve block.
Thanks for the new feature 😃 What I learn w.r.t. my initial problem here is that for a routed component to be informed that its resolved input bindings have been reassigned, its state has to be reloaded so that its bindings get refreshed. Am I right ? In the docs example, the userDetail input binding is reassigned in the handleUserUpdated function, but the userlist.detail state has to be reloaded in order for the user to be refreshed. In the example, you have to transition to this state because userDetail and userEdit share the same ui-view, but if it was not the case, I still would have expect the $onChanges lifecycle hook of userDetail to be triggered when the handleUserUpdated reassigns the user. This would maybe allow the code of the handleUserUpdated function to be reduced to its minimum which is the reassignement of the user. It kinds of trouble me that this function has to know in which state to transition after the user update. What do you think ?