Spec-Zone.ru › Angular.js 1.6

Улучшить эту документацию Просмотреть исходный код $compile

  1. $compileProvider
  2. служба в модуле ng

Обзор

Компилирует строку HTML или DOM в шаблон и генерирует функцию шаблона, которая может быть использована для связывания scope и шаблона.

Компиляция — это процесс обхода дерева DOM и сопоставления элементов DOM с директивами.

Примечание: Этот документ — подробная справка по всем параметрам директивы. Для ознакомления с директивами и примерами их распространённого использования см. руководство по директивам.

Полный API директивы

Существует множество различных вариантов директивы.

Разница заключается в возвращаемом значении функции-фабрики. Вы можете вернуть объект определения директивы (см. ниже), определяющий свойства директивы, или просто функцию postLink, (все остальные свойства будут иметь значения по умолчанию).

Рекомендация: Рекомендуется использовать форму "объекта определения директивы".

Вот пример директивы, объявленной с помощью объекта определения директивы:

var myModule = angular.module(...);

myModule.directive('directiveName', function factory(injectables) {
  var directiveDefinitionObject = {
    priority: 0,
    template: '<div></div>', // or // function(tElement, tAttrs) { ... },
    // or
    // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
    transclude: false,
    restrict: 'A',
    templateNamespace: 'html',
    scope: false,
    controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
    controllerAs: 'stringIdentifier',
    bindToController: false,
    require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
    multiElement: false,
    compile: function compile(tElement, tAttrs, transclude) {
      return {
         pre: function preLink(scope, iElement, iAttrs, controller) { ... },
         post: function postLink(scope, iElement, iAttrs, controller) { ... }
      }
      // or
      // return function postLink( ... ) { ... }
    },
    // or
    // link: {
    //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
    //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
    // }
    // or
    // link: function postLink( ... ) { ... }
  };
  return directiveDefinitionObject;
});
Примечание: Любые не указанные параметры будут использовать значение по умолчанию. Вы можете ознакомиться со значениями по умолчанию ниже.

Следовательно, вышесказанное можно упростить следующим образом:

var myModule = angular.module(...);

myModule.directive('directiveName', function factory(injectables) {
  var directiveDefinitionObject = {
    link: function postLink(scope, iElement, iAttrs) { ... }
  };
  return directiveDefinitionObject;
  // or
  // return function postLink(scope, iElement, iAttrs) { ... }
});

Обработчики жизненного цикла

Контроллеры директивы могут предоставлять следующие методы, которые вызываются AngularJS в определённые моменты жизненного цикла директивы:

  • $onInit() — Вызывается для каждого контроллера после того, как все контроллеры на элементе были построены и их привязки были инициализированы (и до функций предварительной и последующей привязки для директивы на этом элементе). Это хорошее место для размещения кода инициализации для вашего контроллера.
  • $onChanges(changesObj) — Вызывается всякий раз, когда происходит обновление односторонних (<) или интерполяционных (@) привязок. changesObj — это хеш, ключи которого представляют собой имена связанных свойств, которые изменились, а значения представляют собой объект типа { currentValue, previousValue, isFirstChange() }. Используйте этот обработчик для запуска обновлений внутри компонента, например, для клонирования связанного значения, чтобы предотвратить случайное изменение внешнего значения. Обратите внимание, что это также будет вызвано при инициализации ваших привязок.
  • $doCheck() — Вызывается на каждом шаге цикла обработки. Предоставляет возможность обнаружения и реагирования на изменения. Любые действия, которые вы хотите выполнить в ответ на обнаруженные изменения, должны быть вызваны из этого обработчика; реализация этого не влияет на время вызова $onChanges. Например, этот обработчик может быть полезен, если вы хотите выполнить проверку глубокого равенства или проверить объект Date, изменения которого не будут обнаружены детектором изменений AngularJS и, таким образом, не вызовут $onChanges. Этот обработчик вызывается без аргументов; если вы обнаруживаете изменения, вы должны сохранить предыдущее(ые) значение(я) для сравнения с текущими значениями.
  • $onDestroy() — Вызывается для контроллера, когда содержащий его scope уничтожен. Используйте этот обработчик для освобождения внешних ресурсов, наблюдателей и обработчиков событий. Обратите внимание, что у компонентов вызываются обработчики $onDestroy() в том же порядке, что и события $scope.$broadcast, которые генерируются сверху вниз. Это означает, что у родительских компонентов обработчик $onDestroy() вызывается до дочерних компонентов.
  • $postLink() — Вызывается после того, как элемент этого контроллера и его дочерние элементы были связаны. Аналогично функции после привязки, этот обработчик можно использовать для настройки обработчиков событий DOM и выполнения прямой манипуляции с DOM. Обратите внимание, что дочерние элементы, содержащие директивы templateUrl , не будут скомпилированы и связаны, поскольку они ожидают загрузки шаблона асинхронно и их собственная компиляция и связывание приостановлены до тех пор, пока это не произойдёт.

