Spec-Zone.ru › Angular.js 1.8

Улучшить эту документацию Просмотреть исходный код $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() - Вызывается для контроллера, когда содержащий его область видимости уничтожается. Используйте этот обработчик для освобождения внешних ресурсов, наблюдений и обработчиков событий. Обратите внимание, что компоненты вызывают свои обработчики $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 (по умолчанию): Для директивы не будет создана область видимости. Директива будет использовать область видимости родителя.

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

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

Объект хэш 'isolate' 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 и наоборот. Если выражение привязки не присваиваемое или если атрибут не является необязательным и не существует, при обнаружении изменений в локальном значении будет выброшено исключение ($compile:nonassign), так как синхронизация с родительской областью видимости будет невозможна.

    По умолчанию используется метод $watch для отслеживания изменений, и проверка на равенство основана на тождестве объектов. Однако, если в качестве выражения привязки передаётся литерал объекта или массив, проверка на равенство выполняется по значению (используя функцию angular.equals). Также возможно поверхностное наблюдение за оцениваемым значением с помощью $watchCollection: используйте =* или =*attr

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

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

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

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

    По умолчанию используется метод $watch для отслеживания изменений, и проверка на равенство основана на тождестве объектов. Также возможно поверхностное наблюдение за оцениваемым значением с помощью $watchCollection: используйте <* или <*attr

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

Все 4 типа привязок (@, =, <, и &) могут быть сделаны необязательными, добавив ? к выражению. Маркер должен стоять после режима и перед именем атрибута. См. ошибку Invalid Isolate Scope Definition для примеров определений. Это полезно для уточнения интерфейсов, предоставляемых директивами. Существует тонкое различие между необязательными и обязательными привязками, когда атрибут привязки не установлен:

  • привязка необязательная: свойство не будет определено
  • привязка обязательная: свойство будет определено
app.directive('testDir', function() {
  return {
    scope: {
      notoptional: '=',
      optional: '=?',
    },
    bindToController: true,
    controller: function() {
      this.$onInit = function() {
        console.log(this.hasOwnProperty('notoptional')) // true
        console.log(this.hasOwnProperty('optional')) // false
      }
    }
  }
})
Объединение директивы с различными определениями областей видимости

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

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

bindToController

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

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

После создания контроллера начальные значения привязок изолированной области видимости будут привязаны к свойствам контроллера. Вы можете получить доступ к этим привязанным значениям, после их инициализации, предоставив метод контроллера под названием $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 устарело в AngularJS и было удалено в новом Angular (v2+).

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

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

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

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

transclude

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

compile

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

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

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

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

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

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

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

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

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

link

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

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

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

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

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

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

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

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

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

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

  • transcludeFn - функция связывания transclude, предварительно привязанная к правильной области видимости трансклюзии. Это то же самое, что и параметр $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 компилируется снова. Это нежелательный эффект и может привести к неправильному поведению директив, проблемам производительности и утечкам памяти. Обратитесь к руководству по компилятору раздел о двойной компиляции для подробного объяснения и способов её предотвращения.

Проблемы с replace: true

Примечание: replace: true устарела и не рекомендуется к использованию, главным образом из-за проблем, перечисленных здесь. Она полностью удалена в новой Angular.

Значения атрибутов не сливаются

Когда директива replace сталкивается с одинаковым атрибутом в исходном и заменяющем узле, она просто дублирует атрибут и объединяет значения пробелом или с ; в случае атрибута style.

Original Node: <span class="original" style="color: red;"></span>
Replace Template: <span class="replaced" style="background: blue;"></span>
Result: <span class="original replaced" style="color: red; background: blue;"></span>

Это означает, что атрибуты, содержащие выражения AngularJS, не будут правильно слиты, например, ngShow или ngClass вызовут ошибку $parse:

Original Node: <span ng-class="{'something': something}" ng-show="!condition"></span>
Replace Template: <span ng-class="{'else': else}" ng-show="otherCondition"></span>
Result: <span ng-class="{'something': something} {'else': else}" ng-show="!condition otherCondition"></span>

См. вопрос #5695.

Директивы не дублируются перед компиляцией

Когда исходный узел и шаблон замены объявляют одни и те же директивы, они будут скомпилированы дважды, потому что компилятор не дублирует их. Во многих случаях это незаметно, но, например, ngModel будет дважды подключать $formatters и $parsers.

См. вопрос #2573.

трансклюдирование элемента в корне шаблона замены может иметь непредвиденные последствия

Когда у шаблона замены есть директива в корневом узле, которая использует transclude: element, например, ngIf или ngRepeat, структура DOM или наследование контекста могут быть неверными. См. следующие вопросы:

  • Неверный контекст на заменённом элементе: #9837
  • Разный DOM между template и templateUrl: #10612

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

$compile(element, transclude, maxPriority);

Аргументы

Параметр Тип Подробности
элемент 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 добавит клонированные элементы; требуется только для transclude, которые могут содержать не HTML-элементы (например, SVG-элементы). См. также свойство directive.controller.

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

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

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

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

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

    var templateElement = angular.element('<p>{{total}}</p>');
    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`.
    // NOTE: The `clonedElement` returned by the linking function is the same as the
    //       `clonedElement` passed to `cloneAttachFn`.
    

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

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

Spec-Zone.ru

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