4

我已经在这个问题上工作了两天了。我觉得它应该简单得多。

问题描述

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

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

并作为输出:

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

自然它会有一个链接函数和控制器来做一些工作,但主要关注的是:

  • 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>

whereinternalFn将在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一次,控制器函数也必须调用两次。无论如何,将不胜感激如何正确执行此操作的示例。

4

1 回答 1

1

据我了解,您正在尝试修改 DOM 元素并使用属性添加一些指令。这意味着您的指令应该在所有其他指令运行之前运行。为了控制指令的执行顺序,Angular 提供了priority属性。大多数指令在 priority 上运行0,这意味着如果您的指令具有更高的优先级,它将在之前运行。但不幸ngRepeat的是,不仅有 priority 1000,而且还定义了 with 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

于 2014-02-20T17:48:57.490 回答