Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS 1.5.x $onChanges Not Working with One-Way Binding Changes

I don't understand why $onChanges isn't kicked off when I change a bound primitive in an input. Can someone see what I've done wrong, and explain this in an uncomplicated way? I made a plunkr of a quick test application after I couldn't get it to work in my actual application either.

angular
.module('test', [])
.component('test', {

    template: '<child application="vm.application"></child>',
    controller: 'testCtrl as vm'
})
.controller('testCtrl', function() {

    var vm = this;  

    vm.$onInit = function () {

        vm.application = {
            data: {
                name: 'Test'
            }
        }
    };
})
.component('child', {

    template: '<input type="text" ng-model="vm.application.data.name">',
    bindings: {
        application: '<'
    },
    controller: 'childCtrl as vm'
})
.controller('childCtrl', function() {

    var vm = this;

    vm.$onChanges = function (changes) {
        console.log('CHANGED: ', changes);
    };
})
like image 249
mtpultz Avatar asked Jun 04 '16 23:06

mtpultz


People also ask

Does AngularJS use one-way data binding?

One-way data binding in AngularJS means binding data from Model to View (Data flows from the scope/controller to the view). 'ng-bind' is an angular directive used for achieving one-way data binding.

What technique does AngularJS used for two way binding?

AngularJS creates a two way data-binding between the select element and the $ctrl. orderProp model. $ctrl. orderProp is then used as the input for the orderBy filter.

What are bindings in AngularJS?

Data binding in AngularJS is the synchronization between the model and the view. When data in the model changes, the view reflects the change, and when data in the view changes, the model is updated as well.

What is scope on in AngularJS?

AngularJS Scope The scope is the binding part between the HTML (view) and the JavaScript (controller). The scope is an object with the available properties and methods. The scope is available for both the view and the controller.

What does $onchanges do in angular?

The $onChanges is called when the components inits, it's called for the first change and than is not called for the rest of the changes. The property that changes is an object but the changes itself is done on the object reference not on the one of the properties. This works with angular 1.5.3.

What is the API of a compatible component in angular?

Components have a well-defined public API - Inputs and Outputs:However, scope isolation only goes so far, because AngularJS uses two-way binding. So if you pass an object to a component like this - bindings: {item: '='}, and modify one of its properties, the change will be reflected in the parent component.

Is angular taking care of this JS behavior?

This is JS behavior, not Angular fault — but it’s easy to think that Angular will take care about it, while it won’t. What is real example of this situation? Imagine you have a select component which encapsulates common behavior like option translation, proper styling etc, and it takes array with options as binding:

Are You having difficulty with angular C omponent bindings?

C omponent bindings from Angular 1.5 had few difficulties. Although my problems may seem simple for experienced developers, I hope this post will help to protect somebody from unnecessary waste of time.


Video Answer


2 Answers

The $onChanges method is not called for changes on subproperties of an object. Default changes to objects generally follow this sequence within a components lifetime:

  1. UNINITIALIZED_VALUE to undefined
  2. undefined to {} or { someAttribute: someValue, .. }
  3. ({..} to undefined if you delete the object in a parent scope)

In order to watch subproperties you could use the $doCheck method that was added in 1.5.8. It is called on every digest cycle and it takes no parameters. With great power comes great responsibility. In that method you would put logic that detects whether a certain value has been updated or not - the new value will already be updated in the controller's scope, you just need to find a way to determine if the value changed compared to the previously known value.

You could set a previousValueOfObjectAttribute variable on the controller before you start to expect changes to this specific attribute (e.g. when subcomponent B calls an output binding function in component A, based on which the target object - which is an input binding to B - in A changes). In cases where it is not predictable when the change is about to occur, you could make a copy of the specific atributes of interest after any change observed via the $doCheck method.

In my specific use case, I did not explicitly check between an old and new value, but I used a promise (store $q.defer().promise) with the intention that any change I would 'successfully' observe in the $doCheck method would resolve that promise. My controller then looked something like the following:

dn.$doCheck = function () {
  if (dn.waitForInputParam &&
      dn.waitForInputParam.promise.$$state.status === 0 &&
      dn.targetObject.targetAttribute !== false)
    dn.waitForInputParam.resolve(dn.targetObject.targetAttribute);
}

dn.listenToInputChange = function () {
  dn.waitForInputParam = $q.defer();
  dn.waitForInputParam.promise.then(dn.onInputParamChanged);
}

dn.onInputParamChanged = function (value) {
  // do stuff
  //

  // start listening again for input changes -- should be async to prevent infinite $digest loop
  setTimeout(dn.listenToInputChange, 1);
}

(w.r.t. promise.$$state.status, see this post).

For all other intents and purposes, watching changes to primitive data types, you should still use $onChanges. Reference: https://docs.angularjs.org/guide/component

like image 181
David Remie Avatar answered Sep 27 '22 21:09

David Remie


It's $onChanges and not $onChange.

Also, the onChange only updates when the parent value is changed, not the child. Take a look at this plunkr. Note the console.log only fires when you type in the first input.

like image 44
GBa Avatar answered Sep 27 '22 21:09

GBa