Внедрение зависимостей и сервис $injector

Внедрение зависимостей (Dependency Injection) представляет собой способ организации компонентов в приложении, при котором между компонентами отсутствуют жесткие связи: компоненты связаны посредством интерефейсов, а не конкретных реализаций. И в любое время мы можем динамически применить другую реализацию компонента.

В AngularJS ключевую роль во внедрении зависимостей играет сервис $injector. Его роль состоит в определении и установке зависимостей, которые используются функцией.

Свою функциональность сервис $injector реализует с помощью следующих методов:

Рассмотрим на небольшом примере применение сервиса $injector:

</head>
<body>
<div ng-controller="myController">
<button ng-click="buttonClick()">Не нажимать</button>
</div>
<script src="js/lib/angular.min.js"></script>
<script>
angular.module("myApp", []).controller("myController", function ($scope, $injector) {
    var counter = 0;
    var getData = function (dataService, message) {
        if (counter %2 == 0) {
            console.log(dataService.question);
        } 
        else {
            console.log(message);
        }
        counter++;
    }
    $scope.buttonClick = function () {
        var deps = $injector.annotate(getData);
        var args = [];
        for (var i = 0; i < deps.length; i++) {
            if ($injector.has(deps[i])) {
                args.push($injector.get(deps[i]));
                console.log("Сервис: " + deps[i]);
            } 
            else if (deps[i] == "message") {
                args.push("Привет мир");
                console.log("" + deps[i]);
            }
        }
        getData.apply(null, args);
    };
}).factory('dataService', function(){
    return{
        question:{
            text: 'Какой js-фреймворк лучше использовать?',
            author: 'Иван Иванов',
            date: '20/10/2013'
        }
    };
});
</script>
</body>
</html>

На странице определена кнопка, нажатие которой обрабатывается методом buttonClick(). Данный метод определен в контроллере MyController.

Чтобы задействовать сервис $injector, он передается в качестве параметра в функцию контроллер.

Кроме того, здесь определена функция getData, которая зависит от сервиса dataService, а также от аргумента message.

В методе buttonClick мы можем получить все используемые функцией getData зависимости с помощью выражения var deps = $injector.annotate(getData). После этого объект deps будет содержать набор используемых зависимостей, то есть dataService и message.

С помощью метода $injector.has(deps[i]) определяем, является ли зависимость зарегистрированным сервисом. Нам нет смысла как-то переопределять сервис dataService, а вот объект message мы можем определить какой угодно.

Затем с помощью метода $injector.get(deps[i]) получаем реализацию сервиса и добавляем в массив аргументов args

В конце вызываем метод с помощью стандартной функции javascript apply, передавая в нее полученные компоненты: getData.apply(null, args)

Теперь используем метод $injector.invoke(). Для этого изменим метод buttonClick():

$scope.buttonClick = function () {
    var locals = { message: "Привет Ир"};
    $injector.invoke(getData, null, locals);
};

Поскольку опять же нам нет смысла устанавливать зависимости для сервиса dataService, поэтому мы устанавливаем зависимости только для параметра message. Затем установленные параметры передаются в функцию $injector.invoke.