angular-bootstrap-nav-tree
angular-bootstrap-nav-tree copied to clipboard
Incorrect working with lazy load data
..controller_start...
MyModel.get_tree(function(data){ $scope.example_treedata = data })
..controller_end...
In view: abn-tree header="" tree-data="example_treedata" icon-leaf="icon-file" on-select="my_default_handler(branch)" expand-level="2" initial-selection="2"></abn-tree
"Initial-selection" and "Exand-level" doesn't work, but tree is rendered.
This is true, but I am not sure what to do about it ( if anything ). The problem is that "expand-level" and "initial-selection" are only applied when the tree is first created. You can lazy-load the tree data, or you can modify or entirely replace the tree data at any time. How would the tree know when ( and whether ) it should apply the "initial-selection" and "expand-level" ? Maybe it should keep track of whether it is "empty", and then watch the tree data, and then, only when it changes from empty to not-empty, it could apply the "initial-selection" and "expand-level". Would that work?
I have tried, works like this (may be low-performance method, but it works for me):
used: scope.$watch('treeData', function (newval, oldval) {...})
var module;
module = angular.module('angularBootstrapNavTree', []);
module.directive('abnTree', function($timeout) {
return {
restrict: 'E',
templateUrl: 'abn_tree_template.html',
scope: {
treeData: '=',
onSelect: '&',
initialSelection: '=',
fieldSelection: '='
},
link: function(scope, element, attrs) {
var expand_level, for_each_branch, on_treeData_change, select_branch, selected_branch, on_initialSelection_change;
scope.$watch('treeData', function (newval, oldval) {
if (newval) {
if (attrs.iconExpand == null) {
attrs.iconExpand = 'icon-plus';
}
if (attrs.iconCollapse == null) {
attrs.iconCollapse = 'icon-minus';
}
if (attrs.iconLeaf == null) {
attrs.iconLeaf = 'icon-chevron-right';
}
if (attrs.expandLevel == null) {
attrs.expandLevel = '3';
}
if (attrs.fieldSelection == null) {
attrs.fieldSelection = 'label';
}
expand_level = parseInt(attrs.expandLevel, 10);
scope.header = attrs.header;
if (!scope.treeData) {
alert('no treeData defined for the tree!');
}
if (scope.treeData.length == null) {
if (treeData.label != null) {
scope.treeData = [treeData];
} else {
alert('treeData should be an array of root branches');
}
}
for_each_branch = function(f) {
var do_f, root_branch, _i, _len, _ref, _results;
do_f = function(branch, level) {
var child, _i, _len, _ref, _results;
f(branch, level);
if (branch.children != null) {
_ref = branch.children;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
_results.push(do_f(child, level + 1));
}
return _results;
}
};
_ref = scope.treeData;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
root_branch = _ref[_i];
_results.push(do_f(root_branch, 1));
}
return _results;
};
for_each_branch(function(b, level) {
b.level = level;
return b.expanded = b.level < expand_level;
});
selected_branch = null;
select_branch = function(branch) {
if (branch !== selected_branch) {
if (selected_branch != null) {
selected_branch.selected = false;
}
branch.selected = true;
selected_branch = branch;
if (branch.onSelect != null) {
return $timeout(function() {
return branch.onSelect(branch);
});
} else {
if (scope.onSelect != null) {
return $timeout(function() {
return scope.onSelect({
branch: branch
});
});
}
}
}
};
scope.user_clicks_branch = function(branch) {
if (branch !== selected_branch) {
return select_branch(branch);
}
};
on_initialSelection_change = function(newVal, oldVal){
if ( newVal ) {
attrs.initialSelection = newVal
if (attrs.initialSelection != null) {
for_each_branch(function(b) {
if (b[attrs.fieldSelection] == attrs.initialSelection) {
return select_branch(b);
}
});
}
}
};
scope.tree_rows = [];
on_treeData_change = function() {
var add_branch_to_list, root_branch, _i, _len, _ref, _results;
scope.tree_rows = [];
for_each_branch(function(branch) {
if (branch.children) {
if (branch.children.length > 0) {
return branch.children = branch.children.map(function(e) {
if (typeof e === 'string') {
return {
label: e,
children: []
};
} else {
return e;
}
});
}
} else {
return branch.children = [];
}
});
for_each_branch(function(b, level) {
if (!b.uid) {
return b.uid = "" + Math.random();
}
});
add_branch_to_list = function(level, branch, visible) {
var child, child_visible, tree_icon, _i, _len, _ref, _results;
if (branch.expanded == null) {
branch.expanded = false;
}
if (!branch.children || branch.children.length === 0) {
tree_icon = attrs.iconLeaf;
} else {
if (branch.expanded) {
tree_icon = attrs.iconCollapse;
} else {
tree_icon = attrs.iconExpand;
}
}
scope.tree_rows.push({
level: level,
branch: branch,
label: branch.label,
tree_icon: tree_icon,
visible: visible
});
if (branch.children != null) {
_ref = branch.children;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
child_visible = visible && branch.expanded;
_results.push(add_branch_to_list(level + 1, child, child_visible));
}
return _results;
}
};
_ref = scope.treeData;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
root_branch = _ref[_i];
_results.push(add_branch_to_list(1, root_branch, true));
}
return _results;
};
scope.$watch('initialSelection', on_initialSelection_change, true);
return scope.$watch('treeData', on_treeData_change, true);
}
});
}
};
});
@vmamaev This worked for me (for now), but I agree, this isn't ideal. This is an async issue I believe, unfortunately, over my head at this moment to fix.
Works for me, too :+1:
Hi,
Seems to me that atiertant's code in issue #16 does much of the job without the need to keep the old data, but as-is the branches will re-expand whenever the user tries to close a branch (because closing a branch changes the data and therefore we re-run the on_treeData_change).
I found that that wrapping atiertant's code in a check for tree_rows.length solved my problems with lazy-loaded data:
if (!scope.tree_loaded && scope.tree_rows.length > 0) {
scope.tree_loaded = true;
for_each_branch(function(b, level) {
b.level = level;
return b.expanded = b.level < expand_level;
});
if (attrs.initialSelection != null) {
for_each_branch(function(b) {
if (b.label === attrs.initialSelection) {
return select_branch(b);
}
});
}
}
Hi,
you are right ! i got a bug with user interaction ! but your code works only once if scope change before user click and don't change after ... i change my code to this:
on top of link function:
var user_action = false;
at the scope.user_clicks_branch function,i set user_action to true and add a function:
scope.user_clicks_branch = function(branch) {
user_action = true;
if (branch !== selected_branch) {
return select_branch(branch);
}
};
scope.user_expand_branch = function(branch) {
user_action = true;
branch.expanded = !branch.expanded;
};
the code at the end of the on_treeData_change function become:
if (!user_action) {
scope.tree_loaded = true;
for_each_branch(function(b, level) {
b.level = level;
return b.expanded = b.level < expand_level;
});
if (attrs.initialSelection != null) {
for_each_branch(function(b) {
if (b.label === attrs.initialSelection) {
return select_branch(b);
}
});
}
}
else {
user_action = true;
}
and the template to use the user_expand_branch function:
<ul class="nav nav-list nav-pills nav-stacked abn-tree">
<li ng-repeat="row in tree_rows | filter:{visible:true} track by row.branch.uid" ng-animate="'abn-tree-animate'" ng-class="'level-' + {{ row.level }} + (row.branch.selected ? ' active':'')" class="abn-tree-row">
<a ng-click="user_clicks_branch(row.branch)">
<i ng-class="row.tree_icon" ng-click="user_expand_branch(row.branch)" class="indented tree-icon"> </i>
<span class="indented tree-label">{{ row.label }}</span>
</a>
</li>
</ul>
Cool! Does the else user_action=true only run if it is already true though? Or is it just too late at night for me to think...
this is an error:
else {
user_action = false;
}
sorry !
I'm running into this issue as well. Any chance to have this fixed in master?
@atiertant 's solution works fine for me. Thanks!
I used ng-if check so that tree is initialized and rendered only when the data is loaded. It worked for me.
<abn-tree tree-data="my_data" ng-if="isInit" tree-control="my_tree" on-select="my_tree_handler(branch)" expand-level="2" initial-selection="Granny Smith"></abn-tree>
$scope.isInit=false;
setTimeout(function(){
$scope.my_data = treedata_avm;
$scope.isInit=true;
$scope.$apply('isInit');
},1000);
@CaiLifeng 's solution works fine for me. Thanks!
@CaiLifeng 's solution works fine for me too. But How I could set the initial-selection dynamically after to upload the data?
I figure out this problem by adding children to the branch when it is clicked.the solution could click this page. http://blog.csdn.net/baidu_35407267/article/details/52457779