angularjs - triggering $onChanges for updated one way binding -
i'm happy "new" $onchanges method can implement in component's controller. seems triggered when bound variable overwritten outside component, not (for instance) when item added existing array
it intended behaviour or bug? there way of listening updates input bindings, besides doing $scope.$watch on it?
i'm using angular 1.5.3
first tl;dr array bounded via one-way binding, watch expression added not check object equality uses reference checking. means adding element array never fire '$onchanges' method, since watcher never 'dirty'.
i've created plnkr demonstrates this: http://plnkr.co/edit/25pdle?p=preview
click 'add vegetable in outer' , 'change array reference in outer' , @ 'number of $onchanges invocation'. change latter button.
complete explanation grasp going on, should check angular code base. when '<' binding found, following code used set watch expression.
case '<': if (!hasownproperty.call(attrs, attrname)) { if (optional) break; attrs[attrname] = void 0; } if (optional && !attrs[attrname]) break; parentget = $parse(attrs[attrname]); destination[scopename] = parentget(scope); // important part // removewatch = scope.$watch(parentget, function parentvaluewatchaction(newparentvalue) { var oldvalue = destination[scopename]; recordchanges(scopename, newparentvalue, oldvalue); destination[scopename] = newparentvalue; }, parentget.literal); // ------------- // removewatchcollection.push(removewatch); break;
the important part here how 'scope.$watch' expression set up. parameters passed parsed expression , listener function. listener function fired once '$watch' found dirty in digest cycle. if fired, listener execute 'recordchanges' method. records '$onchanges' callback task executed in '$postdigest' phase , notify components listening '$onchanges' lifecycle hook tell them if value has changed.
what's important keep in mind here, if '$watcher' never dirty, '$onchanges' callback not triggered. more importantly, way '$watch' expression created, never dirty, unless reference changes. if wanted check equality between objects instead of reference, should pass third parameter asks this:
$watch: function(watchexp, listener, objectequality, prettyprintexpression)
as not case here way 1 way binding set up, check reference.
this means, if add element array, reference not changed. meaning '$watcher' never dirty, meaning '$onchanges' method not called changes array.
to demonstrate this, i've created plnkr: http://plnkr.co/edit/25pdle?p=preview
it contains 2 components, outer , inner. outer has primitive string value can changed through input box , array can extended adding element or have reference changed.
inner has 2 one-way bounded variables, value , array. listens changes.
this.$onchanges = settype; function settype() { console.log("called"); vm.callcounter++; }
if type input field, '$onchanges' callback fired every time. logical , expected, since string primitive cannot compared reference, meaning '$watcher' dirty, , '$onchanges' lifecycle hook fired.
if click 'add vegetable in outer', execute following code:
this.changevaluearray = function() { vm.valuearray.push("tomato"); };
here add value existing bounded array. we're working reference here, '$watcher' not fired , there no callback. not see counter increment or 'called' statement in console.
note: if click 'add array' inside inner component, array in outer component changes. logical, since updating exact same array reference. though one-way binding, array can updated inside inner component.
if change reference in outer component clicking 'change array reference in outer', '$onchanges' callback fired expected.
as answer question: intended behaviour or bug? guess intended behaviour. otherwise have given option define '<' binding in way check object equality. can create issue on github , ask question if you'd like.
Comments
Post a Comment