Spec-Zone.ru › Angular.js 1.5

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

Инъекция зависимостей (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.5.11/docs/guide/di

Spec-Zone.ru

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