2

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.

georgeawg
  • 48,608
  • 13
  • 72
  • 95
monday
  • 319
  • 2
  • 12
  • 1
    @torazaburo totally missing the point of the question here. – ZombieChowder Jun 14 '18 at 14:46
  • @ZombieChowder Who is missing what point? I was just commenting on terminology. –  Jun 14 '18 at 16:55
  • `$onChanges` life-cycle hook only fires when the *identity* of an object changes. If you want to check for changes in the *contents* of an object, use the `$doCheck` life cycle hook. – georgeawg Jun 14 '18 at 18:30

2 Answers2

2

The $onChanges life-cycle hook only fires when the identity of an object changes. If you want to check for changes to the contents of an object, use the $doCheck life cycle hook.

From the Docs:

Life-Cycle Hooks

  • $doCheck() - Called on each turn of the digest cycle. Provides an opportunity to detect and act on changes. Any actions that you wish to take in response to the changes that you detect must be invoked from this hook; implementing this has no effect on when $onChanges is called. For example, this hook could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which would not be detected by AngularJS's change detector and thus not trigger $onChanges. This hook is invoked with no arguments; if detecting changes, you must store the previous value(s) for comparison to the current values.

-— AngularJS $compile Service API Reference - Life-cycle hooks

See also AngularJs 1.5 - Component does not support Watchers, what is the work around?

georgeawg
  • 48,608
  • 13
  • 72
  • 95
2

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.

Adam Zerner
  • 17,797
  • 15
  • 90
  • 156