Сравнение с обработчиками жизненного цикла в новом Angular

Новый Angular также использует обработчики жизненного цикла для своих компонентов. Хотя обработчики жизненного цикла AngularJS похожи, есть некоторые различия, о которых вам следует знать, особенно при переносе вашего кода из AngularJS в Angular:

  • Обработчики AngularJS имеют префикс $, например $onInit. Обработчики Angular имеют префикс ng, например ngOnInit.
  • Обработчики AngularJS можно определять на прототипе контроллера или добавлять в контроллер внутри его конструктора. В Angular вы можете определять обработчики только на прототипе класса компонента.
  • Из-за различий в обнаружении изменений вы можете получить гораздо больше вызовов $doCheck в AngularJS, чем вызовов ngDoCheck в Angular.
  • Изменения модели внутри $doCheck вызовут новые циклы обработки, которые приведут к распространению изменений по всему приложению. Angular не позволяет обработчику ngDoCheck запускать изменения за пределами компонента. Он либо выдаст ошибку, либо ничего не сделает, в зависимости от состояния enableProdMode().

Примеры обработчиков жизненного цикла

Этот пример показывает, как можно проверить изменения объекта Date, даже если идентичность объекта не изменилась.

Этот пример показывает, как можно использовать $doCheck для запуска изменений вводах вашего компонента, даже если фактическая идентичность компонента не изменяется. (Помните, что клонирование и проверки глубокого равенства на больших массивах или объектах могут негативно сказаться на производительности вашего приложения).

Объект определения директивы

Объект определения директивы предоставляет инструкции для компилятора. Атрибуты:

multiElement

Когда это свойство установлено в true (значение по умолчанию — false), компилятор HTML соберет узлы DOM между узлами с атрибутами directive-name-start и directive-name-end, и сгруппирует их вместе как элементы директивы. Рекомендуется использовать эту функцию для директивы, которая не является строго поведенческой (например, ngClick), и не манипулирует или не заменяет дочерние узлы (например, ngInclude).

priority

Когда для одного DOM-элемента определено несколько директивы, иногда необходимо указать порядок применения директивы. priority используется для сортировки директивы перед вызовом их функций compile. Приоритет определяется как число. Директивы с большим числовым priority компилируются первыми. Функции предварительной привязки также выполняются в порядке приоритета, но функции последующей привязки выполняются в обратном порядке. Порядок директивы с одинаковым приоритетом не определён. Приоритет по умолчанию — 0.

terminal

Если установлено в true, то текущая priority будет последним набором директивы, который будет выполнен (любые директивы с текущим приоритетом всё равно будут выполнены, поскольку порядок выполнения директивы с одинаковым priority не определён). Обратите внимание, что выражения и другие директивы, используемые в шаблоне директивы, также будут исключены из выполнения.

scope

Свойство scope может быть false, true, или объектом:

  • false (по умолчанию): Для директивы не будет создано scope. Директива будет использовать scope родительского элемента.

  • true: Для элемента директивы будет создан новый дочерний scope, который прототипически наследует от родительского scope. Если несколько директивы на одном элементе запросят новый scope, будет создан только один новый scope.

  • {...} (объект-хеш): Для шаблона директивы создаётся новый "изолированный" scope. "Изолированный" scope отличается от обычного scope тем, что он не наследует от родительского scope. Это полезно при создании повторно используемых компонентов, которые не должны случайно считывать или изменять данные в родительском scope. Обратите внимание, что директива с изолированным scope без template или templateUrl не будет применять изолированный scope к дочерним элементам.

