Angular директивы - когда и как использовать компиляцию, контроллер, предварительную ссылку и пост-ссылку
При написании директивы Angular можно использовать любую из следующих функций для управления поведением DOM, содержимым и внешним видом элемента, на котором объявлена директива:
- компиляции
- контроллер
- предварительно ссылка
- пост-ссылка
Кажется, есть некоторая путаница в отношении того, какую функцию следует использовать. Этот вопрос охватывает:
Основы директив
Характер функции, do и dont
Похожие вопросы:
Ответы
Ответ 1
В каком порядке выполняются директивные функции?
Для одной директивы
На основе следующего plunk рассмотрите следующую разметку HTML:
<body>
<div log='some-div'></div>
</body>
Со следующим объявлением директивы:
myApp.directive('log', function() {
return {
controller: function( $scope, $element, $attrs, $transclude ) {
console.log( $attrs.log + ' (controller)' );
},
compile: function compile( tElement, tAttributes ) {
console.log( tAttributes.log + ' (compile)' );
return {
pre: function preLink( scope, element, attributes ) {
console.log( attributes.log + ' (pre-link)' );
},
post: function postLink( scope, element, attributes ) {
console.log( attributes.log + ' (post-link)' );
}
};
}
};
});
Выход консоли будет:
some-div (compile)
some-div (controller)
some-div (pre-link)
some-div (post-link)
Мы видим, что сначала выполняется compile
, затем controller
, затем pre-link
и last <<26 > .
Для вложенных директив
Примечание.. Следующее не относится к директивам, которые отображают их дочерние элементы в их функции ссылок. Достаточно несколько директив Angular (например, ngIf, ngRepeat или любая директива с transclude
). Эти директивы изначально будут иметь функцию link
, вызываемую до вызова их дочерних директив compile
.
Оригинальная разметка HTML часто делается из вложенных элементов, каждая со своей собственной директивой. Как в следующей разметке (см. plunk):
<body>
<div log='parent'>
<div log='..first-child'></div>
<div log='..second-child'></div>
</div>
</body>
Выход консоли будет выглядеть так:
// The compile phase
parent (compile)
..first-child (compile)
..second-child (compile)
// The link phase
parent (controller)
parent (pre-link)
..first-child (controller)
..first-child (pre-link)
..first-child (post-link)
..second-child (controller)
..second-child (pre-link)
..second-child (post-link)
parent (post-link)
Здесь мы можем различать две фазы: фазу компиляции и фазу связи.
Фаза компиляции
При загрузке DOM Angular начинается фаза компиляции, где она пересекает разметку сверху вниз и вызывает compile
для всех директив. Графически мы могли бы выразить это так:
![An image illustrating the compilation loop for children]()
Возможно, важно отметить, что на этом этапе шаблоны, которые получает функция компиляции, являются исходными шаблонами (не шаблон экземпляра).
Фаза связи
экземпляры DOM часто являются просто результатом того, что исходный шаблон отображается в DOM, но они могут быть созданы с помощью ng-repeat
или введены "на лету".
Всякий раз, когда новый экземпляр элемента с директивой отображается в DOM, начинается фаза связи.
На этом этапе Angular вызывает controller
, pre-link
, итерирует дочерние элементы и вызывает post-link
для всех директив, например:
![An illustration demonstrating the link phase steps]()
Ответ 2
Что еще происходит между этими вызовами функций?
Различные директивные функции выполняются из двух других angular функций, называемых $compile
(где выполняется директива compile
) и внутренней функции с именем nodeLinkFn
(где директива controller
, preLink
и postLink
). В функции angular происходят различные события до и после вызова директивных функций. Возможно, наиболее важным является рекурсия ребенка. Следующая упрощенная иллюстрация показывает ключевые этапы на этапах компиляции и связи:
![An illustration showing Angular compile and link phases]()
Чтобы продемонстрировать эти шаги, используйте следующую разметку HTML:
<div ng-repeat="i in [0,1,2]">
<my-element>
<div>Inner content</div>
</my-element>
</div>
Со следующей директивой:
myApp.directive( 'myElement', function() {
return {
restrict: 'EA',
transclude: true,
template: '<div>{{label}}<div ng-transclude></div></div>'
}
});
Compile
API compile
выглядит так:
compile: function compile( tElement, tAttributes ) { ... }
Часто параметры имеют префикс t
, чтобы обозначать элементы и предоставленные атрибуты - это исходный шаблон, а не экземпляр экземпляра.
До вызова compile
переданного содержимого (если есть) удаляется, и шаблон применяется к разметке. Таким образом, элемент, предоставляемый функции compile
, будет выглядеть так:
<my-element>
<div>
"{{label}}"
<div ng-transclude></div>
</div>
</my-element>
Обратите внимание на то, что в данный момент переведенное содержимое не будет вставлено повторно.
После вызова директивы .compile
, angular будет перемещаться по всем дочерним элементам, включая те, которые, возможно, только что были введены директивой (например, элементы шаблона).
Создание экземпляра
В нашем случае будут созданы три экземпляра исходного шаблона выше (через ng-repeat
). Таким образом, следующая последовательность будет выполняться три раза, один раз для каждого экземпляра.
контроллер
API controller
включает в себя:
controller: function( $scope, $element, $attrs, $transclude ) { ... }
Ввод фазы ссылки, функция ссылки, возвращаемая через $compile
, теперь предоставляется с областью.
Во-первых, функция ссылки создает дочернюю область (scope: true
) или изолированную область (scope: {...}
), если требуется.
Затем запускается контроллер, снабженный областью действия элемента экземпляра.
Pre-ссылка
API pre-link
выглядит так:
function preLink( scope, element, attributes, controller ) { ... }
Практически ничего не происходит между вызовом директивы .controller
и .preLink
. angular все еще дают рекомендации относительно того, как каждый должен использоваться.
Следуя вызову .preLink
, функция link будет пересекать каждый дочерний элемент - вызывая правильную функцию ссылки и прикрепляя к ней текущую область действия (которая служит в качестве родительской области для дочерних элементов).
Пост-ссылка
API post-link
аналогичен API-функции pre-link
:
function postLink( scope, element, attributes, controller ) { ... }
Возможно, стоит заметить, что как только вызывается директива .postLink
, процесс ссылок всех его дочерних элементов завершен, включая все дочерние функции .postLink
.
Это означает, что к моменту появления .postLink
дети "живут" готовы. Это включает в себя:
- привязка данных
- применяется переход на
- область применения
Шаблон на этом этапе будет выглядеть так:
<my-element>
<div class="ng-binding">
"{{label}}"
<div ng-transclude>
<div class="ng-scope">Inner content</div>
</div>
</div>
</my-element>
Ответ 3
Как объявить различные функции?
Компиляция, контроль, предварительная ссылка и пост-ссылка
Если использовать все четыре функции, директива будет следовать этой форме:
myApp.directive( 'myDirective', function () {
return {
restrict: 'EA',
controller: function( $scope, $element, $attrs, $transclude ) {
// Controller code goes here.
},
compile: function compile( tElement, tAttributes, transcludeFn ) {
// Compile code goes here.
return {
pre: function preLink( scope, element, attributes, controller, transcludeFn ) {
// Pre-link code goes here
},
post: function postLink( scope, element, attributes, controller, transcludeFn ) {
// Post-link code goes here
}
};
}
};
});
Обратите внимание, что компиляция возвращает объект, содержащий как функции предварительной ссылки, так и пост-ссылки; в Angular lingo мы говорим, что функция компиляции возвращает функцию шаблона.
Компиляция, контроль и пост-связь
Если pre-link
не требуется, функция компиляции может просто вернуть функцию пост-ссылки вместо объекта определения, например:
myApp.directive( 'myDirective', function () {
return {
restrict: 'EA',
controller: function( $scope, $element, $attrs, $transclude ) {
// Controller code goes here.
},
compile: function compile( tElement, tAttributes, transcludeFn ) {
// Compile code goes here.
return function postLink( scope, element, attributes, controller, transcludeFn ) {
// Post-link code goes here
};
}
};
});
Иногда рекомендуется добавить метод compile
после определения метода (post) link
. Для этого можно использовать:
myApp.directive( 'myDirective', function () {
return {
restrict: 'EA',
controller: function( $scope, $element, $attrs, $transclude ) {
// Controller code goes here.
},
compile: function compile( tElement, tAttributes, transcludeFn ) {
// Compile code goes here.
return this.link;
},
link: function( scope, element, attributes, controller, transcludeFn ) {
// Post-link code goes here
}
};
});
Контроллер и пост-связь
Если функция компиляции не требуется, можно вообще пропустить ее объявление и предоставить функцию пост-ссылки под свойством link
объекта конфигурации директивы:
myApp.directive( 'myDirective', function () {
return {
restrict: 'EA',
controller: function( $scope, $element, $attrs, $transclude ) {
// Controller code goes here.
},
link: function postLink( scope, element, attributes, controller, transcludeFn ) {
// Post-link code goes here
},
};
});
Нет контроллера
В любом из приведенных выше примеров можно просто удалить функцию controller
, если это не требуется. Так, например, если нужна только функция post-link
, можно использовать:
myApp.directive( 'myDirective', function () {
return {
restrict: 'EA',
link: function postLink( scope, element, attributes, controller, transcludeFn ) {
// Post-link code goes here
},
};
});
Ответ 4
В чем разница между исходным шаблоном и шаблоном экземпляра?
Тот факт, что Angular позволяет манипулировать DOM, означает, что разметка ввода в процессе компиляции иногда отличается от вывода. В частности, некоторая вводная разметка может быть клонирована несколько раз (например, с ng-repeat
) перед тем, как быть переданной в DOM.
Angular терминология немного противоречива, но она по-прежнему различает два типа разметки:
- Исходный шаблон - разметка для клонирования, если это необходимо. Если клонировать, эта разметка не будет отображаться в DOM.
- Шаблон экземпляра - фактическая разметка, которая будет отображаться в DOM. Если задействовано клонирование, каждый экземпляр будет клонировать.
Следующая разметка демонстрирует это:
<div ng-repeat="i in [0,1,2]">
<my-directive>{{i}}</my-directive>
</div>
Источник html определяет
<my-directive>{{i}}</my-directive>
который служит исходным шаблоном.
Но поскольку он заключен в директиву ng-repeat
, этот шаблон-источник будет клонирован (3 раза в нашем случае). Эти клоны - это шаблон экземпляра, каждый из которых будет отображаться в DOM и связан с соответствующей областью.
Ответ 5
Функция компиляции
Каждая директива compile
функция вызывается только один раз, когда Angular загрузочные файлы.
Официально это место для выполнения (исходных) манипуляций с шаблонами, которые не связаны с областью или привязкой данных.
В первую очередь это делается для целей оптимизации; рассмотрите следующую разметку:
<tr ng-repeat="raw in raws">
<my-raw></my-raw>
</tr>
Директива <my-raw>
будет отображать определенный набор разметки DOM. Таким образом, мы можем:
- Разрешить
ng-repeat
дублировать исходный шаблон (<my-raw>
), а затем модифицировать разметку каждого шаблона экземпляра (вне функции compile
).
- Измените исходный шаблон, чтобы включить требуемую разметку (в функции
compile
), а затем разрешите ng-repeat
дублировать ее.
Если в коллекции raws
есть 1000 элементов, последний вариант может быть быстрее первого.
Do
- Манипулируйте разметку, чтобы она служила шаблоном для экземпляров (клонов).
Не
- Прикрепить обработчики событий.
- Осмотреть дочерние элементы.
- Настроить наблюдения за атрибутами.
- Настройка часов в области.
Ответ 6
Функция пост-ссылки
Когда вызывается функция post-link
, все предыдущие шаги были выполнены - привязка, переключение и т.д.
Это, как правило, место для дальнейшего манипулирования обработанной DOM.
Do
- Манипулировать DOM (визуализированные и, таким образом, созданные экземпляры) элементы.
- Прикрепить обработчики событий.
- Осмотреть дочерние элементы.
- Настроить наблюдения за атрибутами.
- Настройка часов в области.
Ответ 7
Функция контроллера
Каждая директива controller
функция вызывается всякий раз, когда создается новый связанный элемент.
Официально, функция controller
находится там, где одна:
- Определяет логику (методы) контроллера, которые могут совместно использоваться контроллерами.
- Инициирует переменные области видимости.
Опять же, важно помнить, что если директива включает изолированную область видимости, любые свойства внутри нее, которые наследуются от родительской области, пока недоступны.
Do
- Определение логики контроллера
- Инициировать переменные области
Не выполнять:
- Осмотреть дочерние элементы (они еще не отображаются, привязаны к области видимости и т.д.).
Ответ 8
Функция предварительной привязки
Каждая директива pre-link
функция вызывается всякий раз, когда создается новый связанный элемент.
Как видно ранее в разделе порядка компиляции, функции pre-link
называются parent-then-child, тогда как функции post-link
называются child-then-parent
.
Функция pre-link
используется редко, но может быть полезна в специальных сценариях; например, когда дочерний контроллер регистрируется с родительским контроллером, но регистрация должна быть в формате parent-then-child
(ngModelController
делает все так).
Не выполнять:
- Осмотреть дочерние элементы (они еще не отображаются, привязаны к области видимости и т.д.).