Reputation: 773
I'm working with parent and child components. The child-component is one-way data bound ('<'
) to a value that comes via an ng-repeat
over an array in the parent component.
<li ng-repeat="item in $ctrl.items">
<child-component item="item"></child-component>
</li>
And the component:
var childComponent = {
bindings: {
item: '<'
},
controller: 'ChildComponentController',
template: '<div>{{ $ctrl.item.name }}</div>'
}
Perhaps I've misunderstood how $onChanges
is supposed to work, but it seems like updating an item in the parent controller's items array should call $onChanges()
in the child. It updates the view as expected. $onChanges()
fires for each item on the initial page load, but not for the change.
Here is the code I'm using to update the parent array:
function change() {
var item = angular.copy(this.items[1]);
item.name = 'baz';
angular.copy(item, this.items[1]);
}
Full Plunk: https://plnkr.co/edit/x7WA08
What I'm ultimately trying to accomplish is to set a boolean this.updated
on the child controller to show an updated indicator in the view. For simplicity the plunk just does a console.log()
.
I've found plenty of examples for communicating changes from the child back up to the parent (which seems more complicated since it is in the opposite direction of the one-way binding), but none of updating the child from the parent with an ng-repeat. That seems like it should just work.
What am I missing here?
Update
I forgot to mention, I'm using components and $onUpdate
because I'm trying to move toward component-oriented architecture and Angular2 compatibility. I'm hoping for a solution that fits with this goal.
Upvotes: 2
Views: 739
Reputation: 3318
onChanges will only trigger when the object reference changes (or when the value changes if it is a primitive type).
Since you are copying back into the original object then the original object reference doesn't change.
To trigger onChanges for the one item changed you can splice the new copy that you updated back into the same position in the array.
However, this will appear as a new object to ng-repeat so onFirstChange() will be true. If you want it to be flagged as an update then we need to copy whatever is being used by ng-repeat for tracking the items in the collection. In this case since 'track by' is not specified it will default to $$hashKey.
function change() {
var item = angular.copy(this.items[1]);
item.name = 'baz';
// Retain the track by id. In this case $$hashKey
// So that it gets flagged as an update instead of isFirstChange()
item.$$hashKey = this.items[1].$$hashKey;
// Trigger onChanges for the single updated item
this.items.splice(1, 1, item);
// OR trigger onChanges for all items
// This will make isFirstChange() true for all items as they will
// lose $$hashKey. If you have a 'track by' specified then isFirstChange()
// will be false.
//this.items = angular.copy(this.items);
}
Upvotes: 0
Reputation: 41
Have you tried using the $watch angular service?
Just try changing the ChildComponentController to:
function ChildComponentController($scope) {
$scope.$watch('$ctrl.item.name', function() {
alert($scope.$ctrl.item.name);
});
}
Upvotes: 1