Объект-хеш "изолированного" scope определяет набор локальных свойств scope, полученных из атрибутов элемента директивы. Эти локальные свойства полезны для алиасов значений для шаблонов. Ключи в объекте хеша соответствуют имени свойства в изолированном scope; значения определяют, как свойство привязано к родительскому scope, с помощью соответствующих атрибутов в элементе директивы:

  • @ или @attr — привязывают свойство локального контекста к значению атрибута DOM. Результат всегда является строкой, так как атрибуты DOM — это строки. Если имя attr не указано, то предполагается, что имя атрибута совпадает с локальным именем. При заданном <my-component my-attr="hello {{name}}"> и определении изолированного контекста scope: { localName:'@myAttr' }, свойство контекста директивы localName будет отражать интерполированное значение hello {{name}}. При изменении атрибута name будет изменяться и свойство localName в контексте директивы. Значение name считывается из родительского контекста (не из контекста директивы).

  • = или =attr — устанавливают двунаправленную привязку между свойством локального контекста и выражением, переданным через атрибут attr. Выражение вычисляется в контексте родительского контекста. Если имя attr не указано, то предполагается, что имя атрибута совпадает с локальным именем. При заданном <my-component my-attr="parentModel"> и определении изолированного контекста scope: { localModel: '=myAttr' }, свойство localModel в контексте директивы будет отражать значение parentModel в родительском контексте. Изменения в parentModel будут отражаться в localModel и наоборот. Необязательные атрибуты должны быть помечены вопросительным знаком: =? или =?attr. Если выражение привязки не является присваиваемым или если атрибут не является необязательным и не существует, при обнаружении изменений в локальном значении будет выброшено исключение ($compile:nonassign), так как будет невозможно синхронизировать их обратно в родительский контекст. По умолчанию используется метод $watch для отслеживания изменений, а проверка на равенство основана на тождестве объекта. Однако, если в качестве выражения привязки передаётся литерал объекта или литерал массива, проверка на равенство выполняется по значению (с использованием функции angular.equals). Также можно отслеживать вычисленное значение поверхностно с помощью $watchCollection: используйте =* или =*attr (=*? или =*?attr если атрибут является необязательным).

  • < или <attr — устанавливают одностороннюю привязку между свойством локального контекста и выражением, переданным через атрибут attr. Выражение вычисляется в контексте родительского контекста. Если имя attr не указано, то предполагается, что имя атрибута совпадает с локальным именем. Вы также можете сделать привязку необязательной, добавив ?: <? или <?attr.

    Например, при заданном <my-component my-attr="parentModel"> и определении директивы scope: { localModel:'<myAttr' }, свойство изолированного контекста localModel будет отражать значение parentModel в родительском контексте. Любые изменения в parentModel будут отражаться в localModel, но изменения в localModel не будут отражаться в parentModel. Однако есть два нюанса:

    1. односторонняя привязка не копирует значение из родителя в изолированный контекст, она просто устанавливает то же значение. Это означает, что если привязанное значение является объектом, изменения его свойств в изолированном контексте будут отражаться в родительском контексте (поскольку оба ссылаются на один и тот же объект).
    2. односторонняя привязка отслеживает изменения идентичности родительского значения. Это означает, что $watch для родительского значения срабатывает только в случае изменения ссылки на значение. В большинстве случаев это не должно вызывать беспокойства, но это может быть важно, если вы производите одностороннюю привязку к объекту, а затем заменяете этот объект в изолированном контексте. Если вы теперь измените свойство объекта в родительском контексте, изменение не будет распространено на изолированный контекст, потому что идентичность объекта в родительском контексте не изменилась. Вместо этого вы должны назначить новый объект.

    Односторонняя привязка полезна, если вы не планируете распространять изменения из вашего изолированного контекста обратно в родительский. Однако это не делает это совершенно невозможным.

  • & или &attr — позволяет выполнить выражение в контексте родительского контекста. Если имя attr не указано, то предполагается, что имя атрибута совпадает с локальным именем. При заданном <my-component my-attr="count = count + value"> и определении изолированного контекста scope: { localFn:'&myAttr' }, свойство изолированного контекста localFn будет ссылаться на обёртку функции для выражения count = count + value. Часто желательно передавать данные из изолированного контекста в родительский контекст через выражение. Это можно сделать, передав в обёртку функции выражения мапу из имён локальных переменных и их значений. Например, если выражение — increment(amount), то мы можем указать значение amount, вызвав localFn как localFn({amount: 22}).

В общем случае можно применять несколько директивы к одному элементу, но могут быть ограничения в зависимости от типа контекста, требуемого директивами. Следующие пункты помогут объяснить эти ограничения. Для простоты рассматриваются только две директивы, но это также применимо для нескольких директивы:

  • без контекста + без контекста => Две директивы, которые не требуют своего собственного контекста, будут использовать контекст родителя
  • контекст дочернего элемента + без контекста => Обе директивы будут использовать один и тот же дочерний контекст
  • контекст дочернего элемента + контекст дочернего элемента => Обе директивы будут использовать один и тот же дочерний контекст
  • изолированный контекст + без контекста => Директива с изолированным контекстом будет использовать свой собственный созданный изолированный контекст. Другая директива будет использовать контекст родителя
  • изолированный контекст + контекст дочернего элемента => Не сработает! Только один контекст может быть связан с одним элементом. Поэтому эти директивы нельзя применять к одному элементу.
  • изолированный контекст + изолированный контекст => Не сработает! Только один контекст может быть связан с одним элементом. Поэтому эти директивы нельзя применять к одному элементу.

bindToController

Это свойство используется для привязки свойств контекста напрямую к контроллеру. Оно может быть либо true , либо объектом-хешем с тем же форматом, что и свойство scope.

