这两天我一直在研究这个问题。我觉得它应该简单得多。
问题描述
我想创建一个如下使用的指令:
<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
}
}
}
}
};
});
关于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/