Пользовательские директивы AngularJS: шаблоны, область видимости и трансклюзия.
⚡ Умное резюме
Пользовательские директивы в AngularJS расширяют HTML, добавляя многократно используемое поведение, которое вы определяете самостоятельно. Фабрика директив возвращает объект определения, и AngularJS применяет это поведение везде, где в разметке появляется соответствующий элемент, атрибут, класс или комментарий.
Что такое пользовательская директива в AngularJS?
A Пользовательская директива В AngularJS это определяемая пользователем директива, которая расширяет HTML-код, добавляя в него поведение, написанное вами самостоятельно. Она регистрируется в модуль Используя функцию «директива», AngularJS прикрепляет её везде, где на странице появляется соответствующий элемент или атрибут. Несмотря на то, что AngularJS обладает множеством мощных инструментов, он всё же имеет свои преимущества. встроенные директивы нестандартные, например нг-повторить, иногда требуются специальные директивы.
⚠️ Исправление: В исходном тексте говорилось, что пользовательская директива «заменяет элемент, для которого она используется». По умолчанию этого не происходит: template вставлен внутри Соответствующий элемент. Требуется замена. replace: trueУстарело с версии AngularJS 1.3.
⚠️ Уведомление службы поддержки: AngularJS достиг Прекращение поддержки 31 декабря 2021 года. и не получает дальнейших исправлений или обновлений безопасности. Все приведенное ниже остается актуальным для поддержки существующего кода AngularJS 1.x; в современном Angular эквивалентами являются компоненты и директивы атрибутов.
Причина использования пользовательских директив — повторяемость. Когда одна и та же разметка или структура DOM-элементов встречается на нескольких экранах, директива преобразует их в один именованный тег.
Как создать пользовательскую директиву?
Рассмотрим пример того, как можно создать пользовательскую директиву в AngularJS.
В нашем случае пользовательская директива просто внедрит на страницу тег div с текстом «Учебник по Angular JS» при вызове директивы.
На приведенном ниже скриншоте с аннотациями пронумерованы четыре части описания, приведенные ниже.
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp"> <div ng-guru=""></div> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.directive('ngGuru',function(){ return { template: '<div>Angular JS Tutorial</div>' } }); </script> </body> </html>
⚠️ Внимание — в данном объявлении сохранены дефекты: Код воспроизведен в точности так, как он был опубликован. В каждом приведенном здесь листинге повторяются три ошибки: chrset="UTF 8" должно быть charset="UTF-8"; angular.js и angular.min.js Оба файла загружаются, поэтому AngularJS загружается дважды; angular-route.js Загружается перед файлом, от которого зависит.
Code объяснение
- Сначала мы создаем модуль для нашего приложения AngularJS. Это необходимо для создания пользовательской директивы, поскольку директива будет создана с помощью этого модуля.
- Сейчас мы создаём пользовательскую директиву под названием «ng».Guruи определить фабричную функцию, которая будет содержать пользовательский код для нашей директивы.
- Мы используем параметр шаблона, который определен AngularJS для пользовательских директив. В нем мы указываем, что всякий раз, когда используется эта директива, следует просто использовать значение шаблона и внедрить его в вызывающий код.
- Сейчас мы используем созданную нами директиву «ng-guru». При этом значение, заданное для нашего шаблона,
<div>Angular JS Tutorial</div>, теперь будет введен сюда.
Директива по наименованию и нормализации
Несоответствие имен между JavaСкрипты и HTML-код сбивают с толку большинство новичков. AngularJS нормализует Перед сопоставлением удаляются все имена элементов и атрибутов: удаляются любые x- or data- префикс, затем преобразует имя, ограниченное -, : or _ в формате camelCase. Вот почему. ngGuru спички ng-guru, и почему data-ng-guru тоже работает. Форма camelCase — это настоящее название; kebab-case — это просто то, как оно пишется в HTML.
⚠️ Исправление: В исходном тексте говорилось, что имя пользовательской директивы «должно начинаться с букв 'ng'». Официальная документация AngularJS советует обратное: никогда не добавляйте префикс к своим собственным директивам. ngили же они могут конфликтовать с будущей встроенной функцией. Используйте свой собственный префикс, например: g99Guru написано как g99-guru.
Если код выполнится успешно, отобразится следующий вывод.
Выход:
Результат наглядно показывает, что наша пользовательская директива ng-guru, в которой определен шаблон для отображения пользовательского текста, отображается в браузере. Следующий шаг — поместить в этот шаблон что-то полезное для повторного использования.
Как создавать многократно используемые директивы
Мы уже видели возможности пользовательских директив, но мы можем поднять их на новый уровень, создав собственные директивы многократного использования.
Допустим, например, мы хотим внедрить код, который бы постоянно отображал следующие HTML-теги на нескольких экранах, а именно поля для ввода «Имя» и «Возраст» пользователя.
Чтобы повторно использовать этот фрагмент на нескольких экранах без необходимости каждый раз писать к нему код, мы создаем главный элемент управления, или директиву, в AngularJS, которая будет содержать эти два элемента управления.
Теперь, вместо того чтобы повторять эту разметку на каждом экране, мы встраиваем её в директиву и вызываем её. На скриншоте ниже отмечена единственная изменённая строка.
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp"> <div ng-guru=""></div> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.directive('ngGuru',function(){ return { template: ' Name <input type="text"><br><br> Age<input type="text">' }; }); </script> </body> </html>
Code объяснение
- В нашем фрагменте кода для пользовательской директивы меняется только значение, передаваемое параметру шаблона. Вместо обычного тега или строки текста мы вводим весь фрагмент из двух полей ввода для «Имени» и «Возраста», которые необходимо отобразить на странице.
Примечание: Наконечник: Такой длинный шаблон должен находиться в отдельном файле. (Swap) template для templateUrl: 'name-age.html'.
Если код выполнится успешно, отобразится следующий вывод.
Выход:
Из приведенного выше результата видно, что фрагмент кода из шаблона пользовательской директивы добавляется на страницу. Шаблон пока статичен, поэтому следующий шаг — передать ему данные.
Директивы и области видимости AngularJS
сфера определяется как связующее звено, которое соединяет контроллер с представлением, управляя данными между представлением и контроллером.
При создании пользовательских директив AngularJS они по умолчанию будут иметь доступ к объекту области видимости в родительском контроллере. Это тот же самый объект области видимости, а не его копия.
Таким образом, пользовательской директиве становится проще использовать данные, передаваемые в главный контроллер.
Рассмотрим пример использования области видимости родительского контроллера в нашей пользовательской директиве. На скриншоте ниже пронумерованы четыре шага.
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp" ng-controller="DemoController"> <div ng-guru=""></div> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.controller('DemoController',function($scope) { $scope.tutorialName = "Angular JS"; }); app.directive('ngGuru',function(){ return { template: '<div>{{tutorialName}}</div>' } }); </script> </body> </html>
Code объяснение
- Сначала создадим контроллер под названием «DemoController». В нём определим переменную с именем tutorialName и привяжем её к объекту scope одним оператором:
$scope.tutorialName = "Angular JS". - В нашей пользовательской директиве мы можем вызвать переменную «tutorialName», используя выражение. Эта переменная будет доступна, поскольку она определена в контроллере «DemoController», который станет родительским для этой директивы.
- Мы ссылаемся на контроллер в теге div, который будет действовать как наш родительский тег div. Обратите внимание, что это необходимо сделать в первую очередь, чтобы наша пользовательская директива могла получить доступ к переменной TutorialName.
- Наконец, мы просто присоединяем нашу пользовательскую директиву «ng-guru» к нашему тегу div.
Если код выполнится успешно, отобразится следующий вывод.
Выход:
Приведенный выше вывод наглядно демонстрирует, что наша пользовательская директива «ng-guru» использует переменную области видимости tutorialName в родительском контроллере. Совместное использование области видимости родительского контроллера удобно, но не всегда является желаемым результатом.
Использование контроллеров с директивами
AngularJS предоставляет возможность получить доступ к переменной-члену контроллера непосредственно из пользовательских директив без необходимости объекта области.
Это иногда становится необходимым, поскольку в приложении может быть несколько объектов области видимости, принадлежащих нескольким контроллерам, поэтому высока вероятность ошибочного обращения к объекту области видимости неправильного контроллера.
В подобной ситуации есть способ конкретно указать из директивы: «Я хочу получить доступ к этому контроллеру».
Давайте рассмотрим пример того, как этого можно достичь. На скриншоте пронумерованы четыре шага.
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp" ng-controller="DemoController"> <div ng-guru99=""></div> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.controller('DemoController',function() { this.tutorialName = "Angular"; }); app.directive('ngGuru99',function(){ return { controller: 'DemoController', controllerAs: 'ctrl', template: '{{ctrl.tutorialName}}' }; }); </script> </body> </html>
Code объяснение
- Сначала создадим контроллер под названием «DemoController». В нём определим переменную с именем «tutorialName», и на этот раз, вместо того чтобы прикреплять её к объекту scope, прикрепим её непосредственно к контроллеру.
- В нашей пользовательской директиве мы особо отмечаем, что хотим использовать контроллер «DemoController», используя ключевое слово параметра контроллера.
- Мы создаём ссылку на контроллер, используя параметр «controllerAs». Это определено в AngularJS и является способом ссылки на контроллер по псевдониму.
- Наконец, в нашем шаблоне мы используем ссылку, созданную на шаге 3, и переменную-член, которая была напрямую прикреплена к контроллеру на шаге 1.
Примечание: В директиве можно получить доступ к нескольким контроллерам, указав соответствующие блоки контроллера, контроллеров и шаблонных операторов. Для доступа к одному из них... родителя Вместо контроллера директивы используйте require.
Если код выполнится успешно, отобразится следующий вывод.
Выход:
В результате выполнения становится ясно, что пользовательская директива обращается к DemoController и связанной с ним переменной-члену tutorialName, отображая текст «Angular». Оба подхода по-прежнему считывают данные из общей области видимости, чего предотвращает изолированная область видимости.
Чем отличаются привязки @, =, & и < в изолированной области видимости
Директива, имеющая общую область видимости с родительским контроллером, может быть использована только один раз для каждого контроллера, поскольку две копии будут конкурировать за одни и те же переменные. scope объект создает изолировать областьДиректива не видит ничего за пределами указанных вами свойств. Каждая запись использует префиксный символ, указывающий, как передается ее значение.
| Символ | Руководство | Полученная стоимость | Используйте это для |
|---|---|---|---|
| @ | Одностороннее движение | Атрибут представлен в виде интерполированной строки. | Метки, заголовки, любой простой текст |
| = | Двусторонний | Сам живой объект или ценность | Модели, в которые директива записывает данные обратно. |
| & | Внешний звонок | Функция, вычисляющая выражение во внешней области видимости. | Обратные вызовы, например, при закрытии или сохранении. |
| < | Одностороннее движение | Оценочная стоимость никогда не пересчитывалась в большую сторону. | Данные только для чтения, добавлены в AngularJS 1.5. |
Примечание: Наконечник: Тянуться < до =Односторонняя привязка данных дешевле в плане отслеживания и предотвращает незаметное изменение данных вызывающего компонента. В следующем разделе используется @.
Директивы и компоненты AngularJS: ng-transclude
Как мы уже упоминали, AngularJS предназначен для расширения функциональности HTML. И мы уже видели, как можно внедрить код с помощью пользовательских директив многократного использования.
Однако в современной разработке веб-приложений существует также концепция разработки.ping Веб-компоненты — это, по сути, создание собственных HTML-тегов, которые можно использовать в качестве компонентов в нашем коде.
Таким образом, AngularJS предоставляет еще один уровень возможностей для расширения HTML-тегов, позволяя заключать любую разметку, которую пользователь помещает внутрь тега.
Это делается с помощью «нг-перевестиДиректива, которая указывает AngularJS захватывать все, что помещено внутрь пользовательского тега в разметке, и вставлять это в шаблон в этом месте.
Рассмотрим пример того, как этого можно достичь. На скриншоте пронумерованы пять пунктов, которые следуют далее.
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp"> <pane title="{{title}}">Angular JS</pane> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.directive('pane',function(){ return { transclude:true, scope :{title:'@'}, template: '<div style="border: 1px solid black;"> '+ '<ng-transclude></ng-transclude>'+ '</div>' }; }); </script> </body> </html>
Code объяснение
- Мы используем директиву для определения пользовательского HTML-тега под названием «pane», код которого передается через фабричную функцию. В результате тег «pane» отображает текст «Angular JS» в прямоугольнике со сплошной черной рамкой.
- Свойство «transclude» должно быть установлено в значение true, что указывает AngularJS сохранять исходную внутреннюю разметку и делать её доступной для
ng-transclude. - В объекте области видимости мы объявляем привязку заголовка. Атрибуты обычно записываются в виде пар «имя/значение», например:
name="value"В нашем случае атрибут тега pane имеет значение «title», а символ «@» указывает AngularJS скопировать этот атрибут в виде интерполированной строки. - Шаблон рисует сплошную черную рамку для нашего элемента управления и отмечает место, куда вставляется включенная разметка.
- Наконец, мы вызываем наш пользовательский HTML-тег вместе с определенным атрибутом title.
⚠️ Исправление: В исходном тексте символ «@» был описан как «требование Angular». Это не формальность: @ является одним из четырех символов привязки в изолированной области видимости в таблице выше и всегда возвращает строку. Этот шаблон также никогда не отображается. {{title}}и ни один контроллер не определен titleТаким образом, виден только включённый текст.
Если код выполнится успешно, отобразится следующий вывод.
Выход:
- В результате отображается пользовательский элемент панели в виде рамки с рамкой, содержащей встроенный текст «Angular JS».
Директива может также содержать другие директивы, что и определяет способ сборки более крупных компонентов.
Вложенные директивы
Директивы в AngularJS могут быть вложенными. Как внутренние модули или функции в любом другом коде. язык программирования, вам может потребоваться встроить директивы друг в друга.
Вы можете лучше понять это, посмотрев пример ниже.
В этом примере мы создаем две директивы, называемые «внешняя» и «внутренняя».
- Внутренняя директива отображает текст под названием «Inner».
- В то время как внешняя директива фактически вызывает внутреннюю директиву для отображения текста под названием «Внутренний».
На скриншоте ниже отмечены определения обеих директив и точка, где одна вызывает другую.
</head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp"> <outer></outer> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.directive('outer',function(){ return { restrict:'E', template: '<div><h1>Outer</h1><inner></inner></div>', }}); app.directive('inner',function(){ return { restrict:'E', template: '<div><h1>Inner</h1></div>', } }); </script> </body> </html>
⚠️ Внимание — в этом объявлении сохранился дефект: Опубликованный список начинается с </head>пропустив начало <!DOCTYPE html>, <html> и <head> добавьте эти строки перед запуском.
Code объяснение
- Мы создаем директиву под названием «outer», которая будет вести себя как наша родительская директива. Затем эта директива выполнит вызов «внутренней» директивы.
-
restrict:'E'Эта опция указывает AngularJS сопоставлять каждую директиву по следующим параметрам: элемент имя, поэтому их можно записывать в виде тегов.<outer>и<inner>Буква «Е» — это сокращённая форма слова «Element» (Элемент). - Здесь мы создаем внутреннюю директиву, которая отображает текст «Inner» в теге div.
- В шаблоне внешней директивы мы вызываем внутреннюю директиву. Таким образом, здесь мы внедряем шаблон из внутренней директивы во внешнюю директиву.
- Наконец, мы напрямую вызываем внешнюю директиву.
⚠️ Исправление: В оригинальном тексте утверждалось restrict:'E' Требуется «обеспечить доступность данных из внутренней директивы для внешней директивы». На самом деле она ничего подобного не делает; она лишь контролирует. это Можно написать директиву. Вложенность работает, потому что компилятор постоянно переходит к шаблону, который он только что вставил. Для обмена данными используйте require и controller.
Если код выполнится успешно, отобразится следующий вывод.
Выход:
Из вывода,
- Видно, что были вызваны как внешняя, так и внутренняя директивы, и текст в обоих тегах div отображается.
Отображение разметки — это лишь половина работы директивы; другая половина — это реакция на действия пользователя.
Обработка событий в директиве
Мероприятия Такие действия, как щелчки мыши или нажатия кнопок, могут обрабатываться непосредственно внутри директив. Это делается с помощью функции `link`. Функция `link` позволяет директиве прикрепляться к элементам DOM на HTML-странице.
Синтаксис:
Синтаксис функции связи показан ниже.
link: function ($scope, element, attrs)
Функция link обычно принимает 3 параметра: область видимости, элемент, с которым связана директива, и атрибуты целевого элемента. Еще два параметра — обязательный контроллер и функция transclude — поступают при их применении.
Фаза компиляции против фазы компоновки
AngularJS обрабатывает директиву в два этапа, и знание того, какой этап какой, позволяет определить, где должен находиться ваш код.
- Компилировать Этот метод выполняется один раз для элемента шаблона, до того, как появится какая-либо область видимости. Он подходит для структурных изменений, общих для каждого клона.
- Ссылка Запускается один раз для каждого экземпляра, после клонирования шаблона и добавления области видимости. Здесь должны располагаться обработчики событий DOM и операции чтения из области видимости.
Поскольку у оператора compile нет области видимости, всё, что требует данных, должно находиться в операторе link, поэтому в приведённом ниже примере определено следующее: link в одиночестве.
Рассмотрим пример того, как мы можем этого добиться. На скриншоте пронумерованы четыре пункта, указанные ниже.
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp"> <div ng-guru="">Click Me</div> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.directive('ngGuru',function(){ return { link:function($scope,element,attrs) { element.bind('click',function () { element.html('You clicked me'); });} }}); </script> </body> </html>
Code объяснение
- Мы используем функцию link, определенную в AngularJS, чтобы предоставить директиве возможность доступа к событиям в HTML DOM.
- Мы используем ключевое слово 'element', потому что хотим реагировать на событие элемента HTML DOM, которым в нашем случае является элемент 'div'. Затем мы используем функцию 'bind' для добавления пользовательской функциональности к событию клика элемента. Слово 'click' — это ключевое слово, используемое для обозначения события клика любого элемента управления HTML.
- Здесь мы указываем, что хотим заменить внутренний HTML-код элемента, в нашем случае элемента div, текстом «Вы нажали на меня».
- Здесь мы определяем наш тег div для использования пользовательской директивы ng-guru.
Примечание: Наконечник: Директива должна выполнять самоочистку. Удалите все обработчики событий, которые существуют дольше, чем элемент внутри неё. element.on('$destroy', ...)или же страница вызывает утечку памяти.
Если код выполнится успешно, отобразится следующий вывод.
Выход:
- Изначально пользователю отображается текст «Нажмите сюда», поскольку он был задан внутри тега div. При нажатии на тег div вместо этого отображается следующий результат.
В каждом из приведенных выше примеров использовалось одно или два свойства одного и того же объекта определения. Полный набор заслуживает внимания.ping в руки.
Определение директивы. Справочник по свойствам объекта.
Объект, возвращаемый фабрикой директив, — это... Объект определения директивыКаждое свойство отвечает на один вопрос о том, как работает директива.
| Свойства | По умолчанию | Что она делает |
|---|---|---|
| ограничивать | 'EA' | Какие формы соответствуют: элемент E, атрибут A, класс C, комментарий M |
| шаблон | нет | Встроенная разметка, вставленная в соответствующий элемент. |
| templateUrl | нет | Путь к файлу шаблона, загружается один раз, затем кэшируется. |
| сфера | ложный | false разделяет область видимости родительского объекта, true создает дочерний объект, изолирует объект. |
| контроллер | нет | Конструктор, предоставляющий API, который могут потребовать другие директивы. |
| контроллерА | нет | Псевдоним для контроллера внутри шаблона |
| требовать | нет | Контроллер другой директивы для внедрения; ^ ищет родительские директивы, ^^ пропускает себя, ? необязателен |
| ссылка. | нет | Функция для каждого экземпляра: scope, element, attrs, controller, transcludeFn |
| компилировать | нет | Выполняется один раз для шаблона до создания какой-либо области видимости. |
| трансклюдировать | ложный | Сохраняет исходную внутреннюю разметку для ng-transclude. |
| приоритет | 0 | Порядок компиляции определяется общим элементом; более старшие элементы компилируются первыми. |
| терминал | ложный | Останавливает директивы с более низким приоритетом для этого элемента. |
| заменить | ложный | Заменяет элемент шаблоном. Устарело с версии 1.3. |
Примечание: Наконечник: Начинать с restrict, template и link, добавлять scope для повторного использования, controller и require для звонков между различными подразделениями и уйти priority и terminal в одиночестве.

















