Spec-Zone.ru › Angular.js 1.4

Улучшить эту документациюИнъекция зависимостей

Инъекция зависимостей (DI) — это шаблон проектирования программного обеспечения, который описывает, как компоненты получают свои зависимости.

Подсистема инжектора Angular отвечает за создание компонентов, разрешение их зависимостей и предоставление их другим компонентам по запросу.

Использование инъекции зависимостей

DI широко используется в Angular. Вы можете использовать его при определении компонентов или при предоставлении run и config блоков для модуля.

  • Компоненты, такие как сервисы, директивы, фильтры и анимации, определяются инжектируемым фабричным методом или конструкторной функцией. Эти компоненты могут быть инжектированы компонентами «сервис» и «значение» в качестве зависимостей.

  • Контроллеры определяются конструкторной функцией, которая может быть инжектирована любыми компонентами «сервис» и «значение» в качестве зависимостей, но они также могут быть обеспечены специальными зависимостями. См. Контроллеры ниже для списка этих специальных зависимостей.

  • Метод run принимает функцию, которая может быть инжектирована компонентами «сервис», «значение» и «константа» в качестве зависимостей. Обратите внимание, что вы не можете инжектировать «провайдеры» в run блоки.

  • Метод config принимает функцию, которая может быть инжектирована компонентами «провайдер» и «константа» в качестве зависимостей. Обратите внимание, что вы не можете инжектировать компоненты «сервис» или «значение» в конфигурацию.

См. Модули для получения дополнительной информации о run и config блоках.

Фабричные методы

Способ определения директивы, сервиса или фильтра — это фабричная функция. Фабричные методы регистрируются в модулях. Рекомендуемый способ объявления фабрик:

angular.module('myModule', [])
.factory('serviceId', ['depService', function(depService) {
  // ...
}])
.directive('directiveName', ['depService', function(depService) {
  // ...
}])
.filter('filterName', ['depService', function(depService) {
  // ...
}]);

Методы модуля

Мы можем указать функции для выполнения на этапе конфигурации и выполнения для модуля, вызвав методы config и run. Эти функции инжектируются зависимостями, как и фабричные функции выше.

angular.module('myModule', [])
.config(['depProvider', function(depProvider) {
  // ...
}])
.run(['depService', function(depService) {
  // ...
}]);

Контроллеры

Контроллеры — это «классы» или «конструкторные функции», которые отвечают за предоставление поведения приложения, которое поддерживает декларативную разметку в шаблоне. Рекомендуемый способ объявления контроллеров — с использованием нотации массива:

someModule.controller('MyController', ['$scope', 'dep1', 'dep2', function($scope, dep1, dep2) {
  ...
  $scope.aMethod = function() {
    ...
  }
  ...
}]);

В отличие от сервисов, в приложении может быть много экземпляров одного типа контроллера.

Кроме того, контроллерам доступны дополнительные зависимости:

  • $scope: Контроллеры связаны с элементом в DOM и поэтому имеют доступ к области видимости. Другие компоненты (например, сервисы) имеют доступ только к сервису $rootScope.
  • resolves: Если контроллер создается как часть маршрута, то любые значения, разрешенные в рамках маршрута, становятся доступны для инъекции в контроллер.

Аннотация зависимостей

Angular вызывает определённые функции (например, фабрики сервисов и контроллеры) через инжектор. Вам необходимо аннотировать эти функции, чтобы инжектор знал, какие сервисы вводить в функцию. Существует три способа аннотирования вашего кода информацией об именах сервисов:

  • Использование аннотации массива в строке (предпочтительно)
  • Использование аннотации свойства $inject
  • Неявное использование имён параметров функции (имеет особенности)

Аннотация массива в строке

Это предпочтительный способ аннотировать компоненты приложения. Вот как написаны примеры в документации.

Например:

someModule.controller('MyController', ['$scope', 'greeter', function($scope, greeter) {
  // ...
}]);

Здесь мы передаём массив, элементы которого состоят из списка строк (имён зависимостей) и самой функции.

При использовании такого типа аннотации следует следить за синхронизацией массива аннотаций с параметрами в объявлении функции.

$inject Свойство аннотации

Чтобы позволить минификаторам переименовывать параметры функций и при этом иметь возможность инжектировать правильные сервисы, функция должна быть аннотирована свойством $inject. Свойство $inject — это массив имён сервисов для инъекции.

var MyController = function($scope, greeter) {
  // ...
}
MyController.$inject = ['$scope', 'greeter'];
someModule.controller('MyController', MyController);

В этом случае порядок значений в массиве $inject должен соответствовать порядку параметров в MyController.

Так же, как и с аннотацией массива, вам нужно следить за синхронизацией $inject с параметрами в объявлении функции.

Неявная аннотация

Внимание: Если вы планируете минифицировать свой код, имена ваших сервисов будут переименованы и сломают ваше приложение.

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

someModule.controller('MyController', function($scope, greeter) {
  // ...
});

Учитывая функцию, инжектор может вывести имена сервисов для инъекции, проанализировав объявление функции и извлекая имена параметров. В приведённом выше примере $scope и greeter — это два сервиса, которые необходимо инжектировать в функцию.

Одно преимущество этого подхода заключается в том, что нет массива имён для синхронизации с параметрами функции. Вы также можете свободно изменять порядок зависимостей.

