Внедрение зависимостей (Dependency Injection) представляет собой способ организации компонентов в приложении, при котором между компонентами отсутствуют жесткие связи: компоненты связаны посредством интерефейсов, а не конкретных реализаций. И в любое время мы можем динамически применить другую реализацию компонента.
В AngularJS ключевую роль во внедрении зависимостей играет сервис $injector
. Его роль состоит в определении и установке зависимостей, которые используются функцией.
Свою функциональность сервис $injector реализует с помощью следующих методов:
annotate(fn): возвращает набор зависимостей для функции fn
get(name): возвращает контретную реализацию сервиса по определенному названию сервиса
has(name): возвращает true
, если для указанного объекта name установлена зависимость
invoke(fn, self, locals): вызывает функцию fn. Может принимать два необязательных параметра: self
(позволяет установить
аргумент this
для вызываемой функции) и locals
(представляет альтернативный способ передачи аргументов в вызываемую функцию)
Рассмотрим на небольшом примере применение сервиса $injector:
<!DOCTYPE html> <html ng-app="myApp"> <head> <title>Dependency Injection</title> <meta charset="utf-8" /> <link href="css/mystyles.css" rel="stylesheet" /> </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.