Javascript AngularJS:如何创建一个transcluding元素指令来保留属性指令并可以添加新的指令?
这个问题我已经研究了两天了。我觉得应该简单得多 问题描述 我想创建一个指令,如下所示:Javascript AngularJS:如何创建一个transcluding元素指令来保留属性指令并可以添加新的指令?,javascript,angularjs,angularjs-directive,transclusion,Javascript,Angularjs,Angularjs Directive,Transclusion,这个问题我已经研究了两天了。我觉得应该简单得多 问题描述 我想创建一个指令,如下所示: <my-directive ng-something="something"> content </my-directive> click-count { display: block; border: solid 1px; background-color: lightgreen; font-weight: bold; margin: 5px; pa
<my-directive ng-something="something">
content
</my-directive>
click-count {
display: block;
border: solid 1px;
background-color: lightgreen;
font-weight: bold;
margin: 5px;
padding: 5px;
}
这是一些使用指令的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>
我对它可能有什么问题有一些想法,但我尝试了许多替代方法。如果我在链接器中乱搞,可能再次尝试
$compile
,控制器函数也必须调用两次。无论如何,最好举一个如何正确执行的例子。据我所知,您正在尝试修改DOM元素并使用属性添加一些指令。这意味着您的指令应该在所有其他指令运行之前运行。为了控制指令的执行顺序,Angular提供了优先级
属性。大多数指令在优先级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:Hi Vadim。谢谢你的解决方案。然而,我担心这对于这个示例来说太具体了,而我想找到一个通用的解决方案,例如,您正在复制ng单击
,而不是使用它。此外,您还更改了指令的外部接口:计数器是外部作用域的字段,可能对指令的用户隐藏了一个字段。这种逻辑应该局限于内部范围让我们一步一个脚印:如果我们假设不使用terminal:true
指令,那么问题可以解决吗?@mhelvens我添加了更通用的解决方案,使用指令,但不复制它们。它只能与ngRepeat
一起使用,但也可以轻松扩展以支持单个案例。祝您好运,谢谢您提出的有趣问题。第二种解决方案更好,因为它实际上使用了ng click
。我自己也试过类似的方法。问题是控制器功能(如果有)也运行了两次(包括任何副作用)。它仍然将内部状态暴露于外部。-尽管如此,我还是从你的代码中学到了一些有用的东西。例如,您没有使用transclude
:-)非常整洁。我只是觉得保持元素的内容总是有必要的。
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;
}
};
});
<!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>
click-count {
display: block;
border: solid 1px;
background-color: lightgreen;
font-weight: bold;
margin: 5px;
padding: 5px;
}
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
});
});
}
}
}
};
});
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
}
}
}
}
};
});