Tech and Media Labs
This site uses cookies to improve the user experience.




AngularJS Modularization & Dependency Injection

Jakob Jenkov
Last update: 2015-09-02

AngularJS comes with a built-in dependency injection mechanism. You can divide your application into multiple different types of components which AngularJS can inject into each other. Modularizing your application makes it easier to reuse, configure and test the components in your application.

AngularJS contains the following core types of objects and components:

  • Value
  • Factory
  • Service
  • Provider
  • Constant

These core types can be injected into each other using AngularJS dependency injection mechanism. Throughout the rest of this text I will explain how to define and inject these components into each other.

Value

A value in AngularJS is a simple object. It can be a number, string or JavaScript object. Values are typically used as configuration which is injected into factories, services or controllers.

A value has to belong to an AngularJS module. Here are three examples that add values to an AngularJS module:

var myModule = angular.module("myModule", []);

myModule.value("numberValue", 999);

myModule.value("stringValue", "abc");

myModule.value("objectValue", { val1 : 123, val2 : "abc"} );

The values are defined using the value() function on the module. The first parameter is the name of the value, and the second parameter is the value itself. Factories, services and controllers can now reference these values by their name.

Injecting a Value

Injecting a value into an AngularJS controller function is done simply by adding a parameter with the same name as the value (the first parameter passed to the value() function when the value is defined). Here is an example:

var myModule = angular.module("myModule", []);

myModule.value("numberValue", 999);

myModule.controller("MyController", function($scope, numberValue) {

    console.log(numberValue);

});

Notice how the second parameter of the controller function has the same name as the value.

Factory

Factory is a function that creates values. When a service, controller etc. needs a value injected from a factory, the factory creates the value on demand. Once created, the value is reused for all services, controllers etc. which need it injected. Thus, a factory differs from a value in that it can use a factory function to create the object it returns. You can also inject values into a factory for use when creating the object. You cannot do that with a value.

Here is an example that defines a factory on a module, and a controller which gets the factory created value injected:

var myModule = angular.module("myModule", []);

myModule.factory("myFactory", function() {
    return "a value";
});


myModule.controller("MyController", function($scope, myFactory) {

    console.log(myFactory);

});

As you can see, it is very similar to defining and injecting a value object. Keep in mind that it is not the factory function that is injected, but the value produced by the factory function.

Injecting Values Into a Factory

You can inject a value into a factory. It works just like when a value is injected into a controller. Here is an example:

var myModule = angular.module("myModule", []);

myModule.value("numberValue", 999);

myModule.factory("myFactory", function(numberValue) {
    return "a value: " + numberValue;
});

In this example the injected value is used to create the object created by the factory function.

Service

A service in AngularJS is a singleton JavaScript object which contains a set of functions. The functions contain whatever logic is necessary for the service to carry out its work.

AngularJS services are created using the service() function on a module. Here is an example:

function MyService() {
    this.doIt = function() {
        console.log("done");
    }
}


var myModule = angular.module("myModule", []);

myModule.service("myService", MyService);

As you can see, services are defined somewhat differently than factories and values. First of all, the service is defined as a separate, named function. That is because services in AngularJS are created using the new keyword. Thus, AngularJS will do this internally:

var theService = new MyService();

Apart from defining services as functions with functions inside, you add them to and use them with AngularJS just like you would with a value or function. You inject a service into a controller like this:

function MyService() {
    this.doIt = function() {
        console.log("done");
    }
}


var myModule = angular.module("myModule", []);

myModule.service("myService", MyService);


myModule.controller("MyController", function($scope, myService) {

    myService.doIt();

});

Injecting Values Into a Service

You can inject values into a service, just like you can inject values into controllers, or services into controllers etc. Here is an example:

var myModule = angular.module("myModule", []);

myModule.value  ("myValue"  , "12345");