Когда для директивы используется изолированный контекст (см. выше), bindToController: true позволит компоненту привязать свои свойства к контроллеру, а не к контексту.

После создания контроллера начальные значения привязок изолированного контекста будут привязаны к свойствам контроллера. Вы можете получить доступ к этим привязки после их инициализации, предоставив метод контроллера под названием $onInit, который вызывается после того, как все контроллеры на элементе были созданы и их привязки были инициализированы.

Предупреждение о устаревании: если $compileProcvider.preAssignBindingsEnabled(true) был вызван, привязки для контроллеров, не являющихся классами ES6, привязываются к this до вызова конструктора контроллера, но это использование теперь устарело. Пожалуйста, поместите код инициализации, который полагается на привязки, внутри метода $onInit контроллера вместо этого.

Также можно задать bindToController в виде объекта-хеша с тем же форматом, что и свойство scope . Это установит привязки контекста непосредственно к контроллеру. Обратите внимание, что scope по-прежнему может использоваться для определения типа создаваемого контекста. По умолчанию контекст не создаётся. Используйте scope: {} для создания изолированного контекста (полезно для директив компонентов).

Если оба bindToController и scope определены и содержат хеши объектов, то bindToController переопределяет scope.

controller

Конструктор контроллера. Контроллер создаётся до фазы предварительной привязки и может быть доступен другим директивам (см. атрибут require). Это позволяет директивам взаимодействовать друг с другом и дополнять поведение друг друга. Контроллер инжектируем (и поддерживает нотацию квадратных скобок) со следующими локалями:

  • $scope — Текущий контекст, связанный с элементом
  • $element — Текущий элемент
  • $attrs — Текущий объект атрибутов для элемента
  • $transclude — Функция привязки трансляции, предварительно связанная с правильным контекстом трансляции: function([scope], cloneLinkingFn, futureParentElement, slotName):
    • scope: (необязательно) переопределить контекст.
    • cloneLinkingFn: (необязательно) аргумент для создания клонов исходного транслированного содержимого.
    • futureParentElement (необязательно):
      • определяет родителя, к которому cloneLinkingFn добавит клонированные элементы.
      • по умолчанию: $element.parent() соответственно $element для transclude:'element' соответственно transclude:true.
      • необходимо только для трансляций, которые могут содержать элементы, не являющиеся HTML (например, элементы SVG), и когда cloneLinkingFn передаётся, так как эти элементы необходимо создать и клонировать особым образом, когда они определены вне своих обычных контейнеров (например, как <svg>).
      • См. также свойство directive.templateNamespace.
    • slotName: (необязательно) имя слота для трансляции. Если ложно (например, null, undefined или '' ), то предоставляется стандартная трансляция. Функция $transclude также имеет метод на ней, $transclude.isSlotFilled(slotName), который возвращает true , если указанный слот содержит содержимое (т. е. один или несколько узлов DOM).

require

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

  • строка, содержащая имя директивы, передаваемое в функцию привязки
  • массив, содержащий имена директив, передаваемые в функцию привязки. Аргумент, передаваемый в функцию привязки, будет массивом контроллеров в том же порядке, что и имена в свойстве require
  • объект, значения свойств которого являются именами директив, передаваемых в функцию привязки. Аргумент, передаваемый в функцию привязки, также будет объектом с соответствующими ключами, значения которых будут содержать соответствующие контроллеры.

Если свойство require является объектом и bindToController имеет истинное значение, то необходимые контроллеры привязываются к контроллеру, используя ключи свойства require. Эта привязка происходит после того, как все контроллеры были созданы, но перед вызовом $onInit. Если имя необходимого контроллера совпадает с локальным именем (ключом), то имя можно опустить. Например, {parentDir: '^^'} эквивалентно {parentDir: '^^parentDir'}. Обратитесь к $compileProvider для примера использования. Если такой(ие) необходимый(ые) директива(ы) не найдены или директива не имеет контроллера, то генерируется ошибка (если не указана функция связи, и необходимые контроллеры не привязываются к контроллеру директивы, в этом случае проверка ошибок пропускается). Имя можно префиксровать:

  • (без префикса) - Найти необходимый контроллер на текущем элементе. Выбросить ошибку, если не найден.
  • ? - Попробовать найти необходимый контроллер или передать null функции link если не найден.
  • ^ - Найти необходимый контроллер, просматривая элемент и его предков. Выбросить ошибку, если не найден.
  • ^^ - Найти необходимый контроллер, просматривая предков элемента. Выбросить ошибку, если не найден.
  • ?^ - Попробовать найти необходимый контроллер, просматривая элемент и его предков, или передать null функции link если не найден.
  • ?^^ - Попробовать найти необходимый контроллер, просматривая предков элемента, или передать null функции link если не найден.

controllerAs

