javascript - Angular : How to create a transcluding element directive that keeps attribute directives and can add new ones?

标签 javascript angularjs angularjs-directive transclusion

这两天我一直在研究这个问题。我觉得它应该简单得多。

问题描述

我想创建一个如下使用的指令:

<my-directive ng-something="something">
    content
</my-directive>

并有输出:

<my-directive ng-something="something" ng-more="more">
    content
</my-directive>

自然地,它会有一个链接函数和 Controller 来做一些工作,但主要的问题是:

  • DOM 元素保留原始名称,以便可以应用直观的 css 样式,
  • 已经存在的属性指令继续正常工作,并且
  • 新的属性指令可以由元素指令本身添加。

例子

例如,假设我想创建一个元素,当它被点击时在内部做一些事情:

<click-count ng-repeat="X in ['A', 'B', 'C']"> {{ X }} </click-count>

可以编译成这样的东西:

<click-count ng-click="internalFn()"> A </click-count>
<click-count ng-click="internalFn()"> B </click-count>
<click-count ng-click="internalFn()"> C </click-count>

其中 internalFn 将在 clickCount 指令的内部范围内定义。

尝试

我的一个尝试是这个 Plunker:http://plnkr.co/edit/j9sUUS?p=preview

由于 Plunker 似乎已关闭,因此代码如下:

angular.module('app', []).directive('clickCount', function() {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    scope: {
      ccModel: '='
    },
    compile: function(dElement) {
      dElement.attr("ngClick", "ccModel = ccModel + 1");

      return function postLink($scope, iElement, iAttrs, controller, transclude) {
        transclude(function(cloned) { iElement.append(cloned); });
      };
    },
    controller: function ($scope) {
        $scope.ccModel = 0;
    }
  };
});

这是一些使用指令的 HTML:

<!DOCTYPE html>
<html>
<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.js"></script>
  <link rel="stylesheet" href="style.css" />
  <script src="script.js"></script>
</head>
<body ng-app="app">
  <hr> The internal 'ng-click' doesn't work:
  <click-count ng-repeat="X in ['A', 'B', 'C']" cc-model="counter">
    {{ X }}, {{ counter }}
  </click-count>
  <hr> But an external 'ng-click' does work:
  <click-count ng-repeat="X in ['A', 'B', 'C']" cc-model="bla" ng-init="counter = 0" ng-click="counter = counter + 1">
    {{ X }}, {{ counter }}
  </click-count>
  <hr>
</body>
</html>

并且因为元素保留了它的名字,所以 css 可以如下使用:

click-count {
  display: block;
  border: solid 1px;
  background-color: lightgreen;
  font-weight: bold;
  margin: 5px;
  padding: 5px;
}

对于它可能存在的问题,我有几个想法,但我已经尝试了多种替代方法。如果我在链接器中乱搞,也许再次尝试 $compile, Controller 函数也必须被调用两次。无论如何,我们将不胜感激如何正确执行此操作的示例。

最佳答案

据我了解,您正在尝试修改 DOM 元素并使用属性添加一些指令。这意味着您的指令应该在所有其他指令运行之前运行。为了控制指令的执行顺序,Angular 提供了 priority 属性。大多数指令在优先级 0 上运行,这意味着如果您的指令具有更高的优先级,它将在之前运行。但不幸的是ngRepeat不仅有优先级1000,而且还定义了terminal:true,这意味着一旦元素有ngRepeat 您不能在具有更高优先级的同一元素指令上指定。您可以添加属性和行为,但不能添加应在 ngRepeat 之前运行的指令。但是,ngClick 有一个解决方法:

angular.module('app', []).directive('clickCount', function() {
  return {
    restrict: 'E',
    replace: true,
    compile: function(tElement) {
      return {
        pre: function(scope, iElement) {
          iElement.attr('ng-click', 'counter = counter +1'); // <- Add attribute
        },
        post: function(scope, iElement) {
          iElement.on('click', function() { // <- Add behavior
            scope.$apply(function(){ // <- Since scope variables may be modified, don't forget to apply the scope changes
              scope.$eval(iElement.attr('ng-click')); // <- Evaluate expression defined in ng-click attribute in context of scope
            });
          });
        }
      }
    }
  };
});

JSBin:http://jsbin.com/sehobavo/1/edit

另一种解决方法是在不使用 ngRepeat 的情况下重新编译指令:

angular.module('app', []).directive('clickCount', function($compile) {
  return {
    restrict: 'E',
    replace: true,
    compile: function(tElement) {
      return {
        pre: function(scope, iElement) {
          if(iElement.attr('ng-repeat')) { // <- Avoid recursion
            iElement.attr('ng-click', 'counter = counter +1'); // <- Add custom attributes and directives
            iElement.removeAttr('ng-repeat'); // <- Avoid recursion
            $compile(iElement)(scope); // <- Recompile your element to make other directives work
          }
        }
      }
    }
  };
});

JSBin:http://jsbin.com/hucunuqu/4/edit

关于javascript - Angular : How to create a transcluding element directive that keeps attribute directives and can add new ones?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21913022/

相关文章:

JavaScript 抛出未定义的错误

javascript - 通过文件输入替换div背景

javascript - Angular - 锚定页内链接 - Flash 背景

html - ionic 如何打开 pdf 文件并在 ios 和 android 设备中查看

javascript - 大型 AngularJS 应用程序的页面加载性能问题

angularjs - ctrl.$parsers.unshift 抛出未定义的异常

javascript - AngularJS $rootScope 函数 ng-change

javascript - 如何在 Angular 翻译中传递参数

javascript - ng-repeat 拒绝执行

html - md-no-focus-style 在我的设置中无法正常工作