Улучшить эту документациюКомпоненты
В AngularJS, компонент — это особый вид директивы, использующий более простую конфигурацию, подходящую для архитектуры приложения, основанной на компонентах.
Это упрощает написание приложения, аналогично использованию веб-компонентов или новой архитектуры приложений Angular.
Преимущества компонентов:
- более простая конфигурация по сравнению с обычными директивами
- поощряет разумные значения по умолчанию и лучшие практики
- оптимизирован для архитектуры, основанной на компонентах
- написание директив компонентов упростит модернизацию до Angular
Когда не следует использовать компоненты:
- для директив, которым требуется выполнение действий в функциях компиляции и предварительной привязки, так как они недоступны
- когда необходимы расширенные параметры определения директивы, такие как приоритет, терминальность, многоэлементность
- когда требуется директива, которая срабатывает по атрибуту или классу CSS, а не элементу
Создание и настройка компонента
Компоненты можно регистрировать, используя метод .component() модуля AngularJS (возвращаемого angular.module()). Метод принимает два аргумента:
- Имя компонента (как строка).
- Объект конфигурации компонента. (Обратите внимание, что, в отличие от метода
.directive(), этот метод не принимает функцию фабрики.)
Также возможно добавление компонентов через $compileProvider в фазе конфигурации модуля.
Сравнение определения директивы и определения компонента
| Директива | Компонент | |
|---|---|---|
| связывания | Нет | Да (связывает с контроллером) |
| bindToController | Да (по умолчанию: false) | Нет (используйте связывания вместо этого) |
| функция компиляции | Да | Нет |
| контроллер | Да | Да (по умолчанию function() {}) |
| controllerAs | Да (по умолчанию: false) | Да (по умолчанию: $ctrl) |
| функции привязки | Да | Нет |
| multiElement | Да | Нет |
| приоритет | Да | Нет |
| replace | Да (устарело) | Нет |
| require | Да | Да |
| restrict | Да | Нет (ограничено только элементами) |
| scope | Да (по умолчанию: false) | Нет (объем всегда изолированный) |
| шаблон | Да | Да, инъектируемый |
| templateNamespace | Да | Нет |
| templateUrl | Да | Да, инъектируемый |
| terminal | Да | Нет |
| transclude | Да (по умолчанию: false) | Да (по умолчанию: false) |
Архитектура приложения, основанная на компонентах
Как уже упоминалось, помощник по компонентам упрощает структурирование вашего приложения с помощью архитектуры, основанной на компонентах. Но что делает компонент отличным от возможностей помощника по компонентам?
-
Компоненты управляют только своим собственным представлением и данными: Компоненты никогда не должны изменять данные или DOM, которые находятся вне их собственного объема. Обычно в AngularJS можно изменять данные в любом месте приложения с помощью наследования области видимости и наблюдения. Это практично, но также может привести к проблемам, когда не ясно, какая часть приложения отвечает за изменение данных. Вот почему директивы компонентов используют изолированную область видимости, так как целая категория манипуляций с областью видимости невозможна.
-
Компоненты имеют хорошо определенный публичный API — вводы и выводы: Однако изоляция области видимости ограничена, потому что AngularJS использует двустороннюю привязку. Таким образом, если вы передаете объект компоненту следующим образом —
bindings: {item: '='}, и изменяете одно из его свойств, изменение будет отражено в родительском компоненте. Однако для компонентов только компонент, который владеет данными, должен их изменять, чтобы было легко понять, какие данные изменяются и когда. По этой причине компоненты должны следовать нескольким простым соглашениям:- Вводы должны использовать
<и@связывания. Символ<обозначает односторонние связывания, которые доступны начиная с версии 1.5. Разница с=заключается в том, что привязанные свойства в области видимости компонента не отслеживаются, что означает, что если вы присвоите новое значение свойству в области видимости компонента, оно не будет обновлять родительскую область видимости. Тем не менее, родительская и компонентная область видимости ссылаются на один и тот же объект, поэтому, если вы изменяете свойства объекта или элементы массива в области видимости компонента, родительский объект все равно будет отражать эти изменения. Общее правило, следовательно, состоит в том, чтобы никогда не изменять свойство объекта или элемента массива в области видимости компонента.@связывания могут быть использованы, когда ввод — это строка, особенно когда значение связывания не изменяется.bindings: { hero: '<', comment: '@' } - Выходы реализованы с помощью
&связываний, которые работают как обратные вызовы событий компонента.bindings: { onDelete: '&', onUpdate: '&' } - Вместо манипулирования входными данными компонент вызывает соответствующее выходное событие с измененными данными. В случае удаления это означает, что компонент не удаляет сам
hero, а отправляет его обратно в компонент-владелец через соответствующее событие.<!-- note that we use kebab-case for bindings in the template as usual --> <editable-field on-update="$ctrl.update('location', value)"></editable-field><br> <button ng-click="$ctrl.onDelete({hero: $ctrl.hero})">Delete</button> - Таким образом, родительский компонент может решить, что делать с событием (например, удалить элемент или обновить свойства)
ctrl.deleteHero(hero) { $http.delete(...).then(function() { var idx = ctrl.list.indexOf(hero); if (idx >= 0) { ctrl.list.splice(idx, 1); } }); }
- Вводы должны использовать
-
Компоненты имеют хорошо определенный жизненный цикл Каждый компонент может реализовывать "методы жизненного цикла". Это методы, которые будут вызываться в определенные моменты жизни компонента. Можно реализовать следующие методы:
-
$onInit()— Вызывается для каждого контроллера после создания всех контроллеров на элементе и инициализации их связей (и до функций предварительной и последующей привязки для директив на этом элементе). Это хорошее место для размещения кода инициализации вашего контроллера. -
$onChanges(changesObj)— Вызывается всякий раз, когда односторонние привязки обновляются.changesObj— это хеш, ключи которого — имена измененных привязанных свойств, а значения — объект типа{ currentValue, previousValue, isFirstChange() }. Используйте этот метод для запуска обновлений внутри компонента, например, для клонирования привязанного значения, чтобы предотвратить случайное изменение внешнего значения. -
$doCheck()— Вызывается на каждой итерации цикла переваривания. Предоставляет возможность обнаруживать и реагировать на изменения. Любые действия, которые вы хотите выполнить в ответ на обнаруженные изменения, должны вызываться из этого метода; реализация этого не влияет на время вызова$onChanges. Например, этот метод может быть полезен, если вы хотите выполнить проверку глубокого равенства или проверить объект Date, изменения в котором не будут обнаружены детектором изменений AngularJS, а значит, не вызовут$onChanges. Этот метод вызывается без аргументов; при обнаружении изменений необходимо сохранить предыдущее(ые) значение(я) для сравнения с текущими значениями. -
$onDestroy()— Вызывается для контроллера, когда содержащая область видимости уничтожается. Используйте этот метод для освобождения внешних ресурсов, наблюдений и обработчиков событий. -
$postLink()— Вызывается после того, как элемент этого контроллера и его дочерние элементы были связаны. Подобно функции post-link, этот метод можно использовать для настройки обработчиков событий DOM и выполнения непосредственной манипуляции DOM. Обратите внимание, что дочерние элементы, содержащие директивыtemplateUrl, не будут скомпилированы и связаны, так как они ждут асинхронной загрузки своего шаблона, и их собственная компиляция и привязка приостановлены до тех пор, пока это не произойдет. Этот метод можно рассматривать как аналогичный методамngAfterViewInitиngAfterContentInitв Angular. Поскольку процесс компиляции в AngularJS довольно отличается, нет прямой сопоставления, и при модернизации следует проявлять осторожность.
-
Реализовав эти методы, ваш компонент может присоединиться к своему жизненному циклу.
- Приложение — это дерево компонентов: В идеале все приложение должно представлять собой дерево компонентов, которые реализуют четко определенные входы и выходы и минимизируют двустороннюю привязку данных. Таким образом, легче предсказать, когда данные меняются и какое состояние имеет компонент.
Пример дерева компонентов
Следующий пример расширяет простой пример компонента и включает концепции, которые мы представили выше:
Вместо ngController у нас теперь есть компонент heroList, который хранит данные разных героев и создает heroDetail для каждого из них.
Компонент heroDetail теперь содержит новую функциональность:
- кнопка удаления, которая вызывает связанную функцию
onDeleteкомпонента heroList - поле ввода для изменения местоположения героя в виде повторно используемого компонента editableField. Вместо манипулирования самим объектом героя, он отправляет набор изменений вверх в heroDetail, который отправляет его вверх в компонент heroList, который обновляет исходные данные.
Компоненты как шаблоны маршрутов
Компоненты также полезны в качестве шаблонов маршрутов (например, при использовании ngRoute). В приложении, основанном на компонентах, каждый вид является компонентом:
var myMod = angular.module('myMod', ['ngRoute']);
myMod.component('home', {
template: '<h1>Home</h1><p>Hello, {{ $ctrl.user.name }} !</p>',
controller: function() {
this.user = {name: 'world'};
}
});
myMod.config(function($routeProvider) {
$routeProvider.when('/', {
template: '<home></home>'
});
});
При использовании $routeProvider, вы часто можете избежать некоторых шаблонных кодов, передав разрешенные зависимости маршрута непосредственно компоненту. Начиная с версии 1.5, ngRoute автоматически назначает разрешения свойству маршрута $resolve (вы также можете настроить имя свойства через resolveAs). Используя компоненты, вы можете воспользоваться этим и передать разрешения непосредственно в свой компонент без создания дополнительного контроллера маршрута:
var myMod = angular.module('myMod', ['ngRoute']);
myMod.component('home', {
template: '<h1>Home</h1><p>Hello, {{ $ctrl.user.name }} !</p>',
bindings: {
user: '<'
}
});
myMod.config(function($routeProvider) {
$routeProvider.when('/', {
template: '<home user="$resolve.user"></home>',
resolve: {
user: function($http) { return $http.get('...'); }
}
});
});
Взаимодействие между компонентами
Директивы могут требовать контроллеров других директив для обеспечения взаимодействия между ними. Это можно реализовать в компоненте, предоставив объект отображения для свойства require . Ключи объекта указывают имена свойств, под которыми требуемые контроллеры (значения объектов) будут привязаны к контроллеру запрашивающего компонента.
$onInit! Вот пример раздела вкладок, построенный из компонентов:
Тестирование компонентов в едином модуле
Самый простой способ провести модульное тестирование контроллера компонента — использовать $componentController, который включён в ngMock. Преимущество этого метода в том, что вам не нужно создавать какие-либо DOM-элементы. Следующий пример демонстрирует, как это сделать для компонента heroDetail из вышеприведённого примера.
В примерах используется фреймворк для тестирования Jasmine.
Тест контроллера:
describe('HeroDetailController', function() {
var $componentController;
beforeEach(module('heroApp'));
beforeEach(inject(function(_$componentController_) {
$componentController = _$componentController_;
}));
it('should call the `onDelete` binding, when deleting the hero', function() {
var onDeleteSpy = jasmine.createSpy('onDelete');
var bindings = {hero: {}, onDelete: onDeleteSpy};
var ctrl = $componentController('heroDetail', null, bindings);
ctrl.delete();
expect(onDeleteSpy).toHaveBeenCalledWith({hero: ctrl.hero});
});
it('should call the `onUpdate` binding, when updating a property', function() {
var onUpdateSpy = jasmine.createSpy('onUpdate');
var bindings = {hero: {}, onUpdate: onUpdateSpy};
var ctrl = $componentController('heroDetail', null, bindings);
ctrl.update('foo', 'bar');
expect(onUpdateSpy).toHaveBeenCalledWith({
hero: ctrl.hero,
prop: 'foo',
value: 'bar'
});
});
});
© 2010–2018 Google, Inc.
Licensed under the Creative Commons Attribution License 4.0.
https://code.angularjs.org/1.6.9/docs/guide/component