Имя идентификатора для ссылки на контроллер в области видимости директивы. Это позволяет ссылаться на контроллер из шаблона директивы. Это особенно полезно, когда директива используется как компонент, т.е. со isolate областью видимости. Также возможно использовать его в директиве без isolate / new области видимости, но необходимо понимать, что ссылка controllerAs может перезаписать свойство, которое уже существует в родительской области видимости.

restrict

Строка подмножества EACM, которое ограничивает директиву определённым стилем объявления директивы. Если опущено, используются значения по умолчанию (элементы и атрибуты).

  • E - Имя элемента (по умолчанию): <my-directive></my-directive>
  • A - Атрибут (по умолчанию): <div my-directive="exp"></div>
  • C - Класс: <div class="my-directive: exp;"></div>
  • M - Комментарий: <!-- directive: my-directive exp -->

templateNamespace

Строка, представляющая тип документа, используемый разметкой в шаблоне. AngularJS нуждается в этой информации, поскольку этим элементам необходимо создать и клонировать особым способом, когда они определяются за пределами своих обычных контейнеров, таких как <svg> и <math>.

  • html - Все корневые узлы в шаблоне являются HTML. Корневые узлы также могут быть элементами верхнего уровня, такими как <svg> или <math>.
  • svg - Корневые узлы в шаблоне являются элементами SVG (исключая <math>).
  • math - Корневые узлы в шаблоне являются элементами MathML (исключая <svg>).

Если templateNamespace не указано, то пространство имён считается html.

template

HTML разметка, которая может:

  • Заменить содержимое элемента директивы (по умолчанию).
  • Заменить сам элемент директивы (если replace имеет значение true — устаревший).
  • Оборачивать содержимое элемента директивы (если transclude имеет значение true).

Значение может быть:

  • Строкой. Например, <div red-on-hover>{{delete_str}}</div>.
  • Функцией, которая принимает два аргумента tElement и tAttrs (описаны в API функции compile ниже) и возвращает строковое значение.

templateUrl

Это аналогично template, но шаблон загружается из указанного URL-адреса асинхронно.

Поскольку загрузка шаблона асинхронна, компилятор приостановит компиляцию директив на этом элементе до тех пор, пока шаблон не будет решён. Тем временем он продолжит компиляцию и связывание элементов-братьев и родителей так, как если бы этот элемент не содержал никаких директив.

Компилятор не приостанавливает всю компиляцию, чтобы дождаться загрузки шаблонов, потому что это приведёт к «зависанию» всего приложения до тех пор, пока все шаблоны не будут загружены асинхронно — даже в случае, когда только одна вложенная директива имеет templateUrl.

Загрузка шаблона является асинхронной даже если шаблон был предварительно загружен в $templateCache

Вы можете указать templateUrl как строку, представляющую URL-адрес, или как функцию, которая принимает два аргумента tElement и tAttrs (описанные в API функции compile ниже) и возвращает строковое значение, представляющее URL-адрес. В обоих случаях URL-адрес шаблона передаётся через $sce.getTrustedResourceUrl.

replace (УСТАРЕЛО)

replace будет удалено в следующей основной версии — т.е. v2.0).

Указывает, что должен заменить шаблон. По умолчанию false.

  • true - шаблон заменит элемент директивы.
  • false - шаблон заменит содержимое элемента директивы.

Процесс замены мигрирует все атрибуты/классы со старого элемента на новый. См. Руководство по директивам для примера.

Существует очень мало случаев, когда требуется замена элементов для работы приложения, основной из них — это повторно используемые пользовательские компоненты, которые используются в контекстах SVG (потому что SVG не работает с пользовательскими элементами в дереве DOM).

transclude

Извлечь содержимое элемента, где появляется директива, и сделать его доступным для директивы. Содержимое компилируется и предоставляется директиве в качестве функции трансляции. См. раздел Трансляция ниже.

compile

function compile(tElement, tAttrs, transclude) { ... }

Функция compile занимается преобразованием DOM шаблона. Поскольку большинство директив не производят преобразования шаблона, она часто не используется. Функция compile принимает следующие аргументы:

  • tElement - элемент шаблона - Элемент, где была объявлена директива. Безопасно производить преобразование шаблона только над элементом и дочерними элементами.

  • tAttrs - атрибуты шаблона - Нормализованный список атрибутов, объявленных в этом элементе, общий для всех функций compile директив.

  • transclude - [УСТАРЕЛО!] Функция связи трансляции: function(scope, cloneLinkingFn)

