0

I have a few directives, which will display the same data model in their views. The data will be generated after the ajax call. Currently I put the ajax call into a service, and once the ajax call returned, some variable in the service will be updated. Something like this:

//service
serviceModule.service("myService",function(){
     this.data = [];
     this.ajaxCall = function(){
          ...
         //in ajax success call back
          this.data = response.data;
     }; 
});

//directive A
moduleA.directive("directiveA",function(myService){
     return{
         templateUrl:'someUrl.html'
         link: function(scope){
            scope.data = myService.data;
         } 
     }

})

// someUrl.html
<div ng-repeat="line in data">
{{line.abc}}
</div>

My first question is that I am not sure if this is a good approach to update the view based on the ajax return(I mean put the returned data as an attribute in the service, because from the design point of view, it is just a returned value to be assigned somewhere, not some attribute...)

I tried several approaches to automatically detect the change of the service attribute, which leads to a few questions:

1) my original approach(like the pseudo above) does not work, and I wonder why.I thought by using "scope.data=myService.data", myService.data and scope.data are actually pointed to the same memory address, and once the content of that memory address changed, "scope.data" is also changed, and then should be detected by the directive. It will be very helpful if someone can introduce me the mechanism from the memory point of view.

2) Then I did some google search, and found this helpful post: AngularJS : How to watch service variables? then I changed directive a little bit:

//directive A
moduleA.directive("directiveA",function(myService){
     return{
         templateUrl:'someUrl.html'
         link: function(scope){
            scope.service = myService;
         } 
     }

})

and in the html, I just use service.data instead of data. It doesn't work until I add $rootScope.$apply() in the service:

serviceModule.service("myService",function(){
         this.data = [];
         this.ajaxCall = function(){
             //in ajax success call back
              this.data = response.data;
              **$rootScope.$apply();**
         }; 
    });

So, Could anyone explain how this $apply,$watch works for in angular?

Thanks a lot!

Community
  • 1
  • 1
  • What you're really asking is for an explanation of the digest cycle which is a bit beyond the scope of questions and answers on SO. Luckily there are lots of resources available with this information, including [this sitepoint post](http://www.sitepoint.com/understanding-angulars-apply-digest/) which does a good job of explaining things. – Lex Apr 14 '16 at 21:35

1 Answers1

0

Angular's infamous $digest cycle is the trick behind the magic. What happens is that on each $digest cycle, angular runs over all of the $watchers set in your app (like bound variables) on every $scope (from $rootScope down to the last isolate scope) and see if there are changes. If there are, it updates the view. If there was a change down below and we have 2-way binding - it starts all over again until it nothing changed. Having said that, you can now understand your main goal when building an angular app - have less $digests and less $watches. Less $digests so we wouldn't need to run all the watches many times and "choke up" the thread. Less $watches - so each $digest would be as thing as possible. Now... using $scope.$apply would definitely work since it starts a $digest cycle. The problem - it starts a $digest cycle from the top. It all depends on your needs. So you should understand the need - for instance, if your directives are under the same parent directive, it would be enough to use: $scope.$digest(), which would only "refresh" the parent and its children without involving the rest of the app. If your directives are spread, then you'd might want to update them using some promise from the ajax call instead of using binding, and then do a local $scope.$digest() in the directive itself. So your service might look like:

serviceModule.service("myService",function($q){
var deferred = $q.defer();
         this.data = [];
         this.ajaxCall = function(){
             //in ajax success call back
              this.data = response.data;
deferred.notify(this.data);
         }; 
this.promise = deferred.promise;
    });

And in your directive, instead of binding, you can do this:

moduleA.directive("directiveA",function(myService){
     return{
         templateUrl:'someUrl.html',
         link: function(scope){
            var promise = myService.promise;
promise.then(null, null, function(data){
scope.myData = data;
});
         } 
     }

})

The $q service "smartly" initiates a digest whenever your resolve, reject or notify. You can read a great post about it here: http://www.bennadel.com/blog/2778-exploring-q-and-scope-digest-integration-in-angularjs.htm

This was the shortest way for me to explain about $digests and 2 possible solutions. There are more methods that can be used. Here's another post that shows 2 methods: http://webiks.com/communication-in-an-angular-app-two-approaches/

Hope this helps

yccteam
  • 1,819
  • 3
  • 19
  • 42