如何在 normalized/denormalize 属性名称之前获取原始属性名称?

How to get the original attribute name before it was normalized/denormalize an attribute name?

我想创建一个我可以在 <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 它。


效果很好。但请注意我用 ATTENTION 评论的行:这假设用户使用 <select select-the-list>,然后将其规范化为 selectTheList 并使用此指令。然而,根据 the directive docs:

Angular normalizes an element's tag and attribute name to determine which elements match which directives. We typically refer to directives by their case-sensitive camelCase normalized name (e.g. ngModel). However, since HTML is case-insensitive, we refer to directives in the DOM by lower-case forms, typically using dash-delimited attributes on DOM elements (e.g. ng-model).

The normalization process is as follows: [... snip ...]

For example, the following forms are all equivalent and match the ngBind directive:

<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 problem,这就是我提供所有这些上下文的原因。但如果这是一个很好的方法 - 在导致我的指令被调用的元素上找到实际属性的最佳方法是什么,这样我就可以在重新编译之前删除它?

angular 的创建者确实预见到了这一需求。传递给 link 函数的 attributes 不仅仅是一个映射,而是 $compile.directive.Attributes 的一个实例。它包含一个 $attr 属性:

Properties

$attr

A map of DOM element attribute names to the normalized name. This is needed to do reverse lookup from normalized name back to actual name.

因此,您的 ATTENTION 行应该是:

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);
    }
}