Однако этот метод не будет работать с JavaScript-минификаторами/обфускаторами из-за того, как они переименовывают параметры.

Инструменты, такие как ng-annotate, позволяют использовать неявные аннотации зависимостей в вашем приложении и автоматически добавлять аннотации массива в строке перед минификацией. Если вы решили использовать этот подход, вам, вероятно, захочется использовать ng-strict-di.

Из-за этих особенностей мы рекомендуем избегать этого стиля аннотации.

Использование жёсткой инъекции зависимостей

Вы можете добавить директиву ng-strict-di к тому же элементу, что и ng-app, чтобы включить жёсткий режим DI:

<!doctype html>
<html ng-app="myApp" ng-strict-di>
<body>
  I can add: {{ 1 + 2 }}.
  <script src="angular.js"></script>
</body>
</html>

Строгий режим генерирует ошибку всякий раз, когда сервис пытается использовать неявные аннотации.

Рассмотрим этот модуль, который включает сервис willBreak использующий неявную DI:

angular.module('myApp', [])
.factory('willBreak', function($rootScope) {
  // $rootScope is implicitly injected
})
.run(['willBreak', function(willBreak) {
  // Angular will throw when this runs
}]);

При создании сервиса willBreak Angular выведет ошибку из-за строгого режима. Это полезно при использовании инструмента, такого как ng-annotate, для обеспечения того, что все компоненты вашего приложения имеют аннотации.

Если вы используете ручную загрузку, вы также можете использовать жёсткую DI, предоставив strictDi: true в необязательном аргументе конфигурации:

angular.bootstrap(document, ['myApp'], {
  strictDi: true
});

Зачем нужна инъекция зависимостей?

Этот раздел мотивирует и объясняет использование DI в Angular. О том, как использовать DI, см. выше.

Для подробного обсуждения DI см. Инъекцию зависимостей на Википедии, Обратный контроль Мартина Фаулера или прочитайте о DI в вашей любимой книге по шаблонам проектирования.

Существует только три способа, которыми компонент (объект или функция) может получить свои зависимости:

  1. Компонент может создать зависимость, обычно используя оператор new.
  2. Компонент может найти зависимость, обратившись к глобальной переменной.
  3. Компонент может получить зависимость в нужном месте.

Первые два варианта создания или поиска зависимостей не являются оптимальными, так как они жестко связывают зависимость с компонентом. Это затрудняет, если не делает невозможным, изменение зависимостей. Это особенно проблематично в тестах, где часто желательно предоставить имитационные зависимости для изоляции тестов.

Третий вариант является наиболее жизнеспособным, так как он снимает с компонента ответственность за поиск зависимости. Зависимость просто передаётся компоненту.

function SomeClass(greeter) {
  this.greeter = greeter;
}

SomeClass.prototype.doSomething = function(name) {
  this.greeter.greet(name);
}

В приведённом выше примере SomeClass не занимается созданием или поиском зависимости greeter, ему просто передаётся greeter при его создании.

Это желательно, но возлагает ответственность за получение зависимости на код, который создаёт SomeClass.

Для управления ответственностью за создание зависимостей каждое приложение Angular имеет инжектор. Инжектор — это локатор сервисов, отвечающий за создание и поиск зависимостей.

Вот пример использования сервиса инжектора:

// Provide the wiring information in a module
var myModule = angular.module('myModule', []);

Укажите инжектору, как создать сервис greeter. Обратите внимание, что greeter зависит от сервиса $window. Сервис greeter — это объект, содержащий метод greet.

myModule.factory('greeter', function($window) {
  return {
    greet: function(text) {
      $window.alert(text);
    }
  };
});

Создайте новый инжектор, который может предоставлять компоненты, определённые в нашем модуле myModule, и запросите наш сервис greeter у инжектора. (Это обычно выполняется автоматически загрузчиком angular).

var injector = angular.injector(['ng', 'myModule']);
var greeter = injector.get('greeter');

Запрос зависимостей решает проблему жёсткой связи, но также означает, что инжектор должен передаваться по всему приложению. Передача инжектора нарушает Закон Деметера. Для решения этой проблемы мы используем декларативную нотацию в наших HTML-шаблонах, чтобы передать ответственность за создание компонентов инжектору, как в этом примере:

<div ng-controller="MyController">
  <button ng-click="sayHello()">Hello</button>
</div>
function MyController($scope, greeter) {
  $scope.sayHello = function() {
    greeter.greet('Hello World');
  };
}

При компиляции HTML Angular обрабатывает директиву ng-controller, которая, в свою очередь, запрашивает у инжектора создание экземпляра контроллера и его зависимостей.

injector.instantiate(MyController);

Всё это происходит в фоновом режиме. Обратите внимание, что, попросив ng-controller запросить у инжектора создание класса, он может удовлетворить все зависимости MyController без того, чтобы контроллер когда-либо знал об инжекторе.

Это наилучший результат. Код приложения просто объявляет необходимые зависимости, не взаимодействуя с инжектором. Эта настройка не нарушает Закон Деметера.

Примечание: Angular использует инъекцию конструктора.

© 2010–2017 Google, Inc.
Licensed under the Creative Commons Attribution License 4.0.
https://code.angularjs.org/1.4.14/docs/guide/di

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API