Spec-Zone.ru › Angular.js 1.3

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

Инъекция зависимостей (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 в необязательном параметре config:

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);

Это всё делается за кулисами. Обратите внимание, что запрашивая инжектор создать экземпляр класса, он может удовлетворить все зависимости MyController, не зная об инжекторе.

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

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

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

Spec-Zone.ru

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