在Angular指令中递归
有几个受欢迎的递归角度指令Q&A在那里,这些问题都归结为以下解决方案之一:
第一个问题是你不能删除以前编译的代码,除非你理解了手动编译过程。 第二种方法存在的问题是...不是一个指令,而是错过了其强大的功能,但更迫切的是,它不能以与指令相同的方式进行参数化; 它只是绑定到一个新的控制器实例。
我一直在手动执行链接函数中的angular.bootstrap
或@compile()
,但是这给我留下了手动跟踪要移除和添加的元素的问题。
有没有一种好的方法来建立一个参数化的递归模式来管理添加/删除元素以反映运行时状态? 也就是说,具有添加/删除节点按钮的树和某个输入字段的值通过节点的子节点传递。 也许第二种方法与链式范围的组合(但我不知道如何做到这一点)?
受@ dnc253提到的线程描述的解决方案的启发,我将递归功能抽象为一个服务。
module.factory('RecursionHelper', ['$compile', function($compile){
return {
/**
* Manually compiles the element, fixing the recursion loop.
* @param element
* @param [link] A post-link function, or an object with function(s) registered via pre and post properties.
* @returns An object containing the linking functions.
*/
compile: function(element, link){
// Normalize the link parameter
if(angular.isFunction(link)){
link = { post: link };
}
// Break the recursion loop by removing the contents
var contents = element.contents().remove();
var compiledContents;
return {
pre: (link && link.pre) ? link.pre : null,
/**
* Compiles and re-adds the contents
*/
post: function(scope, element){
// Compile the contents
if(!compiledContents){
compiledContents = $compile(contents);
}
// Re-add the compiled contents to the element
compiledContents(scope, function(clone){
element.append(clone);
});
// Call the post-linking function, if any
if(link && link.post){
link.post.apply(null, arguments);
}
}
};
}
};
}]);
其用法如下:
module.directive("tree", ["RecursionHelper", function(RecursionHelper) {
return {
restrict: "E",
scope: {family: '='},
template:
'<p>{{ family.name }}</p>'+
'<ul>' +
'<li ng-repeat="child in family.children">' +
'<tree family="child"></tree>' +
'</li>' +
'</ul>',
compile: function(element) {
// Use the compile function from the RecursionHelper,
// And return the linking function(s) which it returns
return RecursionHelper.compile(element);
}
};
}]);
看到这个Plunker的演示。 我最喜欢这个解决方案,因为:
手动添加元素并编译它们绝对是一种完美的方法。 如果您使用ng-repeat,那么您将不必手动删除元素。
演示:http://jsfiddle.net/KNM4q/113/
.directive('tree', function ($compile) {
return {
restrict: 'E',
terminal: true,
scope: { val: '=', parentData:'=' },
link: function (scope, element, attrs) {
var template = '<span>{{val.text}}</span>';
template += '<button ng-click="deleteMe()" ng-show="val.text">delete</button>';
if (angular.isArray(scope.val.items)) {
template += '<ul class="indent"><li ng-repeat="item in val.items"><tree val="item" parent-data="val.items"></tree></li></ul>';
}
scope.deleteMe = function(index) {
if(scope.parentData) {
var itemIndex = scope.parentData.indexOf(scope.val);
scope.parentData.splice(itemIndex,1);
}
scope.val = {};
};
var newElement = angular.element(template);
$compile(newElement)(scope);
element.replaceWith(newElement);
}
}
});
我不确定这个解决方案是否在你链接的例子或相同的基本概念中找到,但我需要一个递归指令,并且我找到了一个好的,简单的解决方案。
module.directive("recursive", function($compile) {
return {
restrict: "EACM",
priority: 100000,
compile: function(tElement, tAttr) {
var contents = tElement.contents().remove();
var compiledContents;
return function(scope, iElement, iAttr) {
if(!compiledContents) {
compiledContents = $compile(contents);
}
iElement.append(
compiledContents(scope,
function(clone) {
return clone; }));
};
}
};
});
module.directive("tree", function() {
return {
scope: {tree: '='},
template: '<p>{{ tree.text }}</p><ul><li ng-repeat="child in tree.children"><recursive><span tree="child"></span></recursive></li></ul>',
compile: function() {
return function() {
}
}
};
});
您应该创建recursive
指令,然后将其包装在进行递归调用的元素周围。
上一篇: Recursion in Angular directives
下一篇: How do I write a service that return true/false in AngularJS?