1

I am trying to listen/watch to a variable change in a controller into a directive, for link in a directive watch works but I am trying using an API which has work done at compile time(directive lifecycle). So, I am looking for any work-around so I could watch variable change at compile level.

Note: I am using angular-1.4.

Below is code for a link that would work and below it is code that I wish could work but not working(It seems at compile $watch can't be applied.)

Html code:

<div ng-app="myApp">
  <div ng-controller="myCtrl">
    <counter model2="count1"></counter>

  </div>
</div>

Working code :

var app = angular.module('myApp', [])

app.directive('counter',
  function($timeout) {
    return {
      restrict: 'EAC',
      template: `<div>Directive Counter: {{internalCount}}</div>`,
      scope: {
        internalCount: '=model2'
      },

      link: function($scope, element) {
        function addCount() {
          $timeout(function() {
            $scope.internalCount += 1;

            addCount();
          }, 1000)

        }
        $scope.$watch('internalCount', function() {
          console.log("hrruy!!!");
          console.log($scope.internalCount);
        });
        addCount();

      }
    };
  }
)

app.controller('myCtrl', function($scope) {
  $scope.count1 = 10;
});

Below is the Code that i wish should work(i.e using watch at compile level):

var app = angular.module('myApp', [])

app.directive('counter',
  function($timeout) {
    return {
      restrict: 'EAC',
      template: `<div>Directive Counter: {{internalCount}}</div>`,
      scope: {
        internalCount: '=model2'
      },
            link:function(){
      console.log("linker callled...");
      },
      compile: function($scope, element) {
      return function (scope, element, attrs) {
        function addCount() {
          $timeout(function() {
            $scope.internalCount += 1;

            addCount();
          }, 1000)

        }
        scope.$watch('internalCount', function() {
          console.log("hrruy!!!");
          console.log($scope.internalCount);
        });
        addCount();
                }
      }
    };
  }
)

app.controller('myCtrl', function($scope) {
  $scope.count1 = 10;
});

Edited above code as per suggested corrections by Anoop and Hitman's and exact problem requirement to work:

var app = angular.module('myApp', [])

app.directive('counter',
  function($timeout) {
    return {
      restrict: 'EAC',
      template: `<div>Directive Counter: {{internalCount}}</div>`,
      scope: {
        internalCount: '=model2'
      },
      compile: function(element, attrs) {
        return function(scope, element, attrs) {

          scope.$watch('internalCount', function() {
            console.log("hrruy!!!");
            console.log(scope.internalCount);
          }, true);
          //addCount();

        }
      }
    };
  }
)

app.controller('myCtrl', function($scope,$timeout) {
    console.log("MAin Controller!! works!!!");
    $scope.count1 = 10;
    function addCount() {
            $timeout(function() {
              //$scope.internalCount += 1;
                            $scope.count1+=1;
              //addCount();
            }, 1000)

          }
         $scope.$watch('count1', function() {
            console.log("Main-hrruy!!!");
            addCount();
            console.log($scope.count1);
          }, true);
});

Note : As per problem statement here , last code uses "$scope.count1" variable from controller and listens/watches in directive compile function (default post-link being done).

But still a problem arises if say "$scope.count1" is a complex nested JSON map/object. In that scenario, directive does not detect count1 changes.So As how do I keep watching in directive compile function if controller variable is a complex object?

halfer
  • 19,824
  • 17
  • 99
  • 186
Kimchy
  • 501
  • 8
  • 24
  • In second case, neither the $watch works and nor link function gets called ?? – Kimchy Jun 23 '17 at 07:03
  • the function `compile` has `templateElement` and `templateAttributes` as arguments, in that phase the `$scope` object isn't mounted yet. https://stackoverflow.com/questions/24615103/angular-directives-when-and-how-to-use-compile-controller-pre-link-and-post#24615185 – Hitmands Jun 23 '17 at 08:40

1 Answers1

0

In 2nd code snippet, you use compile function, in which you return a postlink function (default), So your link function doesn't work.

From docs. : This(Link) property is used only if the compile property is not defined. (read doc.)

Now, Inside your compile function, you return postlink function and do every manipulation inside it. So you are bind to scope, not $scope

Here in code : return function (scope, element, attrs) {\\

Here is fixed in fiddle

anoop
  • 3,812
  • 2
  • 16
  • 28
  • thanks @anoop , I have editted my problem to work as per clarification (please check last code block and problem statement at the end of query above), but i need further small help a bit further that i am yet not cleared of mess :( – Kimchy Jun 23 '17 at 11:28
  • @user3152549 : You can use `$broadcast and $on` to detect changes. See this updated [fiddle](http://jsfiddle.net/8e56wpnv/), and i've increase time to 5000ms. – anoop Jun 23 '17 at 11:48
  • I have tried that but as per my application it has complicated the solution and complexity has increased with some UI issues as well. – Kimchy Jun 23 '17 at 13:09
  • @user3152549 : there need some event in order to detect changes, your original problem of watching is solved. however `$watch` itself is heavy operation, and with complex data types it decreses performance. I suggest rather switch to events.($broadcast, $on) and remove the $watch altogether. Hope it helps – anoop Jun 23 '17 at 13:19
  • thanks for suggestion but any i idea why $scope.count1 in controller for complex json map does not get detected in directive in last code block? – Kimchy Jun 27 '17 at 04:38
  • it should detect the change, I've shown you correction in fiddle for your `count1` that was working, and for complex data you need to enable `deep watch` by making 3rd param `true`, could you create some fiddle\plunker to reproduce your issue? – anoop Jun 27 '17 at 07:24
  • for me count1 gets Json String Object from a web service as i select item in drop down in UI and watcher in the controller detects the changed count1 but watcher inside the directive does not detect any change although directive watcher executes on page loading time and for initial run correct value of count1 is printed(default count1 value). So, initial value for count1 from controller is being received inside directive perfectly but only once and further drop down change invokes controller watcher but not directive watcher. Also , i am already using deep matching as true.I would recheck well – Kimchy Jun 27 '17 at 09:06
  • where is the code pen ?, and I think I've fixed your original problem you should have marked this answer closed this thread and should post a new qstn instead. – anoop Jul 12 '17 at 11:04
  • I have got back to this issue and would update asap the persisting problem statement. please give me some time. – Kimchy Jul 13 '17 at 04:45
  • 1
    Thanks anoop. Problem was not your answer but in my actual code i was not able to listen due to 2 reason i observed : 1) directive isolated scope attribute names should be in lower case. I was using camel case. 2) variables in scope that are related to ng-model if initialized showed continuously varying variable value ( i used $interval to print ng-model related variable per second which showed continuously switching vatiable value with initial value per second) – Kimchy Jul 13 '17 at 16:28