Angular & Dependency Injection

DI & IoC

DI (dependency injection) enables IoC (Inversion of Control)

Inversion of control is a design paradigm with the goal of giving more control to the targeted components of your application, the ones getting the work done

Dependency injection is a software design pattern that allows the removal of hard-coded dependencies and makes it possible to change them, whether at run-time or compile-time.

In Other words...

In traditional programming, the flow of the business logic is determined by objects that are statically assigned to one another. 

With inversion of control, the flow depends on the object graph that is instantiated by the assembler and is made possible by object interactions being defined through abstractions. The binding process is achieved through dependency injection.

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

Angular DI

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

Angular Module Loading

Angular modules have the opportunity to configure themselves before the module actually bootstraps and starts to run.

Configuration Block:

  • This phase is the only part of the Angular flow that can modify things before the app starts up.
  • The only services that can be injected in this block are constants and providers;

Run Block:

  • Executed at begining of the application;
  • Similar with the main() method in other programming languages;
  • Any service can be injected here.

Angular Services

  • singleton objects that are instantiated only once per application;
  • lazy-loaded (created only when necessary);
  • provide a way to share data and behavior across controllers, directives, filters or other services;

How to create a service?

  • .constant();
  • .value();
  • .service();
  • .factory();
  • .provider();

Constant

  • used for registering a constant service such as string, number, array, object or function;
  • doesn't have the ability to use other services (have dependencies);
angular
    .module('myApp', [])
    .constant('apiUrl', 'http://localhost:8080')
    .config(['apiUrl', function(apiUrl){
        //apiUrl can be used here
    }])
    .run(['$rootScope', function($rootScope){
        //apiUrl can be used here
     }]);

Value

  • used for registering a value service such as string, number, array, object or function;
  • doesn't have the ability to use other services (have dependencies);
angular
    .module('myApp', [])
    .value('objectValue', {
        foo: 'bar'
    })
    .run(['$rootScope', 'objectValue',
        function($rootScope, objectValue){          
            $rootScope.foo = objectValue.foo;
        }
    ]);

Factory

  • used for registering a service factory which will be called to return the service instance;
  • ability to use other services (have dependencies);
  • service initialization;
  • delayed/lazy initialization.
angular
    .module('myApp', [])
    .factory('myFactory', function(){
        var data;  //private variable  

        return {
            fetchData: function(){
                //business to populate data
            },
            getData: function(){
                return data;
            }
        } 
    })
    .run(['$rootScope', 'myFactory',
        function($rootScope, myFactory){          
            myFactory.fetchData();
            $rootScope.data = myFactory.getData();
        }
    ]);

Service

  • used for registering a service constructor which will be invoked with new to create the service instance;
  • same as factory;
angular
    .module('myApp', [])
    .service('myService', function(){
        var data;  //private variable 
 
        this.fetchData= function(){
            //business to populate data
        };
        this.getData= function(){
            return data;
        };
    });
.factory('myService', function(){
    var Service = function(){
        var data;  //private variable  
        this.fetchData= function(){
            //business to populate data
        };
        this.getData= function(){
            return data;
        };
    };

    return new Service();
});

Provider

  • constructor function, whose instance is responsible for 'providing' a factory for a service;
  • can have additional methods that allow configuration of the provider or it's returning service;
  • must have a $get method :
    • that returns the factory service;
    • ability to use other services (have dependencies);
angular
    .module('myApp', [])
    .provider('myFactory', function(){
        var configVar = 'value';
        //The factory Service - can have any dependency
        this.$get = ['$http', function($http){
            var data;  //private variable 
            return{
                fetchData: function(){
                //business to populate data
                },
                getData: function(){
                    return data;
                }
            };
        }];
        //Config method
        this.config = function(config){
            configVar = config;
        };
    })
    .config(['myFactoryProvider', function(myFactoryProvider){
        myFactoryProvider.config('Overriden value');
    }])
    .run(['$rootScope', 'myFactory',
        function($rootScope, myFactory){          
            myFactory.fetchData();
            $rootScope.data = myFactory.getData()    
        }
    ]);

Angular internal services

  • Angular comes with several built-in services like:
    • $http / $httpProvider;
    • $compile / $compileProvider;
    • many more.
  • The $ is a convention to point that the service comes from the framework and it is not custom-made;

Summary

  • All the providers (services) are instantiated only once. That means that they are all singletons;
  • A constant is a value that can be injected everywhere. The value of a constant can never be changed;
  • A value is just a simple injectable value;
  • A service is an injectable constructor;
  • A factory is an injectable function;
  • A provider is a configurable factory.

Demo

Angular Routing

ngRoute

Layout Template

ngRoute module is a core Angular module for basic routing. The module provides the ng-view directive, in order to render the appropriate template.

Any time the route is changed the directive will update it's content:

  • if there is a template associated with the current route:
    • link the controller (if specified) with the scope;
    • link the new scope with the new template;
    • remove the last view and clean the last scope;
    • create a new scope - inherited from the parent;

Routes

  • To create routes on a specific module or app, ngRoute module exposes the $routeProvider.
  • To add a specific route, $routeProvider has the when() method.
$routeProvider
    .when('path', {
        template: '',
        templateUrl: '',
        controller: '',
        controllerAs: ''
        //... 
    })
    .otherwise(routeConfigObj);

Example

<html ng-app="myApp">
<head>...</head>
<body>
    <header>
        <h1>My app</h1>
        <ul>
          <li><a href="#/">Home</a></li>
          <li><a href="#/about">About</a></li>
        </ul>
    </header>

    <div class="content">
        <div ng-view></div>
    </div>
</body>
</html>
angular
.module('myApp', ['ngRoute'])
.config(['$routeProvider', function($routeProvider){
    $routeProvider
        .when('/', {
            template: '<h2>{{page}}</h2>',
            controller: ['$scope', function($scope){
                $scope.page = 'home';
            }]
        })
        .when('/about', {
            template: '<h2>{{page}}</h2>',
            controller: ['$scope', function($scope){
                $scope.page = 'about';
            }]
        })
        .otherwise({redirectTo: '/'});
}]);

Demo

Dependency Injection pattern in Angular

By Alexe Bogdan

Dependency Injection pattern in Angular

Inversion of control paradigm and dependency injection pattern in Angular

  • 1,396