function MyService(myValue) {
    this.doIt = function() {
        console.log("done: " + myValue;
    }
}

myModule.service("myService", MyService);

Notice how the parameter to the MyService function is named the same as the value registered on the module. Thus, the value will be injected into the service when it is created.

Providers

Providers in AngularJS is the most flexible form of factory you can create. You register a provider with a module just like you do with a service or factory, except you use the provider() function instead. Here is an AngularJS provider example:

var myModule = angular.module("myModule", []);

myModule.provider("mySecondService", function() {
    var provider = {};

    provider.$get = function() {
        var service = {};

        service.doService = function() {
            console.log("mySecondService: Service Done!");
        }

        return service;
    }

    return provider;
});

As you can see, the provider() function takes 2 parameters. The first parameter is the name of the service / object which the provider creates. In this case the name is mySecondService. The second parameter is the function which creates the provider. Note: The provider is itself a factory, so at this time no actual service or object is created from the provider. Only the function creating the provider is defined.

When you look at the function creating the provider you can see that the provider is a JavaScript object.

The JavaScript provider object contains a single $get() function. This is the factory function of the provider. In other words, the $get() function creates whatever the provider creates (service, value etc.). In the example above, the provider creates a service object which contains a single service function (standard JavaScript function) called doService().

In order to get the product of a provider injected into a controller, do specify a dependency on the provider, just like you would with a service. What is injected into the controller is the product created by the provider, not the provider itself. Here is an AngularJS provider injection example:

myModule.controller("MyController", function($scope, mySecondService) {

    $scope.whenButtonClicked = function() {
        mySecondService.doIt();
    }

});

As you can see, the name of the provider is used as a parameter in the controller function. The object created by the provider's $get() function will be injected into this parameter.

Configuring a Provider

It is possible to configure a provider further via calls to its function during the configuration phase of a module. Here is an example:

var myModule = angular.module("myModule", []);

myModule.provider("mySecondService", function() {
    var provider = {};
    var config   = { configParam : "default" };

    provider.doConfig = function(configParam) {
        config.configParam = configParam;
    }

    provider.$get = function() {
        var service = {};

        service.doService = function() {
            console.log("mySecondService: " + config.configParam);
        }

        return service;
    }

    return provider;
});

myModule.config( function( mySecondServiceProvider ) {
    mySecondServiceProvider.doConfig("new config param");
});

myModule.controller("MyController", function($scope, mySecondService) {

    $scope.whenButtonClicked = function() {
        mySecondService.doIt();
    }

});

Notice how the provider object now has an extra function called doConfig(). This function can be used to set a configuration parameter on the provider.

Notice also the call to the myModule.config() function. The config function takes a function as parameter. This function can configure the module. The function passed to config() takes a single parameter named mySecondServiceProvider. That is the same name the provider is registered with plus Provider as suffix. The suffix tells AngularJS to inject the provider itself, and not the object created by the provider. Inside the function passed to the config() function the mySecondServiceProvider.doConfig() function is called, which sets the config parameter on the provider.

The controller defined later in the example just depends on the object created by the provider (not the provider itself). It does so by taking a parameter named mySecondService which is the name the provider of the service is registered with. As you can see, the service used from inside the $scope.whenButtonClicked() function.

Constants

In the previous section on providers you saw how to configure a provider via the module.config() function. Unfortunately you cannot inject values into the module.config() function. Instead you can inject constants.

Constants in AngularJS are defined using the module.constants() function. Here is an AngularJS constant example:

myModule.constant("configValue", "constant config value");

This constant can now be injected into the module.config() function like this:

myservices.config( function( mySecondServiceProvider, configValue ) {
    mySecondServiceProvider.doConfig(configValue);
});

As you can see, the parameter configValue matches the name of the constant which is also configValue. Thus the value of the constant will be injected into this parameter. The constant value is then passed as parameter to the doConfig() function on the mySecondServiceProvider provider.

Dependencies Between Modules

As you have seen, values, factories and services are added to an AngularJS module. It is possible for one module to use the values, factories and services of another module. In order to do so, a module needs to declare a dependency on the module which contains the values, factories and services it wants to use. Here is an example:

var myUtilModule = angular.module("myUtilModule", []);

myUtilModule.value  ("myValue"  , "12345");


var myOtherModule = angular.module("myOtherModule", ['myUtilModule']);

myOtherModule.controller("MyController", function($scope, myValue) {

});

Notice how the second module (myOtherModule) lists the name of the first module (myUtilModule) in the second parameter (inside the array) passed to the angular.module() function. This tells AngularJS that all values, factories and services defined inside the myUtilModule should be available inside the myOtherModule module too. In other words, myOtherModule depends on myUtilModule.

Second, notice how the MyController controller function now declares a parameter named myValue. This value will be provided from the value registered on the myUtilModule module.

Minification Safe Dependency Injection in AngularJS

When you minify JavaScript the JavaScript minifier replaces the names of local variables and parameters with shorter names. However, AngularJS uses the parameter names of controller functions, factories, services and providers to decide what to inject into their factory functions. If the names are changed, AngularJS cannot inject the correct objects.

To make your AngularJS code minification safe, you need to provide the names of the objects to inject as strings. You wrap these strings in an array together with the function that needs the values injected. Here is an AngularJS minification safe dependency injection example:

var myapp = angular.module("myapp", ['myservices']);

myapp.controller("AController", ['$scope', function(p1) {
    p1.myvar = "the value";
}]);

This example injects the $scope object into the p1 parameter of the controller function.

Notice how the controller function is registered. Instead of passing the controller function to the angular.controller function directly, an array is passed instead. This array contains the name of the value to inject into the controller function, as well as the controller function itself. The controller function is always the last value in this array. If you need to inject more than one value, the value names are listed in the beginning of the array and in the sequence they are to be injected into the function. Here is a minification safe multi value example:

var myapp = angular.module("myapp", ['myservices']);

myapp.controller("AController", ['$scope', '$http', function(p1, p2) {
    p1.myvar = "the value";
    p2.get("/myservice.json");
}]);

This example injects the $scope object into the p1 parameter, and the $http service into the p2 parameter of the controller function.

Now it no longer matters what the parameter names of the controller function are. AngularJS will use the strings in the beginning of the array to determine what to inject into the controller function.

The same mechanism can be used for factories, services and providers to provide minification safe dependency injection. Here is a minification safe factory, service and provider example:

var myutil = angular.module("myutil", []);


myutil.value("safeValue", "a safe value");


myutil.factory("safeFactory", ['safeValue', function(p1) {
    return { value : p1 };
}]);


function MySafeService(p1){
    this.doIt = function() {
        return "MySafeService.doIt() called: " + p1.value;
    }
}
myutil.service("safeService", ['safeFactory', MySafeService]);


myutil.provider("safeService2", function() {
    var provider = {};

    provider.$get = ['safeService', function(p1) {
        var service = {};

        service.doService = function() {
            console.log("safeService from provider: " + p1.doIt());
        }

        return service;
    }];

    return provider;
});

myapp.controller("AController", ['$scope', 'safeService2', function(p1, p2) {
    p1.myvar = "the value";
    p2.doService();
}]);

Notice especially the declaration of the provider. Notice how the dependencies are not specified on the provider factory function, but on the $get() function of the provider returned from inside the provider factory function. Actually, an array with names of dependencies and the function implementation is used instead of just a $get() function. Other than that, specifying dependencies works the same for providers as for factories, services and controller functions.

Jakob Jenkov




Copyright  Jenkov Aps
Close TOC