2

我想创建一个可以在<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并使用了该指令。但是,根据指令文档

Angular对元素的标签和属性名称进行规范化,以确定哪些元素与哪些指令匹配。我们通常通过区分大小写的驼峰式 规范化名称(例如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 问题,这就是我提供所有这些上下文的原因。但是,如果这是一个好方法 - 在元素上找到导致我的指令被调用的实际属性的最佳方法是什么,所以我可以在重新编译之前将其删除?

4

2 回答 2

2

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

特性

$attr

DOM 元素属性名称到规范化名称的映射。这是从规范化名称到实际名称的反向查找所必需的。

因此,您的ATTENTION行应该是:

el.removeAttr(attributes.$attr['selectTheList']);
于 2015-11-12T00:34:15.320 回答
1

这确实是 XY 问题,因为主题是避免递归编译。

有一些技巧可以处理这个问题。

其中之一是利用这样一个事实,即 DDO 对象的唯一可用位置thiscompile

...
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);
    }
}
于 2015-11-12T00:54:43.640 回答