将过滤应用于层次结构
Apply filtering to a hierarchical structure
我构建了以下显示层次结构的 Angular 应用程序:
我正在尝试在此层次结构的顶部插入一个文本框。过滤底部的数据。已经尝试了几个带有过滤器的示例,但到目前为止运气不佳。
我想做的是在用户开始向文本框输入内容时利用 angular 绑定,动态展开和折叠层次结构并突出显示匹配项。
正在寻找解决此问题的最佳方法的建议。请注意,层次结构可能会变大并拥有大约 3000 条记录。
angular.module('HelloWorldApp', [])
.controller('HelloWorldController', function($scope) {
$scope.mp6Root = [];
$scope.mp6Data = [];
var data = [
{
"cls": "L2-013551",
"clsNm": "FASHION DOLLS",
"subCt": "L3-001793",
"subCtNm": "FASHION DOLLS AND ACCESSORIES",
"ct": "L4-000429",
"ctNm": "DOLLS GAMES PUZZLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006472",
"clsNm": "FASHION DOLL WITH ACCS",
"subCt": "L3-001793",
"subCtNm": "FASHION DOLLS AND ACCESSORIES",
"ct": "L4-000429",
"ctNm": "DOLLS GAMES PUZZLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-014668",
"clsNm": "ACTIVITIES",
"subCt": "L3-001793",
"subCtNm": "FASHION DOLLS AND ACCESSORIES",
"ct": "L4-000429",
"ctNm": "DOLLS GAMES PUZZLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-014667",
"clsNm": "STORAGE",
"subCt": "L3-001793",
"subCtNm": "FASHION DOLLS AND ACCESSORIES",
"ct": "L4-000429",
"ctNm": "DOLLS GAMES PUZZLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-014675",
"clsNm": "FASHION DOLL PLAYSET",
"subCt": "L3-001793",
"subCtNm": "FASHION DOLLS AND ACCESSORIES",
"ct": "L4-000429",
"ctNm": "DOLLS GAMES PUZZLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006476",
"clsNm": "ROLE PLAY FASHION AND TOY",
"subCt": "L3-001793",
"subCtNm": "FASHION DOLLS AND ACCESSORIES",
"ct": "L4-000429",
"ctNm": "DOLLS GAMES PUZZLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-014677",
"clsNm": "CORE PS FIGURE W PLAYSET",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006508",
"clsNm": "CORE PS MUSICAL INSTRUMENT",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-014788",
"clsNm": "WAGONS TOYS",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006536",
"clsNm": "RIDING TOYS FOOT TO FLOOR",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-014678",
"clsNm": "CORE PS PUZZLE",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006506",
"clsNm": "CORE PS FIGURE PLAYSET",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006509",
"clsNm": "CORE PS OTHER TOYS",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006511",
"clsNm": "CORE PS TALKING SOUND",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006507",
"clsNm": "CORE PS LEARNING TOY",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006510",
"clsNm": "CORE PS ROLEPLAY",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006512",
"clsNm": "CORE PS VEHICLES",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006585",
"clsNm": "DIECAST MED LG SCALE VEHICLES",
"subCt": "L3-001818",
"subCtNm": "DIECAST AND PLAYSETS",
"ct": "L4-000425",
"ctNm": "ACT FIGS CONSTRUCTION VEHICLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006587",
"clsNm": "DIECAST PLAYSETS",
"subCt": "L3-001818",
"subCtNm": "DIECAST AND PLAYSETS",
"ct": "L4-000425",
"ctNm": "ACT FIGS CONSTRUCTION VEHICLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006586",
"clsNm": "DIECAST MINI VEHICLES",
"subCt": "L3-001818",
"subCtNm": "DIECAST AND PLAYSETS",
"ct": "L4-000425",
"ctNm": "ACT FIGS CONSTRUCTION VEHICLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006798",
"clsNm": "VACUUMS UPRIGHT BAGLESS",
"subCt": "L3-001851",
"subCtNm": "FLOOR CLEANING",
"ct": "L4-000449",
"ctNm": "HOME ELECTRICS",
"seg": "L5-000054",
"segNm": "HARD HOME",
"area": "L6-000012",
"areaNm": "IN AND OUTDOOR HOME"
},
{
"cls": "L2-006795",
"clsNm": "VACUUMS HAND",
"subCt": "L3-001851",
"subCtNm": "FLOOR CLEANING",
"ct": "L4-000449",
"ctNm": "HOME ELECTRICS",
"seg": "L5-000054",
"segNm": "HARD HOME",
"area": "L6-000012",
"areaNm": "IN AND OUTDOOR HOME"
},
{
"cls": "L2-006791",
"clsNm": "FLOOR DEEP CLEANER CHEMICALS",
"subCt": "L3-001851",
"subCtNm": "FLOOR CLEANING",
"ct": "L4-000449",
"ctNm": "HOME ELECTRICS",
"seg": "L5-000054",
"segNm": "HARD HOME",
"area": "L6-000012",
"areaNm": "IN AND OUTDOOR HOME"
},
{
"cls": "L2-006796",
"clsNm": "VACUUMS STICK",
"subCt": "L3-001851",
"subCtNm": "FLOOR CLEANING",
"ct": "L4-000449",
"ctNm": "HOME ELECTRICS",
"seg": "L5-000054",
"segNm": "HARD HOME",
"area": "L6-000012",
"areaNm": "IN AND OUTDOOR HOME"
},
{
"cls": "L2-012895",
"clsNm": "FLOOR STEAM MOPS",
"subCt": "L3-001851",
"subCtNm": "FLOOR CLEANING",
"ct": "L4-000449",
"ctNm": "HOME ELECTRICS",
"seg": "L5-000054",
"segNm": "HARD HOME",
"area": "L6-000012",
"areaNm": "IN AND OUTDOOR HOME"
}]
;
$scope.loadMP6DataToMemory = function(data) {
angular.forEach(data, function (value, key) {
if ($.inArray(value.area, $scope.mp6Root) === -1) {
$scope.mp6Root.push(value.area);
}
addToMap(value.cls, value.clsNm, "");
addToMap(value.subCt, value.subCtNm, value.cls);
addToMap(value.ct, value.ctNm, value.subCt);
addToMap(value.seg, value.segNm, value.ct);
addToMap(value.area, value.areaNm, value.seg);
});
}
addToMap = function (pKey, pName, pChild) {
if (!$scope.mp6Data[pKey]) {
cSet = [];
$scope.mp6Data[pKey] = { name: pName, children: cSet };
} else {
if ($.inArray(pChild, $scope.mp6Data[pKey].children) === -1) {
$scope.mp6Data[pKey].children.push(pChild);
}
}
}
$scope.ExpandMP6 = function (pKey) {
if (pKey) {
mp = $scope.mp6Data[pKey];
return {
name: mp.name,
children: mp.children,
visible: false
}
}
}
$scope.loadMP6DataToMemory(data);
$scope.l5visible = false;
$scope.l4visible = false;
$scope.l3visible = false;
$scope.l2visible = false;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="container" ng-app="HelloWorldApp" ng-controller="HelloWorldController">
<div class="md-grid">
<ul class="md-list">
<li class="md-list-item-text" ng:repeat="l6 in mp6Root" ng-click="l5visible = !l5visible; $event.stopPropagation();">
L6 {{ExpandMP6(l6).name}}
<ul class="md-list" ng-show="l5visible">
<li class="md-list-item-text" ng:repeat="l5 in ExpandMP6(l6).children" ng-click="l4visible = !l4visible; $event.stopPropagation();">
L5 {{ExpandMP6(l5).name}}
<ul class="md-list" ng-show="l4visible">
<li class="md-list-item-text" ng:repeat="l4 in ExpandMP6(l5).children" ng-click="l3visible = !l3visible; $event.stopPropagation();">
L4 {{ExpandMP6(l4).name}}
<ul class="md-list" ng-show="l3visible">
<li class="md-list-item-text" ng:repeat="l3 in ExpandMP6(l4).children" ng-click="l2visible = !l2visible; $event.stopPropagation();">
L3 {{ExpandMP6(l3).name}}
<ul class="md-list" ng-show="l2visible">
<li class="md-list-item-text" ng:repeat="l2 in ExpandMP6(l3).children">
L2 {{ExpandMP6(l2).name}}
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
编辑:
这是我正在考虑的过滤器,但似乎不适合我构建 html 的方式:
如果 HTML 结构需要更改,我愿意接受建议。
首先,您应该创建一个嵌套指令来显示您的树。如果突然有7个级别显示怎么办?所以首先我会写一个递归指令,这也会减少代码大小。
对于数据过滤部分,您可以将 input
与 ng-model-options="{debounce: 300}"
结合使用 ng-change="filterFunction()"
,这样过滤仅在用户完成搜索后 300 毫秒后应用。
filterFunction()
一旦您的数据以分层形式构建,就非常容易编写,并且可以更改对象状态以指示您的指令是否应显示它,以及是否应显示其子项。
结果如下所示:
MainController.js
var app = angular.module('app', []);
app.controller('MainController', [function () {
var ctrl = this;
ctrl.search = '';
initHierarchies(); // function that transforms the data in hierarchical form
// filterHierarchies is called everytime the user changed the search input
ctrl.filterHierarchies = function () {
ctrl.filteredHierarchies = hierarchiesFilter(ctrl.hierarchies, ctrl.search).hierarchies;
}
ctrl.filterHierarchies(); // init the filteredHierarchies data.
// function that filters the hierarchy. It is a recursive function
function hierarchiesFilter(hierarchies, search) {
if (!hierarchies || !hierarchies.length) {
return { hierarchies: [], hasExpandedChildren: false};
}
console.log(hierarchies, search);
var oneIsExpanded = false;
for (var i = 0; i < hierarchies.length; i++) {
hierarchies[i].showChildren = false;
if (search.length) {
var rx = new RegExp(search, 'i');
if (hierarchies[i].name.match(rx)) {
oneIsExpanded = true;
}
}
// if the node has children which are expanded, we need to display it so its children that
// should be highlighted are visible
var hasExpandedChildren = hierarchiesFilter(hierarchies[i].children, search).hasExpandedChildren;
if (hasExpandedChildren) {
hierarchies[i].showChildren = true;
oneIsExpanded = true;
}
}
return { hierarchies: hierarchies, hasExpandedChildren: oneIsExpanded };
};
// function to transform the array data to a hierarchical structure
function initHierarchies() {
var data = getData();
var mp6Data = {};
var mp6Root = [];
angular.forEach(data, function (value, key) {
if (mp6Root.indexOf(value.area) === -1) {
mp6Root.push(value.area);
}
addToMap(value.cls, value.clsNm, "");
addToMap(value.subCt, value.subCtNm, value.cls);
addToMap(value.ct, value.ctNm, value.subCt);
addToMap(value.seg, value.segNm, value.ct);
addToMap(value.area, value.areaNm, value.seg);
});
function addToMap(pKey, pName, pChild) {
if (!mp6Data[pKey]) {
mp6Data[pKey] = { name: pName, childrenKeys: [] };
} else {
if (mp6Data[pKey].childrenKeys.indexOf(pChild) === -1) {
mp6Data[pKey].childrenKeys.push(pChild);
}
}
}
function buildHierarchicalStructure(childrenKeys) {
var builtChildren = [];
for (var i = 0; i < childrenKeys.length; i++) {
builtChildren.push({
name: mp6Data[childrenKeys[i]].name,
children: buildHierarchicalStructure(mp6Data[childrenKeys[i]].childrenKeys)
});
}
return builtChildren;
}
for (var i = 0; i < mp6Data.length; i++) {
mp6Data[i].showChildren = true;
}
ctrl.hierarchies = buildHierarchicalStructure(mp6Root);
}
}]);
hierarchy.directive.js
app.directive('hierarchy', ['RecursionHelper', function (RecursionHelper) {
return {
template: '<div><div ng-click="hierarchyCtrl.ngModel.showChildren = !hierarchyCtrl.ngModel.showChildren">{{ hierarchyCtrl.ngModel.name }}</div><ul ng-if="hierarchyCtrl.ngModel.children && (hierarchyCtrl.ngModel.showChildren)"><li ng-repeat="element in hierarchyCtrl.ngModel.children"><hierarchy ng-model="element"></hierarchy></li></ul></div>',
restrict: 'E',
scope: { ngModel: '=' },
controller: ['$scope', function($scope) { this.ngModel = $scope.ngModel; }],
controllerAs: 'hierarchyCtrl',
compile: function (element) {
return RecursionHelper.compile(element);
},
};
}]);
index.html
<body>
<h1>Hello Plunker!</h1>
<div ng-controller="MainController as mainCtrl">
<input type="text" ng-model="mainCtrl.search" ng-model-options="{debounce: 300}" ng-change="mainCtrl.filterHierarchies()" />
<ul>
<li ng-repeat="hierarchy in mainCtrl.filteredHierarchies"><hierarchy ng-model="hierarchy"></hierarchy></li>
</ul>
</div>
</body>
Plunker 示例:https://plnkr.co/edit/1jiiiwkdUZY4tm7sm79F?p=preview
我会让你编写代码部分来突出显示与搜索匹配的文本,因为转换执行过滤的函数非常容易。提示:在 hierarchiesFilter()
函数中,您可以将 htmlHighlighted
属性 添加到每个节点,您将匹配文本包裹在 <strong>
和 </strong>
标签之间。
因为这可能不是您想要的确切行为,您可以调整过滤器功能以在用户更改搜索时准确显示您想要的内容。
一些代码(递归助手)来自这个post:Recursion in Angular directives
我构建了以下显示层次结构的 Angular 应用程序:
我正在尝试在此层次结构的顶部插入一个文本框。过滤底部的数据。已经尝试了几个带有过滤器的示例,但到目前为止运气不佳。
我想做的是在用户开始向文本框输入内容时利用 angular 绑定,动态展开和折叠层次结构并突出显示匹配项。
正在寻找解决此问题的最佳方法的建议。请注意,层次结构可能会变大并拥有大约 3000 条记录。
angular.module('HelloWorldApp', [])
.controller('HelloWorldController', function($scope) {
$scope.mp6Root = [];
$scope.mp6Data = [];
var data = [
{
"cls": "L2-013551",
"clsNm": "FASHION DOLLS",
"subCt": "L3-001793",
"subCtNm": "FASHION DOLLS AND ACCESSORIES",
"ct": "L4-000429",
"ctNm": "DOLLS GAMES PUZZLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006472",
"clsNm": "FASHION DOLL WITH ACCS",
"subCt": "L3-001793",
"subCtNm": "FASHION DOLLS AND ACCESSORIES",
"ct": "L4-000429",
"ctNm": "DOLLS GAMES PUZZLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-014668",
"clsNm": "ACTIVITIES",
"subCt": "L3-001793",
"subCtNm": "FASHION DOLLS AND ACCESSORIES",
"ct": "L4-000429",
"ctNm": "DOLLS GAMES PUZZLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-014667",
"clsNm": "STORAGE",
"subCt": "L3-001793",
"subCtNm": "FASHION DOLLS AND ACCESSORIES",
"ct": "L4-000429",
"ctNm": "DOLLS GAMES PUZZLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-014675",
"clsNm": "FASHION DOLL PLAYSET",
"subCt": "L3-001793",
"subCtNm": "FASHION DOLLS AND ACCESSORIES",
"ct": "L4-000429",
"ctNm": "DOLLS GAMES PUZZLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006476",
"clsNm": "ROLE PLAY FASHION AND TOY",
"subCt": "L3-001793",
"subCtNm": "FASHION DOLLS AND ACCESSORIES",
"ct": "L4-000429",
"ctNm": "DOLLS GAMES PUZZLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-014677",
"clsNm": "CORE PS FIGURE W PLAYSET",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006508",
"clsNm": "CORE PS MUSICAL INSTRUMENT",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-014788",
"clsNm": "WAGONS TOYS",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006536",
"clsNm": "RIDING TOYS FOOT TO FLOOR",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-014678",
"clsNm": "CORE PS PUZZLE",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006506",
"clsNm": "CORE PS FIGURE PLAYSET",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006509",
"clsNm": "CORE PS OTHER TOYS",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006511",
"clsNm": "CORE PS TALKING SOUND",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006507",
"clsNm": "CORE PS LEARNING TOY",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006510",
"clsNm": "CORE PS ROLEPLAY",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006512",
"clsNm": "CORE PS VEHICLES",
"subCt": "L3-001798",
"subCtNm": "CORE PRESCHOOL TOYS",
"ct": "L4-000428",
"ctNm": "PRESCHOOL",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006585",
"clsNm": "DIECAST MED LG SCALE VEHICLES",
"subCt": "L3-001818",
"subCtNm": "DIECAST AND PLAYSETS",
"ct": "L4-000425",
"ctNm": "ACT FIGS CONSTRUCTION VEHICLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006587",
"clsNm": "DIECAST PLAYSETS",
"subCt": "L3-001818",
"subCtNm": "DIECAST AND PLAYSETS",
"ct": "L4-000425",
"ctNm": "ACT FIGS CONSTRUCTION VEHICLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006586",
"clsNm": "DIECAST MINI VEHICLES",
"subCt": "L3-001818",
"subCtNm": "DIECAST AND PLAYSETS",
"ct": "L4-000425",
"ctNm": "ACT FIGS CONSTRUCTION VEHICLES",
"seg": "L5-000031",
"segNm": "TOYS",
"area": "L6-000004",
"areaNm": "HARDLINES"
},
{
"cls": "L2-006798",
"clsNm": "VACUUMS UPRIGHT BAGLESS",
"subCt": "L3-001851",
"subCtNm": "FLOOR CLEANING",
"ct": "L4-000449",
"ctNm": "HOME ELECTRICS",
"seg": "L5-000054",
"segNm": "HARD HOME",
"area": "L6-000012",
"areaNm": "IN AND OUTDOOR HOME"
},
{
"cls": "L2-006795",
"clsNm": "VACUUMS HAND",
"subCt": "L3-001851",
"subCtNm": "FLOOR CLEANING",
"ct": "L4-000449",
"ctNm": "HOME ELECTRICS",
"seg": "L5-000054",
"segNm": "HARD HOME",
"area": "L6-000012",
"areaNm": "IN AND OUTDOOR HOME"
},
{
"cls": "L2-006791",
"clsNm": "FLOOR DEEP CLEANER CHEMICALS",
"subCt": "L3-001851",
"subCtNm": "FLOOR CLEANING",
"ct": "L4-000449",
"ctNm": "HOME ELECTRICS",
"seg": "L5-000054",
"segNm": "HARD HOME",
"area": "L6-000012",
"areaNm": "IN AND OUTDOOR HOME"
},
{
"cls": "L2-006796",
"clsNm": "VACUUMS STICK",
"subCt": "L3-001851",
"subCtNm": "FLOOR CLEANING",
"ct": "L4-000449",
"ctNm": "HOME ELECTRICS",
"seg": "L5-000054",
"segNm": "HARD HOME",
"area": "L6-000012",
"areaNm": "IN AND OUTDOOR HOME"
},
{
"cls": "L2-012895",
"clsNm": "FLOOR STEAM MOPS",
"subCt": "L3-001851",
"subCtNm": "FLOOR CLEANING",
"ct": "L4-000449",
"ctNm": "HOME ELECTRICS",
"seg": "L5-000054",
"segNm": "HARD HOME",
"area": "L6-000012",
"areaNm": "IN AND OUTDOOR HOME"
}]
;
$scope.loadMP6DataToMemory = function(data) {
angular.forEach(data, function (value, key) {
if ($.inArray(value.area, $scope.mp6Root) === -1) {
$scope.mp6Root.push(value.area);
}
addToMap(value.cls, value.clsNm, "");
addToMap(value.subCt, value.subCtNm, value.cls);
addToMap(value.ct, value.ctNm, value.subCt);
addToMap(value.seg, value.segNm, value.ct);
addToMap(value.area, value.areaNm, value.seg);
});
}
addToMap = function (pKey, pName, pChild) {
if (!$scope.mp6Data[pKey]) {
cSet = [];
$scope.mp6Data[pKey] = { name: pName, children: cSet };
} else {
if ($.inArray(pChild, $scope.mp6Data[pKey].children) === -1) {
$scope.mp6Data[pKey].children.push(pChild);
}
}
}
$scope.ExpandMP6 = function (pKey) {
if (pKey) {
mp = $scope.mp6Data[pKey];
return {
name: mp.name,
children: mp.children,
visible: false
}
}
}
$scope.loadMP6DataToMemory(data);
$scope.l5visible = false;
$scope.l4visible = false;
$scope.l3visible = false;
$scope.l2visible = false;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="container" ng-app="HelloWorldApp" ng-controller="HelloWorldController">
<div class="md-grid">
<ul class="md-list">
<li class="md-list-item-text" ng:repeat="l6 in mp6Root" ng-click="l5visible = !l5visible; $event.stopPropagation();">
L6 {{ExpandMP6(l6).name}}
<ul class="md-list" ng-show="l5visible">
<li class="md-list-item-text" ng:repeat="l5 in ExpandMP6(l6).children" ng-click="l4visible = !l4visible; $event.stopPropagation();">
L5 {{ExpandMP6(l5).name}}
<ul class="md-list" ng-show="l4visible">
<li class="md-list-item-text" ng:repeat="l4 in ExpandMP6(l5).children" ng-click="l3visible = !l3visible; $event.stopPropagation();">
L4 {{ExpandMP6(l4).name}}
<ul class="md-list" ng-show="l3visible">
<li class="md-list-item-text" ng:repeat="l3 in ExpandMP6(l4).children" ng-click="l2visible = !l2visible; $event.stopPropagation();">
L3 {{ExpandMP6(l3).name}}
<ul class="md-list" ng-show="l2visible">
<li class="md-list-item-text" ng:repeat="l2 in ExpandMP6(l3).children">
L2 {{ExpandMP6(l2).name}}
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
编辑:
这是我正在考虑的过滤器,但似乎不适合我构建 html 的方式:
如果 HTML 结构需要更改,我愿意接受建议。
首先,您应该创建一个嵌套指令来显示您的树。如果突然有7个级别显示怎么办?所以首先我会写一个递归指令,这也会减少代码大小。
对于数据过滤部分,您可以将 input
与 ng-model-options="{debounce: 300}"
结合使用 ng-change="filterFunction()"
,这样过滤仅在用户完成搜索后 300 毫秒后应用。
filterFunction()
一旦您的数据以分层形式构建,就非常容易编写,并且可以更改对象状态以指示您的指令是否应显示它,以及是否应显示其子项。
结果如下所示:
MainController.js
var app = angular.module('app', []);
app.controller('MainController', [function () {
var ctrl = this;
ctrl.search = '';
initHierarchies(); // function that transforms the data in hierarchical form
// filterHierarchies is called everytime the user changed the search input
ctrl.filterHierarchies = function () {
ctrl.filteredHierarchies = hierarchiesFilter(ctrl.hierarchies, ctrl.search).hierarchies;
}
ctrl.filterHierarchies(); // init the filteredHierarchies data.
// function that filters the hierarchy. It is a recursive function
function hierarchiesFilter(hierarchies, search) {
if (!hierarchies || !hierarchies.length) {
return { hierarchies: [], hasExpandedChildren: false};
}
console.log(hierarchies, search);
var oneIsExpanded = false;
for (var i = 0; i < hierarchies.length; i++) {
hierarchies[i].showChildren = false;
if (search.length) {
var rx = new RegExp(search, 'i');
if (hierarchies[i].name.match(rx)) {
oneIsExpanded = true;
}
}
// if the node has children which are expanded, we need to display it so its children that
// should be highlighted are visible
var hasExpandedChildren = hierarchiesFilter(hierarchies[i].children, search).hasExpandedChildren;
if (hasExpandedChildren) {
hierarchies[i].showChildren = true;
oneIsExpanded = true;
}
}
return { hierarchies: hierarchies, hasExpandedChildren: oneIsExpanded };
};
// function to transform the array data to a hierarchical structure
function initHierarchies() {
var data = getData();
var mp6Data = {};
var mp6Root = [];
angular.forEach(data, function (value, key) {
if (mp6Root.indexOf(value.area) === -1) {
mp6Root.push(value.area);
}
addToMap(value.cls, value.clsNm, "");
addToMap(value.subCt, value.subCtNm, value.cls);
addToMap(value.ct, value.ctNm, value.subCt);
addToMap(value.seg, value.segNm, value.ct);
addToMap(value.area, value.areaNm, value.seg);
});
function addToMap(pKey, pName, pChild) {
if (!mp6Data[pKey]) {
mp6Data[pKey] = { name: pName, childrenKeys: [] };
} else {
if (mp6Data[pKey].childrenKeys.indexOf(pChild) === -1) {
mp6Data[pKey].childrenKeys.push(pChild);
}
}
}
function buildHierarchicalStructure(childrenKeys) {
var builtChildren = [];
for (var i = 0; i < childrenKeys.length; i++) {
builtChildren.push({
name: mp6Data[childrenKeys[i]].name,
children: buildHierarchicalStructure(mp6Data[childrenKeys[i]].childrenKeys)
});
}
return builtChildren;
}
for (var i = 0; i < mp6Data.length; i++) {
mp6Data[i].showChildren = true;
}
ctrl.hierarchies = buildHierarchicalStructure(mp6Root);
}
}]);
hierarchy.directive.js
app.directive('hierarchy', ['RecursionHelper', function (RecursionHelper) {
return {
template: '<div><div ng-click="hierarchyCtrl.ngModel.showChildren = !hierarchyCtrl.ngModel.showChildren">{{ hierarchyCtrl.ngModel.name }}</div><ul ng-if="hierarchyCtrl.ngModel.children && (hierarchyCtrl.ngModel.showChildren)"><li ng-repeat="element in hierarchyCtrl.ngModel.children"><hierarchy ng-model="element"></hierarchy></li></ul></div>',
restrict: 'E',
scope: { ngModel: '=' },
controller: ['$scope', function($scope) { this.ngModel = $scope.ngModel; }],
controllerAs: 'hierarchyCtrl',
compile: function (element) {
return RecursionHelper.compile(element);
},
};
}]);
index.html
<body>
<h1>Hello Plunker!</h1>
<div ng-controller="MainController as mainCtrl">
<input type="text" ng-model="mainCtrl.search" ng-model-options="{debounce: 300}" ng-change="mainCtrl.filterHierarchies()" />
<ul>
<li ng-repeat="hierarchy in mainCtrl.filteredHierarchies"><hierarchy ng-model="hierarchy"></hierarchy></li>
</ul>
</div>
</body>
Plunker 示例:https://plnkr.co/edit/1jiiiwkdUZY4tm7sm79F?p=preview
我会让你编写代码部分来突出显示与搜索匹配的文本,因为转换执行过滤的函数非常容易。提示:在 hierarchiesFilter()
函数中,您可以将 htmlHighlighted
属性 添加到每个节点,您将匹配文本包裹在 <strong>
和 </strong>
标签之间。
因为这可能不是您想要的确切行为,您可以调整过滤器功能以在用户更改搜索时准确显示您想要的内容。
一些代码(递归助手)来自这个post:Recursion in Angular directives