Search code examples
javascriptangularjsangularjs-componentsangularjs-1.6

Communicate changes between sibling components in AngularJS 1.6


I have an AngularJS 1.6 app that looks like this:

angular.module('app').component('parent', {
    template: '    
      <parent>
         <display options="ctl.options"></display>
         <controls options="ctl.options"></controls>
      </parent>',
    controller: function() {
      this.options = { x: 100, y: 0.2 };
    },
    controllerAs: 'ctl',
    bindToController: true
});

I'd like to use inputs in controls component to modify properties of the options object, so that changes are reflected in display (but without rewriting entire object each time one property has been changed).

How can I do it? Even if I set options to two-way binding in controls, display is not updated and $onChanges does not fire.

It can be easily accomplished with $watch or messages, but I can't figure out a proper component-centric way to do it.


Solution

  • A declarative way to do it would be to have a factory store the data, and pass the factory in to each controller. When either controller/component updates the state, it'll be reflected in the other controller/component.

    var app = angular.module('app', []);
    
    app.factory('Options', function(){
      return {
        data: {
          x: 100,
          y: 0.2
        }
      };
    });
    
    app.controller('DisplayCtrl', function($scope, Options){
      $scope.options = Options.data;
    });
    
    app.controller('ControlsCtrl', function($scope, Options){
       $scope.options = Options.data;
    });
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    <div ng-app="app">
      <div ng-controller="DisplayCtrl">
        <p>x: {{ options.x }}</p>
        <p>y: {{ options.y }}</p>
      </div>
      <div ng-controller="ControlsCtrl">
         <label>
            x: <input type="number" ng-model="options.x">
         </label>
         <label>
            y: <input type="number" ng-model="options.y">
         </label>
      </div>
    </div>

    See Share data between AngularJS controllers for more thoughts. I think https://stackoverflow.com/a/25145593/1927876 is the best answer because it is so declarative and easy to reason about, and is what I base my answer here on.