Search code examples
javascriptangularjsangularjs-scopeangularjs-service

Passing service between controllers and directives


I have a single service defined as follows:

 angular.module('myApp')
     .factory('myService', function ($resource, $q) {
         mySource = [];
         EndPoint = $resource('/my/api/endpoint', {});
         return({
              getSourceAsFunction: function(){
                  return(mySource);
              }
              ,getSourceAsValue: mySource,
              ,setSource: function(newSource){
                     mySource=newSource;
              }
              ,fetchSource: function(attributes){
                  request = $q.defer
                  EndPoint.get(attributes
                  ,function(success){
                          request.resolve(success.data);
                          //mySource['headersArray'] = success.data['headersArray'];
                          //mySource['footersArray'] = success.data['footersArray'];
                  },function(failure){
                          request.reject(failure);
                  });
                  request.promise
           });
      });

This service is then injected in multiple controllers, on various directives (some of which have isolate scope). In only one of them (as of right now), is the fetch (and resulting set) operation actually executed, but the fetch operation could be executed repeatedly by a user through a click interaction.

The other controllers bind like so:

      $scope.fooVar = myService.getSourceasValue;

or

      $scope.fooVar = myService.getSourceAsFunction();

In both cases the variable does not automatically update when the value in the service is changed.

Setting up a $watch like so:

       $scope.$watch('fooVar', function(newVal, oldVal){console.log('hi')}, true);

Also never triggers (in both cases).

The two commented lines in the resource callback:

       //mySource['headersArray'] = success.data['headersArray'];
       //mySource['footersArray'] = success.data['footersArray'];

mySource was at that point instantiated as {}. This did not fix the issue.

Were the previous attempt to try and reuse the same object.

One way I have found to get around this limitation is to set up a $watch like so:

       $scope.$watch(function(){return(myService.getSourceAsFunction())}
                  , function(newVal, oldVal){console.log('hi'), true)};

However, I find this worrying since I do not want this function to continually run in the background. (How does that work? How often does that function run? How many $digest iterations does that trigger? Just how (in)efficient is that solution?)

How can I get around this? How can I bind to a service variable "by reference" if you will?

Might there be a better approach? I have attempted events, however as some of these elements are siblings, this results in an overly complex chain, where a child might $emit up to have a listener on a joint parent catch an event and $broadcast back down.

What other approaches are there?


Solution

  • What you could do is, set your service under the scope, I.E. $scope.myService = myService and then you can access it straight from the HTML.

    You will also need to send the $scope to your service so once it fetches the data you can run $scope.$apply().

    The problem right now is that you're setting it when the controller initialized. The controller will not be initialized again and therefore it will never update.

    $watch will only be updated once the scope is $digest'd. See the docs here