Учебное пособие по AngularJS: демистификация пользовательских директив
Опубликовано: 2022-03-11С быстрым ростом JavaScript как языка полного стека все больше и больше приложений используют фреймворки, которые позволяют веб-браузеру обрабатывать больше операций пользовательского интерфейса, таких как привязка данных, управление представлениями данных, преобразование данных и многие другие службы. Одной из самых функциональных, расширяемых и популярных сред является AngularJS, а одним из наиболее полезных компонентов среды AngularJS является то, что называется директивой . AngularJS предоставляет множество полезных директив и, что еще более важно, предоставляет богатую основу для создания пользовательских директив.
Что такое директива? Проще говоря, директивы — это функции JavaScript, которые манипулируют и добавляют поведение к элементам HTML DOM. Директивы могут быть очень простыми или чрезвычайно сложными. Поэтому очень важно получить четкое представление об их многочисленных параметрах и функциях, которые ими управляют.
В этом руководстве будут рассмотрены четыре функции, которые выполняются как директивы и применяются к DOM, и будут предоставлены примеры. Этот пост предполагает некоторое знакомство с AngularJS и пользовательскими директивами. Если вы новичок в Angular, вам может понравиться руководство по созданию вашего первого приложения AngularJS.
Четыре функции жизненного цикла директивы AngularJS
Существует множество параметров, которые можно настроить, и важно, как эти параметры связаны друг с другом. Каждая директива проходит нечто похожее на жизненный цикл, когда AngularJS компилирует и связывает DOM. Жизненный цикл директивы начинается и заканчивается в процессе начальной загрузки AngularJS до отображения страницы. В жизненном цикле директивы есть четыре отдельные функции, которые могут выполняться, если они определены. Каждая из них позволяет разработчику контролировать и настраивать директиву на разных этапах жизненного цикла.
Четыре функции: compile , controller , pre-link и post-Link .
Функция компиляции позволяет директиве манипулировать DOM до того, как она будет скомпилирована и связана, что позволяет ей добавлять/удалять/изменять директивы, а также добавлять/удалять/изменять другие элементы DOM.
Функция контроллера облегчает директивную коммуникацию. Братские и дочерние директивы могут запрашивать контроллер своих братьев и сестер и родителей для передачи информации.
Функция предварительной ссылки позволяет манипулировать частной $scope до начала процесса пост-ссылки.
Метод пост-ссылки является основным рабочим методом директивы.
В директиве происходят манипуляции с DOM после компиляции, настраиваются обработчики событий, а также часы и другие вещи. В декларации директивы четыре функции определены следующим образом.
.directive("directiveName",function () { return { controller: function() { // controller code here... }, compile: { // compile code here... return { pre: function() { // pre-link code here... }, post: function() { // post-link code here... } }; } } })Как правило, не все функции необходимы. В большинстве случаев разработчики просто создают контроллер и функцию пост-ссылки , следуя приведенному ниже шаблону.
.directive("directiveName",function () { return { controller: function() { // controller code here... }, link: function() { // post-link code here... } } })В этой конфигурации ссылка относится к функции пост-ссылки .
Независимо от того, определены ли все функции или некоторые из них, важен порядок их выполнения, особенно их выполнение относительно остальной части приложения AngularJS.
Выполнение функции директивы AngularJS по отношению к другим директивам
Рассмотрим следующий фрагмент HTML с директивами parentDir , childDir и grandChildDir , примененными к фрагменту HTML.
<div parentDir> <div childDir> <div grandChildDir> </div> </div> </div>Порядок выполнения функций внутри директивы и относительно других директив следующий:
- Фаза компиляции
- Функция компиляции : parentDir
- Функция компиляции : childDir
- Функция компиляции : grandChildDir
- Контроллер и фаза предварительной связи
- Функция контроллера : parentDir
- Функция предварительной ссылки : parentDir
- Функция контроллера : childDir
- Функция предварительной ссылки : childDir
- Функция контроллера : grandChildDir
- Функция предварительной ссылки : grandChildDir
- Этап пост-ссылки
- Функция пост-ссылки : grandChildDir
- Функция пост-ссылки : childDir
- Функция пост-ссылки : parentDir
Объяснение функции директивы AngularJS: глубокое погружение
Фаза компиляции происходит первой. По сути, на этапе компиляции прослушиватели событий присоединяются к элементам DOM. Например, если конкретный элемент DOM привязан к свойству $scope , к этому элементу DOM применяется прослушиватель событий, который позволяет обновлять его значением свойства $scope . Процесс компиляции начинается с корневого элемента DOM, из которого было загружено приложение AngularJS, и проходит по ветвям DOM, используя обход в глубину, сначала компилируя родительский элемент, а затем его дочерние элементы вплоть до листовых узлов.
После завершения компиляции директивы больше не могут быть добавлены или удалены из DOM (хотя есть способ обойти это, напрямую используя службу компиляции. Следующим этапом является вызов контроллеров и функций предварительной компоновки для всех директив. Когда контроллер вызывается, $scope доступен и может быть использован.Элемент $, введенный в контроллер, содержит скомпилированный шаблон, но не включает включенный дочерний контент (включенный контент — это содержимое между начальным и конечным HTML-тегами, на котором указана директива). По определению, контроллеры в шаблоне MVC просто передают модель в представление и определяют функции для обработки событий. Следовательно, контроллер директивы не должен модифицировать DOM директивы по двум причинам: это нарушает цель Контроллер, а включенный дочерний контент не был добавлен в DOM. Так что же делает контроллер, помимо изменения $scope ? Контроллер позволяет дочерним директивам взаимодействовать с родительские директивы. Саму функцию контроллера следует рассматривать как объект контроллера, который будет передан в функцию пост-ссылки дочерней директивы, если дочерняя директива запросит его. Таким образом, контроллер обычно используется для облегчения взаимодействия директив путем создания объекта со свойствами и методами, которые могут использоваться его родственными и дочерними директивами. Родительская директива не может определить, может ли дочерняя директива потребовать свой контроллер, поэтому лучше ограничить код в этом методе функциями и свойствами, которые могут безопасно использоваться дочерними директивами.

После функции контроллера выполняется функция предварительной связи. Функция предварительной ссылки для многих загадочна. Если вы прочитали большую часть документации в Интернете и в книгах, люди пишут, что эта функция используется только в редких случаях и людям она почти никогда не понадобится. Те же самые объяснения не дают примера ситуации, в которой его можно было бы использовать.
Функция предварительной ссылки на самом деле совсем не сложная. Во-первых, если вы просмотрите исходный код AngularJS, вы найдете отличный пример функции предварительной ссылки: ее использует директива ng-init . Почему? Это просто отличный способ выполнить приватный код с использованием $scope ; код, который не может быть вызван родственными и дочерними директивами. В отличие от функции контроллера, функция предварительной ссылки не передается в директивы. Следовательно, его можно использовать для выполнения кода, который изменяет $scope своей директивы. Именно это и делает директива ng-init . Когда функция предварительной ссылки для ng-init выполняется, она просто выполняет JavaScript, переданный в директиву, против $scope директивы. Результат выполнения доступен через прототипное наследование $scope дочерним директивам во время выполнения их функций контроллера, предварительной ссылки и пост-ссылки, но без предоставления доступа к этим дочерним директивам для повторного выполнения кода в родительской предварительной ссылке. функция ссылки. Кроме того, директиве может потребоваться выполнить другой код, не связанный с $scope , который он хочет сохранить закрытым.
Некоторые опытные разработчики AngularJS сказали бы, что этот закрытый код все еще может быть помещен в контроллер, а затем не вызываться дочерними директивами. Этот аргумент будет верен, если директива будет использоваться только первоначальным разработчиком, который ее написал, но если директива будет распространяться и повторно использоваться другими разработчиками, то инкапсуляция частного кода в функцию предварительной ссылки может быть очень полезной. Поскольку разработчики никогда не знают, как их директива будет повторно использоваться с течением времени, защита частного кода от выполнения дочерней директивой является хорошим подходом к инкапсуляции кода директивы. Я считаю хорошей практикой размещать открытый код директивной связи в функции контроллера, а частный код — в функции предварительной связи. Как и контроллер, предварительная ссылка никогда не должна выполнять манипуляции с DOM или выполнять функцию преобразования, поскольку содержимое дочерних директив еще не было связано.
Для каждой директивы ее контроллер и функция предварительной ссылки выполняются до контроллера и функции предварительной ссылки ее дочерних директив. Как только фаза контроллера и предварительной ссылки для всех директив завершена, AngularJS начинает фазу связывания и выполняет функции пост-ссылки для каждой директивы. Фаза компоновки выполняется противоположно потокам компиляции, контроллера и предварительной компоновки, начиная с листовых узлов DOM и продвигаясь вверх к корневому узлу DOM. Обход DOM после ссылки следует в основном по пути в глубину. Когда каждая дочерняя директива связана, выполняется ее функция post-link.
Функция post-link чаще всего реализуется в пользовательских директивах AngularJS. В этой функции можно сделать почти все разумное. Можно манипулировать DOM (только для себя и дочерних элементов), доступен $scope , можно использовать объект контроллера для родительских директив, можно запускать функции включения и т. д. Однако есть несколько ограничений. Новые директивы не могут быть добавлены в DOM, потому что они не будут скомпилированы. Кроме того, все манипуляции с DOM должны выполняться с использованием функций DOM. Простой вызов функции html для элемента DOM и передача нового HTML удалит все обработчики событий, добавленные в процессе компиляции. Например, они не будут работать должным образом:
element.html(element.html());или
element.html(element.html() + "<div>new content</div>");Код не приведет к изменению HTML, но переназначение строковой версии элементов DOM удалит все обработчики событий, созданные в процессе компиляции. Обычно функция post-link используется для подключения обработчиков событий, $watch es и $observe s.
После выполнения всех функций пост-ссылки $scope применяется к скомпилированной и связанной структуре DOM, и страница AngularJS оживает.
Директивная функциональная схема
Вот диаграмма, в которой указано назначение каждой функции, что доступно при ее выполнении, а также рекомендации по правильному использованию каждой функции.
| Исполнение Заказ | Директива Функция | ДОМ | включить | $область | Вызываемый от ребенка |
|---|---|---|---|---|---|
| 1 | компилировать | DOM не был скомпилирован, но шаблон был загружен в область содержимого элемента DOM. Директивы можно добавлять и удалять. DOM можно манипулировать как функциями DOM, так и заменой строк HTML. | Функция Transclude доступна, но устарела и не должна вызываться. | Недоступно. | Функция не может быть вызвана дочерними элементами. |
| 2 | контроллер | Скомпилированный элемент DOM доступен, но не должен изменяться. Включенный дочерний контент не был добавлен в элемент DOM. Никакие изменения DOM не должны происходить, потому что это контроллер, а включенный дочерний контент еще не был связан. | Функция Transclude доступна, но не должна вызываться. | $scope доступен и может быть использован. Параметры функции вводятся с помощью службы $injector . | Функция передается в дочерние директивы, связывающие функции, и вызывается ими. |
| 3 | предварительная ссылка | Скомпилированный элемент DOM доступен, но его не следует изменять, поскольку дочерние элементы DOM директивы еще не связаны. | Функция Transclude доступна, но не должна вызываться. | $scope доступен и может быть изменен. | Функция не вызывается дочерними директивами. Но может вызывать контроллеры родительских директив. |
| 4 | почтовая ссылка | Доступны скомпилированные элементы DOM и дочерние директивные элементы DOM. DOM можно изменить только с помощью функций DOM (без замены HTML), и можно добавлять только контент, который не требует компиляции. Добавление/удаление директив запрещено. | Функция Transclude доступна и может быть вызвана. | $scope доступен и может быть использован. | Не вызывается дочерними директивами, но может вызывать контроллер родительских директив. |
Резюме
В этом руководстве по директивам AngularJS мы узнали о цели, порядке выполнения и общих возможностях и использовании каждой из четырех функций директивы: compile , controller , pre-link и post-link . Из четырех функций чаще всего используются контроллер и пост-ссылка, но для более сложных директив, которым требуется больший контроль над DOM или нужна среда выполнения с частной областью, можно использовать функции компиляции и предварительной ссылки.
