angularjsangularjs-provider

Angularjs provider with different configurations in different modules


I'd like to know if there is a way of making the configuration of a provider unique not across the entire application, but only across the module that is configuring it.

For example, I have a module "core" which contains the definition of a provider. Then I have modules "module1" and "module2" which use the provider but have to configure it in a particular way for the specific module.

What is happening with me is that the configuration done in the module defined last overrides the configuration done in all other modules that use the provider in the application.

I made this simple plunker to exemplify this:

var app = angular.module('app', ['module1','module2']);

angular.module('core',[])

.provider('movie', function () {
  var version;
  return {
    setVersion: function (value) {
      version = value;
    },
    $get: function () {
      return {
          title: 'The Matrix' + ' ' + version
      }
    }
  }
});

angular.module('module1',['core'])

.config(function (movieProvider) {
  movieProvider.setVersion('Reloaded');
})

.controller('ctrl1', function ($scope,movie) {
  $scope.title = movie.title;
  $scope.module = 'module1';
});


angular.module('module2',['core'])

.config(function (movieProvider) {
  movieProvider.setVersion('Revolutions');
})

.controller('ctrl2', function ($scope,movie) {
  $scope.title = movie.title;
  $scope.module = 'module2';
});

http://plnkr.co/edit/BI5nhmcdgzQZfZo56Koh?p=preview


Solution

  • Theory and other useless crap

    In angular providers, $get is intended to return a single instance of the provided functionality; $get() is re-executed on each instantiation of the provider (like a factory). The function or object providing the definition of the provider is only instantiated once (like a service).

    So, to get factory-like properties out of your provider you'd want to keep instance-specific properties scoped inside of $get; and to get service-like properties scoped outside of $get.

    This may seem a bit limiting or roundabout at first, but it allows you to combine instance-scoped and globally-scoped properties in really nifty ways.

    Your problem, specifically

    To your problem specifically, if we just move var version into $get, suddenly we get the factory-like behavior you were looking for:

    .provider('movie', function () {
      return {
        $get: function () {
          var version;
    
          var obj = {
              title: function() { return 'The Matrix' + ' ' + version; },
              setVersion: function (value) {
                version = value;
              }
          };
    
          return obj;
        }
      }
    });
    

    (http://plnkr.co/edit/0m9qKzNuhCOOXTbrCQ1T?p=preview)

    Changing the scope (along with changing title to be a function of a variable instead of a string generated statically at the creation of obj), gets us the desired output:

    This is module module1
    
    Title: The Matrix Reloaded
    
    This is module module2
    
    Title: The Matrix Revolutions
    

    This is basically like a factory ATM.

    Where providers actually stand out

    To get into a more normal use case of a provider (where we want to combine global and per-instance scoping), let's say we wanted to have an API provider that uses may use one of several different APIs in different peoples' applications - but only one API per application. We'd want our provider to look roughly like this -

    .provider('movie', function () {
      var imdbEndpoint;
    
      return {
        setApiEndpoint: function(newImdbEndpoint) {
          imdbEndpoint = newImdbEndpoint;
        },
        $get: function () {
          var version;
    
          var obj = {
              title: function() { return imdbEndpoint + ' says the The Matrix version is ' + ' ' + version; },
              setVersion: function (value) {
                version = value;
              }
          };
    
          return obj;
        }
      }
    });
    

    (http://plnkr.co/edit/ZoSgQDpNdX8MOmet7xzM?p=preview)

    Output: This is module module1

    Title: api.imdb.com says the The Matrix version is Reloaded
    
    This is module module2
    
    Title: api.imdb.com says the The Matrix version is Revolutions
    

    So now our API endpoint URL is scoped globally (or on a per-application basis) but we can reference whichever movie we want without getting confused about which layer of the matrix Neo is in - because that's what this is really about, right?