Spec-Zone.ru › Angular.js 1.7

Улучшить эту документацию Посмотреть исходный код $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 без template или templateUrl не применит изолированный scope к своим дочерним элементам.

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

END_OF_DOCUMENT_MARKER
  • @ или @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. Часто желательно передавать данные из изолированной области видимости через выражение в родительскую область видимости. Это можно сделать, передав карту имён локальных переменных и их значений в функцию-обёртку fn. Например, если выражение — increment(amount), то мы можем указать значение amount, вызвав localFn как localFn({amount: 22}).

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

  • привязка необязательная: свойство не будет определено
  • привязка обязательная: свойство определено
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 может быть строкой, массивом или объектом:

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

Если свойство 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, которая передаётся функции компиляции, устарела, поскольку, например, она не знает о правильной внешней области видимости. Пожалуйста, используйте функцию трансклюзии, которая передаётся функции связывания вместо неё.

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

  • возвращение функции (после связывания) - эквивалентно регистрации функции связывания через свойство 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 контроллеров директив, см. раздел контроллера для получения подробностей. 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: элемент в корне шаблон замещения может иметь непредвиденные последствия

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

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

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

$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) для привязки.
  • cloneAttachFn - Если cloneAttachFn задано, тогда функция link клонирует template и вызовет функцию cloneAttachFn, позволяя вызывающей стороне прикрепить клонированные элементы к документу DOM в соответствующем месте. Функция cloneAttachFn вызывается как:
    cloneAttachFn(clonedElement, scope) где:

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

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

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

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

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

  • Если вы не просите функцию link клонировать шаблон, создайте 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`
    

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

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

Spec-Zone.ru

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