Примечание: экземпляр шаблона и экземпляр ссылки могут быть разными объектами, если шаблон был клонирован. По этой причине небезопасно выполнять какие-либо действия, кроме преобразований DOM, которые применяются ко всем клонированным узлам DOM в функции compile. В частности, регистрация обработчиков событий DOM должна выполняться в функции link, а не в функции compile.
Примечание: функция compile не может обрабатывать директивы, которые рекурсивно используют себя в собственных шаблонах или функциях compile. Компиляция таких директив приводит к бесконечному циклу и ошибкам переполнения стека. Этому можно избежать, вручную используя $compile в функции postLink для императивной компиляции шаблона директивы вместо полагания на автоматическую компиляцию шаблона через template или templateUrl объявление или ручную компиляцию внутри функции compile.
Примечание: функция transclude, которая передаётся в функцию compile, устарела, поскольку, например, не знает правильной внешней области видимости. Пожалуйста, используйте функцию transclude, которая передаётся в функцию link вместо неё.

Функция compile может возвращать значение, которое может быть либо функцией, либо объектом.

  • возвращение функции (post-link) - эквивалентно регистрации функции link через свойство link объекта конфигурации, когда функция compile пустая.

  • возвращение объекта с зарегистрированными функциями через pre и post свойства - позволяет управлять тем, когда функция link должна вызываться во время фазы link. См. информацию о функциях pre-link и post-link ниже.

link

Это свойство используется только если свойство compile не определено.

function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }

Функция link отвечает за регистрацию обработчиков событий DOM и обновление DOM. Она выполняется после клонирования шаблона. Именно здесь будет размещена основная логика директивы.

  • scope - Область видимости - Область видимости, используемая директивой для регистрации наблюдателей.

  • iElement - элемент экземпляра - Элемент, где используется директива. Безопасно изменять потомков элемента только в функции postLink, так как потомки уже связаны.

  • iAttrs - атрибуты экземпляра - Нормализованный список атрибутов, объявленных в этом элементе, общий для всех функций связывания директивы.

  • controller - экземпляр(ы) контроллера, требуемый(е) директивой - Экземпляры разделяются между всеми директивами, что позволяет директивам использовать контроллеры как канал связи. Точное значение зависит от свойства require директивы:

    • не требуется(ют) контроллеры: собственный контроллер директивы или undefined, если его нет
    • string: экземпляр контроллера
    • array: массив экземпляров контроллеров

    Если требуемый контроллер не найден, а он необязателен, экземпляр null, иначе выбрасывается ошибка Отсутствует необходимый контроллер.

    Обратите внимание, что вы также можете потребовать собственный контроллер директивы — он будет доступен как любой другой контроллер.

  • transcludeFn - функция связывания трансклюзии, предварительно привязанная к правильной области видимости трансклюзии. Это то же самое, что и параметр $transclude контроллеров директивы, см. раздел о контроллере для получения подробностей. function([scope], cloneLinkingFn, futureParentElement).

Функция предварительного связывания

Выполняется до связывания дочерних элементов. Небезопасно выполнять преобразования DOM, так как функция связывания компилятора не сможет найти правильные элементы для связывания.

Функция пост-связывания

Выполняется после связывания дочерних элементов.

Обратите внимание, что дочерние элементы, содержащие директивы templateUrl не будут скомпилированы и связаны, поскольку они ожидают загрузки своего шаблона асинхронно, а их компиляция и связывание приостановлены до тех пор, пока это не произойдёт.

Безопасно выполнять преобразования DOM в функции пост-связывания для элементов, которые не ожидают разрешения своих асинхронных шаблонов.

Трансклюзия

Трансклюзия — это процесс извлечения набора элементов DOM из одной части DOM и их копирования в другую часть DOM, сохраняя при этом их связь с исходной областью видимости AngularJS, из которой они были взяты.

Трансклюзия используется (часто с ngTransclude) для вставки исходного содержимого элемента директивы в указанное место в шаблоне директивы. Преимущество трансклюзии перед просто ручным перемещением элементов DOM заключается в том, что содержимое трансклюзии имеет доступ к свойствам в области видимости, из которой оно было взято, даже если у директивы изолированная область видимости. См. Руководство по директивам.

Это позволяет виджету иметь частное состояние для своего шаблона, в то время как содержимое трансклюзии имеет доступ к своей исходной области видимости.

Примечание: При тестировании директивы трансклюзии элемента не следует размещать директиву в корне фрагмента DOM, который компилируется. См. Тестирование директив трансклюзии.

Существует три вида трансклюзии в зависимости от того, хотите ли вы трансклюдировать только содержимое элемента директивы, весь элемент или несколько частей содержимого элемента:

  • true - трансклюдировать содержимое (т.е. дочерние узлы) элемента директивы.
  • 'element' - трансклюдировать весь элемент директивы, включая любые директивы в этом элементе, определённые с более низким приоритетом, чем эта директива. При использовании свойство template игнорируется.
  • {...} (хеш объекта): - сопоставить элементы содержимого с "слотами" трансклюзии в шаблоне.

Трансклюзия с несколькими слотами объявляется путём предоставления объекта для свойства transclude.

