I'm using and am attempting to filter nodes that have not expanded. So I've modified this code to include AngularJS filter :
treeRepeat.html :
<p id="expand-collapse-all">
<a href="" ng-click="expandAll()">Expand all</a>
<a href="" ng-click="collapseAll()">Collapse all</a>
</p>
Filter : <input ng-model="myFilter" type="text">
<ul frang-tree>
<li frang-tree-repeat="node in treeData | filter:myFilter">
<div><span class="icon"
ng-class="{collapsed: node.collapsed, expanded: !node.collapsed}"
ng-show="node.children && node.children.length > 0"
ng-click="node.collapsed = !node.collapsed"></span>
<span class="label"
ng-class="{folder: node.children && node.children.length > 0}"
ng-bind="node.label"
ng-click="action(node)"></span>
</div>
<ul ng-if="!node.collapsed && node.children && node.children.length > 0"
frang-tree-insert-children="node.children | filter:myFilter"></ul>
</li>
</ul>
This works as expected if all of the tree nodes are expanded : Line 20 on controllers.js :
$scope.treeData = JSON.parse("[ { \"label\": \"root\", \"children\": [ { \"label\": \"folder A\", \"collapsed\": true, \"children\": [ { \"label\": \"folder B\", \"collapsed\": true, \"children\": [ { \"label\": \"file B1\", \"collapsed\": true }, { \"label\": \"file B2\", \"collapsed\": true } ] }, { \"label\": \"file A1\", \"collapsed\": true }, { \"label\": \"file A2\", \"collapsed\": true }, { \"label\": \"file A3\", \"collapsed\": true }, { \"label\": \"file A4\", \"collapsed\": true } ] }, { \"label\": \"folder C\", \"collapsed\": true, \"children\": [ { \"label\": \"folder D\", \"collapsed\": true, \"children\": [ { \"label\": \"folder E\", \"collapsed\": true, \"children\": [ { \"label\": \"file E1\", \"collapsed\": true }, { \"label\": \"file E2\", \"collapsed\": true }, { \"label\": \"file E3\", \"collapsed\": true } ] } ] }, { \"label\": \"folder F\", \"collapsed\": true, \"children\": [ { \"label\": \"file F1\", \"collapsed\": true }, { \"label\": \"file F2\", \"collapsed\": true } ] }, { \"label\": \"file C1\", \"collapsed\": true } ] }, { \"label\": \"folder G\", \"collapsed\": true, \"children\": [ { \"label\": \"file G1\", \"collapsed\": true }, { \"label\": \"file G2\", \"collapsed\": true }, { \"label\": \"file G3\", \"collapsed\": true }, { \"label\": \"file G4\", \"collapsed\": true } ] }, { \"label\": \"folder H\", \"collapsed\": true, \"children\": [ { \"label\": \"file H1\", \"collapsed\": true }, { \"label\": \"file H2\", \"collapsed\": true }, { \"label\": \"file H3\", \"collapsed\": true } ] } ] } ]");
But if the nodes are collapsed then the matched nodes expanded / viewable. The tree remains collapsed. Config for collapsed nodes : Line 21 on controllers.js :
$scope.treeData = JSON.parse("[ { \"label\": \"root\", \"children\": [ { \"label\": \"folder A\", \"collapsed\": true, \"children\": [ { \"label\": \"folder B\", \"collapsed\": true, \"children\": [ { \"label\": \"file B1\", \"collapsed\": true }, { \"label\": \"file B2\", \"collapsed\": true } ] }, { \"label\": \"file A1\", \"collapsed\": true }, { \"label\": \"file A2\", \"collapsed\": true }, { \"label\": \"file A3\", \"collapsed\": true }, { \"label\": \"file A4\", \"collapsed\": true } ] }, { \"label\": \"folder C\", \"collapsed\": true, \"children\": [ { \"label\": \"folder D\", \"collapsed\": true, \"children\": [ { \"label\": \"folder E\", \"collapsed\": true, \"children\": [ { \"label\": \"file E1\", \"collapsed\": true }, { \"label\": \"file E2\", \"collapsed\": true }, { \"label\": \"file E3\", \"collapsed\": true } ] } ] }, { \"label\": \"folder F\", \"collapsed\": true, \"children\": [ { \"label\": \"file F1\", \"collapsed\": true }, { \"label\": \"file F2\", \"collapsed\": true } ] }, { \"label\": \"file C1\", \"collapsed\": true } ] }, { \"label\": \"folder G\", \"collapsed\": true, \"children\": [ { \"label\": \"file G1\", \"collapsed\": true }, { \"label\": \"file G2\", \"collapsed\": true }, { \"label\": \"file G3\", \"collapsed\": true }, { \"label\": \"file G4\", \"collapsed\": true } ] }, { \"label\": \"folder H\", \"collapsed\": true, \"children\": [ { \"label\": \"file H1\", \"collapsed\": true }, { \"label\": \"file H2\", \"collapsed\": true }, { \"label\": \"file H3\", \"collapsed\": true } ] } ] } ]");
Plunkr :
Do I need to manually expand the nodes as the user types or is there an angular config I can use to expand these nodes ?
I've tried adding a custom function that fires everytime user types :
function matchChildNode(objData , parentNode) {
angular.forEach(objData, function(childNode, key) {
var searchText = "";
//AngularJS does not initialise the searchText var until used. As the function is re-initialised for every node
//need to check if is undefined
if ($scope.searchText == undefined) {
searchText = ""
} else {
searchText = $scope.searchText
}
if (searchText.toLowerCase() === childNode.label.toLowerCase()) {
parentNode.collapsed = false
}
matchChildNode(childNode.children , childNode);
});
}
}
But this is very inefficient as it traverses entire tree structure for each keyword user types. This also just works for exactly matched text : searchText.toLowerCase() === childNode.label.toLowerCase()
. Have tried using contains
instead of ===
with no success.
plnkr src :
app.css (removed due to stackoverflow 30000 character limitation when asking questions)
directives.js (removed due to stackoverflow 30000 character limitation when asking questions)
filter.js :
'use strict';
angular.module('app.filters', []);
index.html :
<!doctype html>
<html lang="en" ng-app="app">
<head>
<meta charset="utf-8">
<title>treeRepeat demo</title>
<link rel="stylesheet" href="app.css"/>
</head>
<body>
<h1>treeRepeat</h1>
<div id="menu" ng-controller="MenuCtrl">
<ul>
<li ng-repeat="item in menu" ng-class="{selected: item == getCurrentMenuItem()}"><a href="#/{{item.index}}">{{item.shortLabel}}</a></li>
</ul>
<h2>{{getCurrentMenuItem().fullLabel}}</h2>
</div>
<ng-view></ng-view>
<script src="angular.js"></script>
<script src="angular-route.js"></script>
<script src="app.js"></script>
<script src="services.js"></script>
<script src="controllers.js"></script>
<script src="filters.js"></script>
<script src="directives.js"></script>
</body>
</html>
services.js :
'use strict';
angular.module('app.services', [])
.constant('menu', []);
treerepeat.html :
<p id="expand-collapse-all">
<a href="" ng-click="expandAll()">Expand all</a>
<a href="" ng-click="collapseAll()">Collapse all</a>
</p>
Filter : <input ng-model="myFilter" type="text">
<ul frang-tree>
<li frang-tree-repeat="node in treeData | filter:myFilter">
<div><span class="icon"
ng-class="{collapsed: node.collapsed, expanded: !node.collapsed}"
ng-show="node.children && node.children.length > 0"
ng-click="node.collapsed = !node.collapsed"></span>
<span class="label"
ng-class="{folder: node.children && node.children.length > 0}"
ng-bind="node.label"
ng-click="action(node)"></span>
</div>
<ul ng-if="!node.collapsed && node.children && node.children.length > 0"
frang-tree-insert-children="node.children | filter:myFilter"></ul>
</li>
</ul>
I'm using https://github./tchatel/angular-treeRepeat and am attempting to filter nodes that have not expanded. So I've modified this code to include AngularJS filter :
treeRepeat.html :
<p id="expand-collapse-all">
<a href="" ng-click="expandAll()">Expand all</a>
<a href="" ng-click="collapseAll()">Collapse all</a>
</p>
Filter : <input ng-model="myFilter" type="text">
<ul frang-tree>
<li frang-tree-repeat="node in treeData | filter:myFilter">
<div><span class="icon"
ng-class="{collapsed: node.collapsed, expanded: !node.collapsed}"
ng-show="node.children && node.children.length > 0"
ng-click="node.collapsed = !node.collapsed"></span>
<span class="label"
ng-class="{folder: node.children && node.children.length > 0}"
ng-bind="node.label"
ng-click="action(node)"></span>
</div>
<ul ng-if="!node.collapsed && node.children && node.children.length > 0"
frang-tree-insert-children="node.children | filter:myFilter"></ul>
</li>
</ul>
This works as expected if all of the tree nodes are expanded : Line 20 on controllers.js :
$scope.treeData = JSON.parse("[ { \"label\": \"root\", \"children\": [ { \"label\": \"folder A\", \"collapsed\": true, \"children\": [ { \"label\": \"folder B\", \"collapsed\": true, \"children\": [ { \"label\": \"file B1\", \"collapsed\": true }, { \"label\": \"file B2\", \"collapsed\": true } ] }, { \"label\": \"file A1\", \"collapsed\": true }, { \"label\": \"file A2\", \"collapsed\": true }, { \"label\": \"file A3\", \"collapsed\": true }, { \"label\": \"file A4\", \"collapsed\": true } ] }, { \"label\": \"folder C\", \"collapsed\": true, \"children\": [ { \"label\": \"folder D\", \"collapsed\": true, \"children\": [ { \"label\": \"folder E\", \"collapsed\": true, \"children\": [ { \"label\": \"file E1\", \"collapsed\": true }, { \"label\": \"file E2\", \"collapsed\": true }, { \"label\": \"file E3\", \"collapsed\": true } ] } ] }, { \"label\": \"folder F\", \"collapsed\": true, \"children\": [ { \"label\": \"file F1\", \"collapsed\": true }, { \"label\": \"file F2\", \"collapsed\": true } ] }, { \"label\": \"file C1\", \"collapsed\": true } ] }, { \"label\": \"folder G\", \"collapsed\": true, \"children\": [ { \"label\": \"file G1\", \"collapsed\": true }, { \"label\": \"file G2\", \"collapsed\": true }, { \"label\": \"file G3\", \"collapsed\": true }, { \"label\": \"file G4\", \"collapsed\": true } ] }, { \"label\": \"folder H\", \"collapsed\": true, \"children\": [ { \"label\": \"file H1\", \"collapsed\": true }, { \"label\": \"file H2\", \"collapsed\": true }, { \"label\": \"file H3\", \"collapsed\": true } ] } ] } ]");
But if the nodes are collapsed then the matched nodes expanded / viewable. The tree remains collapsed. Config for collapsed nodes : Line 21 on controllers.js :
$scope.treeData = JSON.parse("[ { \"label\": \"root\", \"children\": [ { \"label\": \"folder A\", \"collapsed\": true, \"children\": [ { \"label\": \"folder B\", \"collapsed\": true, \"children\": [ { \"label\": \"file B1\", \"collapsed\": true }, { \"label\": \"file B2\", \"collapsed\": true } ] }, { \"label\": \"file A1\", \"collapsed\": true }, { \"label\": \"file A2\", \"collapsed\": true }, { \"label\": \"file A3\", \"collapsed\": true }, { \"label\": \"file A4\", \"collapsed\": true } ] }, { \"label\": \"folder C\", \"collapsed\": true, \"children\": [ { \"label\": \"folder D\", \"collapsed\": true, \"children\": [ { \"label\": \"folder E\", \"collapsed\": true, \"children\": [ { \"label\": \"file E1\", \"collapsed\": true }, { \"label\": \"file E2\", \"collapsed\": true }, { \"label\": \"file E3\", \"collapsed\": true } ] } ] }, { \"label\": \"folder F\", \"collapsed\": true, \"children\": [ { \"label\": \"file F1\", \"collapsed\": true }, { \"label\": \"file F2\", \"collapsed\": true } ] }, { \"label\": \"file C1\", \"collapsed\": true } ] }, { \"label\": \"folder G\", \"collapsed\": true, \"children\": [ { \"label\": \"file G1\", \"collapsed\": true }, { \"label\": \"file G2\", \"collapsed\": true }, { \"label\": \"file G3\", \"collapsed\": true }, { \"label\": \"file G4\", \"collapsed\": true } ] }, { \"label\": \"folder H\", \"collapsed\": true, \"children\": [ { \"label\": \"file H1\", \"collapsed\": true }, { \"label\": \"file H2\", \"collapsed\": true }, { \"label\": \"file H3\", \"collapsed\": true } ] } ] } ]");
Plunkr : https://plnkr.co/edit/CtXlRfdreolTTc018c0A?p=preview
Do I need to manually expand the nodes as the user types or is there an angular config I can use to expand these nodes ?
I've tried adding a custom function that fires everytime user types :
function matchChildNode(objData , parentNode) {
angular.forEach(objData, function(childNode, key) {
var searchText = "";
//AngularJS does not initialise the searchText var until used. As the function is re-initialised for every node
//need to check if is undefined
if ($scope.searchText == undefined) {
searchText = ""
} else {
searchText = $scope.searchText
}
if (searchText.toLowerCase() === childNode.label.toLowerCase()) {
parentNode.collapsed = false
}
matchChildNode(childNode.children , childNode);
});
}
}
But this is very inefficient as it traverses entire tree structure for each keyword user types. This also just works for exactly matched text : searchText.toLowerCase() === childNode.label.toLowerCase()
. Have tried using contains
instead of ===
with no success.
plnkr src :
app.css (removed due to stackoverflow 30000 character limitation when asking questions)
directives.js (removed due to stackoverflow 30000 character limitation when asking questions)
filter.js :
'use strict';
angular.module('app.filters', []);
index.html :
<!doctype html>
<html lang="en" ng-app="app">
<head>
<meta charset="utf-8">
<title>treeRepeat demo</title>
<link rel="stylesheet" href="app.css"/>
</head>
<body>
<h1>treeRepeat</h1>
<div id="menu" ng-controller="MenuCtrl">
<ul>
<li ng-repeat="item in menu" ng-class="{selected: item == getCurrentMenuItem()}"><a href="#/{{item.index}}">{{item.shortLabel}}</a></li>
</ul>
<h2>{{getCurrentMenuItem().fullLabel}}</h2>
</div>
<ng-view></ng-view>
<script src="angular.js"></script>
<script src="angular-route.js"></script>
<script src="app.js"></script>
<script src="services.js"></script>
<script src="controllers.js"></script>
<script src="filters.js"></script>
<script src="directives.js"></script>
</body>
</html>
services.js :
'use strict';
angular.module('app.services', [])
.constant('menu', []);
treerepeat.html :
<p id="expand-collapse-all">
<a href="" ng-click="expandAll()">Expand all</a>
<a href="" ng-click="collapseAll()">Collapse all</a>
</p>
Filter : <input ng-model="myFilter" type="text">
<ul frang-tree>
<li frang-tree-repeat="node in treeData | filter:myFilter">
<div><span class="icon"
ng-class="{collapsed: node.collapsed, expanded: !node.collapsed}"
ng-show="node.children && node.children.length > 0"
ng-click="node.collapsed = !node.collapsed"></span>
<span class="label"
ng-class="{folder: node.children && node.children.length > 0}"
ng-bind="node.label"
ng-click="action(node)"></span>
</div>
<ul ng-if="!node.collapsed && node.children && node.children.length > 0"
frang-tree-insert-children="node.children | filter:myFilter"></ul>
</li>
</ul>
Share
Improve this question
edited Jan 4, 2016 at 16:06
blue-sky
asked Jan 4, 2016 at 15:59
blue-skyblue-sky
54k161 gold badges466 silver badges780 bronze badges
5
-
Not all scenario works in expanded nodes. Expand all and search
A
. Should work as if you searchfile A
. Right? – Valijon Commented Jan 6, 2016 at 17:34 - @Valijon yes, seems to work, only problem is that when user deletes all text from textbox the tree remains expanded, I should be able to check this scenario and just reload the tree : plnkr.co/edit/CtXlRfdreolTTc018c0A?p=preview – blue-sky Commented Jan 7, 2016 at 13:22
- @blue-sky Do you want to only filter on nodes that have not been expanded? – Tjaart van der Walt Commented Jan 8, 2016 at 9:11
- @TjaartvanderWalt I want to filter on all nodes and just expand the nodes that contain the matched text. – blue-sky Commented Jan 8, 2016 at 9:51
- @blue-sky please see my answer – Tjaart van der Walt Commented Jan 8, 2016 at 12:54
2 Answers
Reset to default 4 +100You can achieve this by using a filter with a predicate function.
function(value, index): A predicate function can be used to write arbitrary filters. The function is called for each element of array. The final result is an array of those elements that the predicate returned true for.
- Create a predicate function
$scope.getFilter = function(value, index){
if(!$scope.myFilter || $scope.myFilter === '' && (value.label === 'root')){
$scope.collapseAll();
return true;
}
if (findMatch(value, $scope.myFilter)){
value.collapsed = false;
return true;
}else{
value.collapsed = true;
return false;
}
}
- Create a function that checks if the current item is a match(this function is called from the predicate function)
function findMatch(value, filter) {
var found = false;
if(value.label.toLowerCase().indexOf(filter) > -1){
value.collapsed = false;
found = true;
}
if(!value.children || value.children.length===0){
if(value.collapsed!== undefined){
value.collapsed = true;
return found;
}
}
for (var i = 0; i < value.children.length; i++) {
if (value.children[i].label.toLowerCase().indexOf(filter) > -1) {
//match found
value.collapsed = false;
value.children[i].collapsed = false;
found = true;
} else {
//check child items
if(value.children[i].children && value.children[i].children.length>0)
{
if (findMatchingChildren(value.children[i].children, filter)) {
value.children[i].collapsed = false;
found = true;
}else{
value.children[i].collapsed = true;
}
}
}
}
return found;
}
3. Create a function that loops through the children items of the current item, this function calls the function in step 2. You end up with a recursive function that checks all the levels.
function findMatchingChildren(children, filter) {
var found = false;
for (var i = 0; i < children.length; i++) {
if (findMatch(children[i], filter)) {
children[i].collapsed = false;
found = true;
}else{
children[i].collapsed = true;
}
}
return found;
}
As you find matching items you set the collapsed
value to false
Please see working example here
HTML
<p id="expand-collapse-all">
<a href="" ng-click="expandAll()">Expand all</a>
<a href="" ng-click="collapseAll()">Collapse all</a>
</p>
Filter : <input ng-model="myFilter" type="text">
<ul frang-tree>
<li frang-tree-repeat="node in treeData | filter:getFilter">
<div>
<span class="icon"
ng-class="{collapsed: node.collapsed, expanded: !node.collapsed}"
ng-show="node.children && node.children.length > 0"
ng-click="node.collapsed = !node.collapsed"></span>
<span class="label"
ng-class="{folder: node.children && node.children.length > 0}"
ng-bind="node.label"
ng-click="action(node)"></span>
</div>
<ul ng-if="!node.collapsed && node.children && node.children.length>0" frang-tree-insert-children="node.children | filter:myFilter"></ul>
</li>
</ul>
Consider ditching the angular-treeRepeat
module altogether and using a recursive template. Ben Foster explains how it works in this blog post and it is a much more elegant (and less insanely plex) solution, I think.
Working example with filter: JSFiddle
HTML:
<div ng-app="app" ng-controller='AppCtrl'>
Filter: <input ng-model="myFilter" type="text">
<script type="text/ng-template" id="categoryTree">
{{ category.title }}
<ul ng-show="category.categories">
<li
ng-repeat="category in category.categories | filter:myFilter"
ng-include="'categoryTree'">
</li>
</ul>
</script>
<ul>
<li
ng-repeat="category in categories | filter:myFilter"
ng-include="'categoryTree'">
</li>
</ul>
</div>
JavaScript:
var app = angular.module('app', [])
.controller('AppCtrl', function($scope) {
$scope.categories = [{
title: 'Computers',
categories: [{
title: 'Laptops',
categories: [{
title: 'Ultrabooks'
},{
title: 'Macbooks'
}]
},{
title: 'Desktops'
},{
title: 'Tablets',
categories: [{
title: 'Apple'
},{
title: 'Android'
}]
}
]
},{
title: 'Printers'
}];
});
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745154414a4614030.html
评论列表(0条)