当replace = true时,如何防止angular指令中的重复属性

时间:2014-11-22 09:06:18

标签: angularjs angularjs-directive

我发现指定replace: true的角度指令会将指令用法中的属性复制到模板呈现的输出中。如果模板包含相同的属性,则模板属性值和指令属性值将在最终输出中组合在一起。

指令用法:

<foo bar="one" baz="two"></foo>

指令:

.directive('foo', function() {
  return {
    restrict: 'E',
    replace: true,
    template: '<div bar="{{bar}}" baz="baz"></div>',
    scope: {
      bar: '@'
    },
    link: function(scope, element, attrs, parentCtrl) {
      scope.bar = scope.bar || 'bar';
    }
  };
})

输出:

<div bar="one " baz="two baz" class="ng-isolate-scope"></div>

bar="one "中的空格导致问题,baz中的多个值也是如此。有没有办法改变这种行为?我意识到我可以在我的指令中使用非冲突属性,并在输出中同时具有模板属性和非冲突属性。但我希望能够使用相同的属性名称,并更好地控制模板的输出。

我想我可以对linkelement.removeAttr()使用element.attr()方法。似乎应该有一个更好的解决方案。

最后,我意识到有人贬低remove: true,但有充分的理由保留它。在我的情况下,我需要它用于使用transclusion生成SVG标记的指令。详情请见此处: https://github.com/angular/angular.js/commit/eec6394a342fb92fba5270eee11c83f1d895e9fb

2 个答案:

答案 0 :(得分:7)

不,没有一种很好的声明方式告诉Angular在移植到模板时应该如何合并或操作x属性。

Angular实际上是从源到目标元素的一个直接的属性副本(有一些例外)并合并属性值。您可以在Angular编译器的mergeTemplateAttributes函数中看到此行为。

由于您无法更改该行为,因此您可以使用指令定义的compilelink属性来控制属性及其值。你最有可能在编译阶段而不是链接阶段进行属性操作更有意义,因为你希望这些属性准备就绪&#34;当任何链接功能运行时。

您可以这样做:

.directive('foo', function() {
  return {
    // ..
    compile: compile
    // ..
  };

  function compile(tElement, tAttrs) {
    // destination element you want to manipulate attrs on
    var destEl = tElement.find(...);

    angular.forEach(tAttrs, function (value, key) {
      manipulateAttr(tElement, destEl, key);
    })

    var postLinkFn = function(scope, element, attrs) {
      // your link function
      // ...
    }

    return postLinkFn;
  }

  function manipulateAttr(src, dest, attrName) {
    // do your manipulation
    // ...
  }
})

答案 1 :(得分:3)

知道如何合并值将会很有帮助。模板是优先级,元素还是需要某种合并?

由于缺乏我只能做出假设,下面的代码假定您要从元素上存在的模板中删除属性。

.directive('foo', function() {
    return {
        restrict: 'E',
        replace: true,
        template: function(element, attrs) {
            var template = '<div bar="{{bar}}" baz="baz"></div>';
            template = angular.element(template);
            Object.keys(attrs.$attr).forEach(function(attr) {\
                // Remove all attributes on the element from the template before returning it.
                template.removeAttr(attrs.$attr[attr]);
            });
            return template;
        },
        scope: {
          bar: '@'
        }
    };
})