Этот объект является отображением, где ключи — это имя слота для заполнения, а значение — это селектор элемента, используемый для соответствия HTML слоту. Селектор элемента должен быть в нормализованной форме (например, myElement) и будет соответствовать стандартным вариантам элементов (например, my-element, my:element, data-my-element, и т. д.).

Для получения дополнительной информации ознакомьтесь с руководством по Сопоставлению директив

Если префикс селектора элемента ? , то этот слот необязателен.

Например, объект трансклюзии { slotA: '?myCustomElement' } сопоставляет элементы <my-custom-element> со слотом slotA, к которому можно получить доступ через функцию $transclude или через директиву ngTransclude.

Слоты, которые не помечены как необязательные (?) приведут к ошибке во время компиляции, если в содержимом трансклюзии нет соответствующих элементов. Если вы хотите узнать, был ли необязательный слот заполнен содержимым, вы можете вызвать $transclude.isSlotFilled(slotName) на функции трансклюзии, переданной в функцию связывания директивы и инжектируемой в контроллер директивы.

Функции трансклюзии

Когда директива запрашивает трансклюзию, компилятор извлекает её содержимое и предоставляет функцию трансклюзии функции link и controller директивы. Эта функция трансклюзии — специальная функция связывания, которая вернёт скомпилированное содержимое, связанное с новой областью видимости трансклюзии.

Если вы просто используете ngTransclude, то вам не нужно беспокоиться об этой функции, так как ngTransclude позаботится об этом за вас.

Если вы хотите вручную управлять вставкой и удалением содержимого трансклюзии в вашей директиве, то вы должны использовать эту функцию трансклюзии. При вызове функции трансклюзии она возвращает объект jqLite/jQuery, содержащий скомпилированный DOM, который связан с правильной областью видимости трансклюзии.

При вызове функции трансклюзии вы можете передать функцию прикрепления клона. Эта функция принимает два параметра, function(clone, scope) { ... }, где clone — свежая скомпилированная копия вашего содержимого трансклюзии, а scope — новая созданная область видимости трансклюзии, к которой будет связан клон.

Лучшая практика: всегда предоставляйте cloneFn (функцию прикрепления клона) при вызове функции трансклюзии, так как тогда вы получаете свежий клон исходного DOM и также имеете доступ к новой области видимости трансклюзии.

Обычной практикой является прикрепление вашего содержимого трансклюзии (clone) к DOM внутри вашей функции прикрепления клона:

var transcludedContent, transclusionScope;

$transclude(function(clone, scope) {
  element.append(clone);
  transcludedContent = clone;
  transclusionScope = scope;
});

Позже, если вы хотите удалить содержимое трансклюзии из вашего DOM, то вы также должны уничтожить связанную область видимости трансклюзии:

transcludedContent.remove();
transclusionScope.$destroy();
Лучшая практика: если вы намерены добавлять и удалять содержимое трансклюзии вручную в вашей директиве (вызывая функцию трансклюзии для получения DOM и вызывая element.remove() для удаления), то вы также несете ответственность за вызов $destroy для области видимости трансклюзии.

Встроенные директивы манипулирования DOM, такие как ngIf, ngSwitch и ngRepeat автоматически уничтожают свои клоны трансклюзии по мере необходимости, поэтому вам не нужно беспокоиться об этом, если вы просто используете ngTransclude для вставки трансклюзии в вашу директиву.

Области видимости трансклюзии

При вызове функции трансклюзии возвращается фрагмент DOM, предварительно привязанный к области видимости трансклюзии. Эта область видимости специальная, так как она является дочерней по отношению к области видимости директивы (и, следовательно, уничтожается при уничтожении области видимости директивы), но наследует свойства области видимости, из которой она была взята.

Например, рассмотрим директиву, которая использует трансклюзию и изолированную область видимости. Иерархия DOM может выглядеть так:

<div ng-app>
  <div isolate>
    <div transclusion>
    </div>
  </div>
</div>

Иерархия областей видимости $parent будет выглядеть так:

- $rootScope
  - isolate
    - transclusion

но области видимости будут наследоваться протопически от разных областей видимости до их $parent.

- $rootScope
  - transclusion
- isolate

Атрибуты

Объект Атрибутов — передаётся в качестве параметра в функциях link() или compile(). Он имеет множество применений.

  • Доступ к нормализованным именам атрибутов: Директивы, такие как 'ngBind', могут быть выражены различными способами: 'ng:bind', data-ng-bind, или 'x-ng-bind'. Объект атрибутов позволяет получить нормализованный доступ к атрибутам.

  • Взаимодействие между директивами: Все директивы делят один экземпляр объекта атрибутов, что позволяет директивам использовать объект атрибутов для взаимодействия между директивами.

  • Поддержка интерполяции: Атрибуты интерполяции назначаются объекту атрибутов, что позволяет другим директивам читать интерполированное значение.

  • Наблюдение за изменениями интерполированных атрибутов: Используйте $observe для наблюдения за изменениями значения атрибутов, содержащих интерполяцию (например, src="{{bar}}"). Это не только очень эффективно, но и единственный способ легко получить фактическое значение, поскольку на этапе связывания интерполяция ещё не вычислена, и поэтому значение в это время установлено в undefined.

