Spec-Zone.ru › Angular.js 1.7

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

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

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

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

Инъекция зависимостей широко используется в AngularJS. Вы можете использовать её при определении компонентов или при предоставлении run и config блоков для модуля.

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

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

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

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

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

См. Модули для получения дополнительной информации о 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 и поэтому получают доступ к scope. Другие компоненты (например, сервисы) имеют доступ только к сервису $rootScope.
  • резольверы: Если контроллер создан в рамках маршрута, любые значения, разрешенные в рамках маршрута, доступны для инъекции в контроллер.

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

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

  • Использование встроенной аннотации массива (предпочтительный вариант)
  • Использование аннотации свойства $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.

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

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

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

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

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 с неявной инъекцией:

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

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

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

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

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

Этот раздел мотивирует и объясняет использование DI в AngularJS. Как использовать 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.

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

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

Сначала создайте модуль AngularJS, который будет содержать определение сервиса. (Пустой массив, переданный во втором параметре, означает, что этот модуль не зависит от других модулей.)

// Create a module to hold the service definition
var myModule = angular.module('myModule', []);

Покажите инжектору, как создать сервис greeter, который представляет собой просто объект, содержащий метод greet. Обратите внимание, что greeter зависит от сервиса $window, который будет предоставлен (инжектирован в greeter) инжектором.

// Define the `greeter` service
myModule.factory('greeter', function($window) {
  return {
    greet: function(text) {
      $window.alert(text);
    }
  };
});

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

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

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

injector.instantiate(MyController);

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

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

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

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

Spec-Zone.ru

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