Search code examples
angularjsangularjs-componentsangularjs-1.5

Two way data bindings does not trigger $onChanges in Components


Two way data bindings not updating between components

I am setting up inter component communication using two way data binding. I have one parent controller which fetches data from AJAX call and sends that data to 2 components.

I have tried to modify the data that is passed to the components, but if child1 component updates the data, child component is not getting the update data though the two way data binding is present. I read that $onChanges hook will not capture the change event for two way data binding.

<div ng-controller="ParentController as ctrl">
    <child1 data="ctrl.data"></child1>
    <child2 data="ctrl.data"></child>
</div>

Parent Controller:

var app = angular.module('app',[]);
app.controller('ParentController', function($scope, $get){
   //get data from AJAX call
   this.data = getDataFromAjaxCall();
}

Child1 Component:

app.component('child1',{
   bindings : {
      data : '='
   },
   controller: function($scope){
      var self = this;
      self.$onChanges = function(changes){
         if(changes.data)
            console.log('data changed');
      }
      self.addData = function(){
         self.data.push({
            id : 10,
            name : 'abc'
         });
      }
   }
});

Child2 Component:

app.component('child2',{
   bindings : {
      data : '='
   },
   controller: function($scope){
      var self = this;
      self.$onChanges = function(changes){
         if(changes.data)
            console.log('data changed');
      }
      self.addData = function(){
         self.data.push({
            id : 20,
            name : 'pqr'
         });
      }
   }
});

I expect to get the updated data in child1 component if the child2 component modified the data and vice versa.


Solution

  • The $onChanges life-cycle hook only trigger on changes to one-way ("<") and attribute ("@") bindings. It does not trigger on changes to two-way ("=") bindings.

    With components, use one-way ("<") binding for inputs and expression ("&") binding for outputs:

    app.component('child1',{
       bindings: {
           ̶d̶a̶t̶a̶ ̶:̶ ̶'̶=̶'̶
           facts: "<",
           factsChange: "&", 
       },
       controller: function(){
          this.$onChanges = function(changes){
             if(changes.facts)
                console.log('facts changed');
          }
       }
    });
    

    Avoid using two-way ("=") bindings. They make migration to Angular 2+ more difficult.

    For more information, see AngularJS Developer Guide - Component-Based Application Architecture.

    Also be careful with bindings that start with data. Directive normalization will strip names that start with data-. See AngularJS Developer Guide - Directive Normalization.


    Functions that do XHRs can't return data. They can only return promises from which data need to be extracted.

    var app = angular.module('app',[]);
    app.controller('ParentController', function($scope, $get){
       //get data from AJAX call
       ̶t̶h̶i̶s̶.̶d̶a̶t̶a̶ ̶=̶ ̶g̶e̶t̶D̶a̶t̶a̶F̶r̶o̶m̶A̶j̶a̶x̶C̶a̶l̶l̶(̶)̶;̶
       var promise = getDataFromAjaxCall();
       promise.then( response => {
           this.data = response.data;
       });
    }
    

    JavaScript browsers uses a single-threaded non-blocking event-driven architecture for IO. Programmers familiar with imperative programming styles need to change the way they think about IO with JavaScript browsers.