function linkingFn(scope, elm, attrs, ctrl) {
  // get the attribute value
  console.log(attrs.ngModel);

  // change the attribute
  attrs.$set('ngModel', 'new value');

  // observe changes to interpolated attribute
  attrs.$observe('ngModel', function(value) {
    console.log('ngModel has changed value to ' + value);
  });
}
Примечание: Обычно директивы регистрируются с помощью module.directive. Приведённый ниже пример демонстрирует, как работает $compile.

Известные проблемы

Двойная компиляция

Двойная компиляция возникает, когда уже скомпилированная часть DOM компилируется повторно. Это нежелательный эффект, который может привести к некорректному поведению директив, проблемам производительности и утечкам памяти. Обратитесь к разделу руководства по компилятору о двойной компиляции для подробного объяснения и способов её предотвращения.

Использование

$compile(element, transclude, maxPriority);

Аргументы

Параметр Тип Подробности
element stringDOMElement

Элемент или строка HTML, которые нужно скомпилировать в функцию шаблона.

transclude function(angular.Scope, cloneAttachFn=)

Функция, доступная для директив — УСТАРЕВШАЯ.

Примечание: Передача функции transclude в функцию $compile устарела, так как она, например, не будет использовать правильную внешнюю область видимости. Пожалуйста, передайте функцию transclude как parentBoundTranscludeFn в функцию link вместо этого.
maxPriority number

Применить только директивы с приоритетом ниже заданного (только влияет на корневой(е) элемент(ы), а не на их потомков)

Возвращаемое значение

function(scope, cloneAttachFn=, options=)

Функция link, которая используется для связывания шаблона (элемента/дерева DOM) со scope. Где:

  • scope - Scope для привязки.
  • cloneAttachFn - Если cloneAttachFn предоставлено, то функция link скопирует template и вызовет функцию cloneAttachFn, позволяя вызывающему элементу прикрепить скопированные элементы к документу DOM в нужном месте. Функция cloneAttachFn вызывается следующим образом:
    cloneAttachFn(clonedElement, scope) где:

    • clonedElement - копия исходного element, переданного в компилятор.
    • scope - текущий scope, с которым работает функция связывания.
  • options - Необязательный объект со значениями для связывания. Если options предоставлено, можно использовать следующие ключи для управления поведением связывания:

    • parentBoundTranscludeFn - функция transclude, предоставляемая директивам; если она указана, она будет передана в функции link директив, найденных в element во время компиляции.
    • transcludeControllers - объект со значениями, где ключи соответствуют именам контроллеров, а значения - объекту с ключом instance, который соответствует экземпляру контроллера; если предоставлено, это позволит сделать контроллеры доступными для директив в compileNode:
      {
        parent: {
          instance: parentControllerInstance
        }
      }
      
    • futureParentElement - определяет родителя, к которому cloneAttachFn добавит скопированные элементы; необходимо только для transcludes, которые могут содержать не-HTML-элементы (например, SVG-элементы). См. также свойство directive.controller.

Вызов функции связывания возвращает элемент шаблона. Это либо исходный элемент, переданный, либо копия элемента, если cloneAttachFn предоставлен.

После связывания представление не обновляется до вызова $digest, который обычно выполняется AngularJS автоматически.

Если вам нужен доступ к связанному представлению, есть два способа:

  • Если вы не просите функцию связывания скопировать шаблон, создайте элемент(ы) DOM до передачи их в компилятор и сохраните эту ссылку.

    var element = $compile('<p>{{total}}</p>')(scope);
    
  • Если, с другой стороны, вам нужно скопировать элемент, ссылка на представление в исходном примере не будет указывать на копию, а скорее на исходный шаблон, который был скопирован. В этом случае вы можете получить доступ к копии через cloneAttachFn:

    var templateElement = angular.element('<p>{{total}}</p>'),
        scope = ....;
    
    var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
      //attach the clone to DOM document at the right place
    });
    
    //now we have reference to the cloned DOM via `clonedElement`
    

Дополнительную информацию о работе компилятора см. в разделе «Компилятор HTML AngularJS» руководства разработчика.

© 2010–2018 Google, Inc.
Licensed under the Creative Commons Attribution License 4.0.
https://code.angularjs.org/1.6.9/docs/api/ng/service/$compile

Spec-Zone.ru

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