Dependency Injection

Dependency Injection (DI) is a software design pattern that deals with how components get hold of their dependencies.

The AngularJS injector subsystem is in charge of creating components, resolving their dependencies, and providing them to other components as requested.

Using Dependency Injection

Dependency Injection is pervasive throughout AngularJS. You can use it when defining components or when providing run and config blocks for a module.

  • Services, directives, filters, and animations are defined by an injectable factory method or constructor function, and can be injected with “services”, “values”, and “constants” as dependencies.
  • Controllers are defined by a constructor function, which can be injected with any of the “service” and “value” as dependencies, but they can also be provided with “special dependencies”. See Controllers below for a list of these special dependencies.
  • The run method accepts a function, which can be injected with “services”, “values” and, “constants” as dependencies. Note that you cannot inject “providers” into run blocks.
  • The config method accepts a function, which can be injected with “providers” and “constants” as dependencies. Note that you cannot inject “services” or “values” into configuration.
  • The provider method can only be injected with other “providers”. However, only those that have been registered beforehand can be injected. This is different from services, where the order of registration does not matter.

Factory Methods

The way you define a directive, service, or filter is with a factory function. The factory methods are registered with modules. The recommended way of declaring factories is:

angular.module(‘myModule’, [])

.factory(‘serviceId’, [‘depService’, function(depService) {

// …

}])

.directive(‘directiveName’, [‘depService’, function(depService) {

// …

}])

.filter(‘filterName’, [‘depService’, function(depService) {

// …

}]);

Module Methods

We can specify functions to run at configuration and run time for a module by calling the config and run methods. These functions are injectable with dependencies just like the factory functions above.

angular.module(‘myModule’, [])

.config([‘depProvider’, function(depProvider) {

// …

}])

.run([‘depService’, function(depService) {

// …

}]);

Controllers

Controllers are “classes” or “constructor functions” that are responsible for providing the application behavior that supports the declarative markup in the template. The recommended way of declaring Controllers is using the array notation:

someModule.controller(‘MyController’, [‘$scope’, ‘dep1’, ‘dep2’, function($scope, dep1, dep2) {

$scope.aMethod = function() {

}

}]);

Unlike services, there can be many instances of the same type of controller in an application.

Moreover, additional dependencies are made available to Controllers:

  • $scope: Controllers are associated with an element in the DOM and so are provided with access to the scope. Other components (like services) only have access to the $rootScope service.
  • resolves: If a controller is instantiated as part of a route, then any values that are resolved as part of the route are made available for injection into the controller.

Dependency Annotation

AngularJS invokes certain functions (like service factories and controllers) via the injector. You need to annotate these functions so that the injector knows what services to inject into the function. There are three ways of annotating your code with service name information:

  • Using the inline array annotation (preferred)
  • Using the $inject property annotation
  • Implicitly from the function parameter names (has caveats)

Inline Array Annotation

This is the preferred way to annotate application components. This is how the examples in the documentation are written.

For example:

someModule.controller(‘MyController’, [‘$scope’, ‘greeter’, function($scope, greeter) {

// …

}]);

Here we pass an array whose elements consist of a list of strings (the names of the dependencies) followed by the function itself.

When using this type of annotation, take care to keep the annotation array in sync with the parameters in the function declaration.

$inject Property Annotation

To allow the minifiers to rename the function parameters and still be able to inject the right services, the function needs to be annotated with the $inject property. The $inject property is an array of service names to inject.

var MyController = function($scope, greeter) {

// …

}

MyController.$inject = [‘$scope’, ‘greeter’];

someModule.controller(‘MyController’, MyController);

In this scenario the ordering of the values in the $inject array must match the ordering of the parameters in MyController.

Just like with the array annotation, you’ll need to take care to keep the $inject in sync with the parameters in the function declaration.

Why Dependency Injection?

There are only three ways a component (object or function) can get a hold of its dependencies:

  • The component can create the dependency, typically using the new operator.
  • The component can look up the dependency, by referring to a global variable.
  • The component can have the dependency passed to it where it is needed.

The first two options of creating or looking up dependencies are not optimal because they hard code the dependency to the component. This makes it difficult, if not impossible, to modify the dependencies. This is especially problematic in tests, where it is often desirable to provide mock dependencies for test isolation.

The third option is the most viable, since it removes the responsibility of locating the dependency from the component. The dependency is simply handed to the component.

Share this post
[social_warfare]
Services in AngularJS
Built-in Services

Get industry recognized certification – Contact us

keyboard_arrow_up