jakub.g
jakub.g

Reputation: 41238

Angular 1: $injector can't find a provider dependency when injecting provider inside a provider

My use case is: we have several helper classes, A and B, that are services, A depends on B, and I wanted to make them providers so that they can be used in .config phase.

I followed this SO answer to load a provider inside a provider.

As you can see here, it works:

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

var coreModule = angular.module('CoreModule', []);
coreModule.provider('Car', function() {
  //CarProvider.engine
  this.engine = 'big engine';

  //Car
  this.$get = function() {
    return {
      color: 'red'
    };
  };
});
coreModule.provider('ParameterService', ['$injector', function($injector) {
  try {
    var CarProvider = $injector.get('CarProvider');
    this.deepEngine = CarProvider.engine;
    console.log('deepEngine = ' + this.deepEngine);
  } catch (e) {
    console.log("nope!")
  }

  // ParameterService
  this.$get = function() {
    return {};
  };
}]);
coreModule.config(function(CarProvider) {
  console.log('configEngine = ' + CarProvider.engine); // big engine
});

This works if I have Car and ParameterService in one file in this order.

However when I split Car and ParameterService into multiple files on disk, or I define ParameterService before Car in the same file, $injector.get('CarProvider') inside ParameterService fails.

How do I fix the issue?

I want to have one provider/service per file and I don't understand what is missing.

Upvotes: 2

Views: 852

Answers (2)

Estus Flask
Estus Flask

Reputation: 222369

The order in which the services are defined doesn't matter during run phase, where service instances are injected. But it does matter during configuration phase, where service providers are injected, i.e. in provider constructors and config blocks.

Providers and config blocks are executed in the order in which they are defined. If Car provider is defined after ParameterService provider or config block, CarProvider doesn't exist at the moment when those two are executed.

To avoid potential race conditions, one module per file pattern should be followed. This allows to keep the app highly modular (also beneficial for testing) and never care about the order in which the files are loaded. E.g.:

angular.module('app', ['app.carService', 'app.parameterService']).config(...);

angular.module('app.carService', []).provider('Car', ...);

angular.module('app.parameterService', []).provider('ParameterService', ...);

Module parts are executed in the order in which the modules are defined in angular.module array hierarchy, from children to parents.

The decision if config block needs its own module depends on what it does (mostly for testing reasons).

Upvotes: 2

Jukebox
Jukebox

Reputation: 1603

It is possible to have providers in different files. You just need to attach them to the first module that you created.

If your markup looks like this:

<script src="coreModule.js"></script>
<script src="parameterService.js"></script>

Then, in coreModule.js, define your module:

angular.module('CoreModule', [])
    .provider('Car', function() {
        ...
    }

Remember, the second parameter ([]) tells angular to create a new module.

Then, declare your other provider in a different file, and attach it to your existing 'CoreModule' module:

angular.module('CoreModule')
.provider('ParameterService', ['$injector', function($injector) {
    ...
}

Notice that we are only passing one parameter to .module(). This tells angular to add your provider to an existing module.

Plunkr Demo

Upvotes: 1

Related Questions