Как получить оригинальное имя атрибута до его нормализации / денормализации имени атрибута?

Я хочу создать директиву, которую я могу использовать на <select> элементы, чтобы сказать это, чтобы заполнить <select> со всемирно известным списком, который динамически обновляется и распределяется между всеми компонентами приложения.

Я предполагаю использовать это так:

<select ng-model="listentry" select-the-list></select>

Вот как я пока это делаю:

.directive('selectTheList', function ($compile, ListData) {
    return {
        restrict: 'A',
        priority: 1000,
        terminal: true,
        link: function (scope, el, attributes) {
            var child = scope.$new();
            child.listData = ListData.theList;
            el.attr('ng-options', 'listItem.name for listItem in listData track by listItem.id');
            el.removeAttr('select-the-list'); /**** ATTENTION ****/
            $compile(el)(child);
        }
    };
});

То есть я назначаю ng-options атрибут, который делает то, что я хочу, на основе области, которую я установил для этой цели, а затем $compile Это.


Это прекрасно работает. Но обратите внимание на строку, которую я прокомментировал с ВНИМАНИЕМ: это предполагает, что пользователь использовал <select select-the-list> который затем нормализовался selectTheList и использовал эту директиву. Тем не менее, в соответствии с директивой документов:

Angular нормализует тег элемента и имя атрибута, чтобы определить, какие элементы соответствуют каким директивам. Обычно мы ссылаемся на директивы по их чувствительному к регистру нормализованному имени camelCase (например, ngModel). Однако, поскольку HTML не чувствителен к регистру, мы обращаемся к директивам в DOM с помощью строчных форм, обычно используя атрибуты с разделителями-тире в элементах DOM (например, ng-model).

Процесс нормализации выглядит следующим образом: [... snip ...]

Например, все следующие формы эквивалентны и соответствуют ngBind директива:

<div ng-controller="Controller">
  Hello <input ng-model='name'> <hr/>
  <span ng-bind="name"></span> <br/>
  <span ng:bind="name"></span> <br/>
  <span ng_bind="name"></span> <br/>
  <span data-ng-bind="name"></span> <br/>
  <span x-ng-bind="name"></span> <br/>
</div>

То есть, если пользователь делает <select select:the:list> тогда директива будет применена, element.removeAttr('select-the-list') не сработает, и я получу бесконечный цикл.


Это может быть проблемой XY, поэтому я предоставил весь этот контекст. Но если это хороший способ сделать это - как лучше всего найти фактический атрибут в элементе, который вызвал мою директиву, чтобы я мог удалить его перед повторной компиляцией?

2 ответа

Решение

Создатели angular действительно предполагали необходимость в этом. attributes перешел в ваш link Функция не просто карта, но экземпляр $compile.directive.Attributes, Содержит $attr имущество:

свойства

$ атр

Карта имен атрибутов элемента DOM к нормализованному имени. Это необходимо для обратного поиска от нормализованного имени до фактического имени.

Таким образом, ваша строка ВНИМАНИЕ должна быть:

el.removeAttr(attributes.$attr['selectTheList']);

Это действительно проблема XY, потому что субъект избегает рекурсивной компиляции.

Есть несколько хитростей, чтобы справиться с этим.

Одним из них является использование того факта, что единственное место, где объект DDO доступен как this является compile:

...
compile: function (element, attrs)  {
  attrs.$set(this.name, null);
  // so the appearance of 'compile' won't require nesting link fn
  return this.link;
},
link: ...

И изменение имени директивы или вставка ее кода не приведет к несоответствиям.

Другой является более общим и особенно полезным, если удаление атрибута по какой-то причине неприменимо или директива не является атрибутом:

link: function (scope, element) {
    ...
    if (element.data('$recursion')) {
        element.data('$recursion', false);
    } else {
        element.data('$recursion', true);
        $compile(element)(scope);
    }
}
Другие вопросы по тегам