Search code examples
javascripthtmlangularjscomponentsangularjs-components

AngularJS 1.5 component won't invoke $onChanges upon change


I have an AngularJS 1.5 component inside an html page (not parent component) and the component won't invoke $onChanges upon change.

HTML

<my-comp standards-data="standardsData"></my-comp>

Component

angular.module("bla").component('myComp', {
    templateUrl: '/my-comp.tpl.html',
    controller: myController,
    bindings: {
        standardsData: '<'
    }
});

function myController() {
    var self = this;

    self.$onInit = function () {
        self.standardsData = {};
    }

    self.$onChanges = function (changes) {
        self.standardsData.something = changes.standardsData.currentValue.something;
    };
}

When I fetch new data in the ctrl of the html that contains my component, it won't influence the component. I get only into the $onInit of the component and after $scope.standardsData changes (in the containing html/ctrl), the $OnChanges of my component won't invoke.

Hope I described the problem correctly, Thanks !


Solution

  • Your component binding to standardsData is a binding to an object reference, which does not change even when one of its properties is modified. As a result, $onChanges is not triggered. This is due to the fact that $onChanges uses a shallow $watch.

    In other words, $onChanges is triggered only if we use primitives (i.e. non-objects) or change the reference of the javascript object bound into the component

    So, you need to either: 1) Bind manually to the property/properties you want, rather than the object reference, or 2) Change the entire standardsData object when its data changes. You could also 3) Rewrite $onChanges functionality, I suppose (not recommended).

    Option 1: Bind only to the property

    Use if the parent controller/component merely changes a property or properties.

    <my-comp standards-data-something="standardsData.something"></my-comp>
    

    and

    bindings: {
        standardsDataSomething: '<'
    }
    

    Option 2: Change the reference

    Use if the parent controller/component obtains completely new standardsData. You would keep your current HTML for this option and set:

    standardsData = {newData};  //Reset the new data from http or wherever
    

    to change the reference.


    Some further reading you may find interesting:

    http://blog.kwintenp.com/the-onchanges-lifecycle-hook/

    http://www.codelord.net/2016/12/20/replacing-angulars-deep-watches-with-the-%24docheck-lifecycle-hook/