[英]How to Create recursive Angular.js Templates without isolating scope?
我正在嘗試在Angular.js中表示一個遞歸數據結構。 此處提供了一個簡化的演示:
http://plnkr.co/edit/vsUHLYMfI4okbiVlCK7O?p=preview
在預覽中,我具有以下用於遞歸對象的HTML:
<ul>
<li ng-repeat="person in people">
<span ng-click="updateClicks(person)">{{person.name}}</span>
<ul>
<li ng-repeat="kid in person.kids">
<span ng-click="updateClicks(kid)">{{kid.name}}</span>
</li>
</ul>
</li>
</ul>
在我的應用程序中,視圖要復雜得多。 我想要一種以遞歸方式為每個人生成模板html的方法。 我嘗試使用指令執行此操作,但是當我沒有隔離范圍時,便遇到了無限循環的問題。 當我做了隔離范圍,我不再能夠調用到綁定的控制器功能(在這個例子中, updateClicks
功能,但是在我的應用程序有幾個)。
如何為這些對象遞歸生成html,並且仍然能夠調用屬於控制器的函數?
我認為最好的方法是使用$ emit。
假設您的遞歸指令如下所示:
directive('person', function($compile){
return{
restrict: 'A',
link: function(scope, element, attributes){
//recursive bit, if we've got kids, compile & append them on
if(scope.person.kids && angular.isArray(scope.person.kids)) {
$compile('<ul><li ng-repeat="kid in person.kids" person="kid"></li></ul>')(scope, function(cloned, scope){
element.find('li').append(cloned);
});
}
},
scope:{
person:'='
},
template: '<li><span ng-click="$emit(\'clicked\', person)">{{person.name}}</span></li>'
}
});
注意ng-click="$emit(clicked, person)"
代碼,不要分散\\的使用,這只是為了逃避。 $ scope。$ emit會在整個作用域鏈中一直發送一個事件,因此在您的控制器中,您單擊的函數基本上保持不變,但是現在您正在監聽該事件,而不是被ng-click觸發。
$scope.$on('clicked', function(event, person){
person.clicks++;
alert(person.name + ' has ' + person.clicks + ' clicks!');
});
很酷的事情是,事件對象甚至具有與遞歸指令隔離的作用域。
這是可以正常使用的plnkr: http ://plnkr.co/edit/3z8OXOeB5FhWp9XAW58G?p=preview甚至降到了第三級,以確保遞歸有效。
帶有角度指令的遞歸樹沒有作用域隔離,迫使您通過在每個深度級別使用不同的作用域屬性來模擬隔離。
我找不到任何東西,所以我寫了自己的。
假設您的HTML是:
<body ng-app="App" ng-controller="AppCtrl">
<div test="tree.children" test-label="tree.label">{{b}}</div>
</body>
然后,您有一個主模塊和一個控制器,將一個樹添加到范圍:
var App = angular.module('App', []);
App.controller('AppCtrl', function($scope, $timeout) {
// prodive a simple tree
$scope.tree = {
label: 'A',
children: [
{
label: 'a',
children: [
{ label: '1' },
{ label: '2' }
]
},
{
label: 'b',
children: [
{ label: '1' },
{ label: '2' }
]
}
]
};
// test that pushing a child in the tree is ok
$timeout(function() {
$scope.tree.children[1].children.push({label: 'c'});
},2000);
$timeout(function() {
// test that changing a label is ok
$scope.tree.children[1].label = 'newLabel';
},4000);
});
最后考慮指令test
的以下實現:
App.directive('test', function($compile) {
// use an int to suffix scope properties
// so that inheritance does not cause infinite loops anymore
var inc = 0;
return {
restrict: 'A',
compile: function(element, attr) {
// prepare property names
var prop = 'test'+(++inc),
childrenProp = 'children_'+prop,
labelProp = 'label'+prop,
childProp = 'child_'+prop;
return function(scope, element, attr) {
// create a child scope
var childScope = scope.$new();
function observeParams() {
// eval attributes in current scope
// and generate html depending on the type
var iTest = scope.$eval(attr.test),
iLabel = scope.$eval(attr.testLabel),
html = typeof iTest === 'object' ?
'<div>{{'+labelProp+'}}<ul><li ng-repeat="'+childProp+' in '+childrenProp+'"><div test="'+childProp+'.children" test-label="'+childProp+'.label">{{'+childProp+'}}</div></li></ul></div>'
: '<div>{{'+labelProp+'}}</div>';
// set scope values and references
childScope[childrenProp]= iTest;
childScope[labelProp]= iLabel;
// fill html
element.html(html);
// compile the new content againts child scope
$compile(element.contents())(childScope);
}
// set watchers
scope.$watch(attr.test, observeParams);
scope.$watch(attr.testLabel, observeParams);
};
}
};
});
所有的解釋都在注釋中。
您可以看看JSBin 。
我的實現方式當然可以改善。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.