我想从JSON文件中创建某种项目列表。数据结构(年,月,项目)如下所示:
[{
"name": "2013",
"months": [{
"name": "May 2013",
"projects": [{
"name": "2013-05-09 Project A"
}, {
"name": "2013-05-14 Project B"
}, { ... }]
}, { ... }]
}, { ... }]
我正在使用嵌套的ng-repeat
显示所有数据,并使其可以通过从输入框绑定到查询的过滤器进行搜索。
<input type="search" ng-model="query" placeholder="Suchen..." />
<div class="year" ng-repeat="year in data | orderBy:'name':true">
<h1>{{year.name}}</h1>
<div class="month" ng-repeat="month in year.months | orderBy:sortMonth:true">
<h3>{{month.name}}</h3>
<div class="project" ng-repeat="project in month.projects | filter:query | orderBy:'name'">
<p>{{project.name}}</p>
</div>
</div>
</div>
如果我现在输入“Project B”,所有空的父元素仍然可见。我该如何隐藏它们?我尝试了一些ng-show
技巧,但主要问题似乎是,我无法访问有关父母过滤状态的任何信息。
这是演示我的问题的小提琴:http://jsfiddle.net/stekhn/y3ft0cwn/7/
答案 0 :(得分:3)
您基本上必须过滤月份以仅保留至少包含一个已过滤项目的月份,并且您还必须过滤年份以仅保留至少有一个过滤月份的月份。
使用以下代码可以轻松实现:
function MainCtrl($scope, $filter) {
$scope.query = '';
$scope.monthHasVisibleProject = function(month) {
return $filter('filter')(month.children, $scope.query).length > 0;
};
$scope.yearHasVisibleMonth = function(year) {
return $filter('filter')(year.children, $scope.monthHasVisibleProject).length > 0;
};
并在视图中:
<div class="year" ng-repeat="year in data | filter:yearHasVisibleMonth | orderBy:'name':true">
<h1>{{year.name}}</h1>
<div class="month" ng-repeat="month in year.children | filter:monthHasVisibleProject | orderBy:sortMonth:true">
这是非常低效的,因为要知道是否接受了一年,您过滤所有月份,并且每个月过滤所有项目。因此,除非性能足以满足您的数据量,否则您应该应用相同的原则,但每次修改查询时都要保持每个对象的接受/拒绝状态(项目,然后是月,然后是年)。
答案 1 :(得分:2)
我认为最好的方法是实现自定义函数,以便在query
更改时使用过滤后的数据更新自定义数组。像这样:
$scope.query = '';
$scope.filteredData= angular.copy($scope.data);
$scope.updateFilteredData = function(newVal){
var filtered = angular.copy($scope.data);
filtered = filtered.map(function(year){
year.children=year.children.map(function(month){
month.children = $filter('filter')(month.children,newVal);
return month;
});
return year;
});
$scope.filteredData = filtered.filter(function(year){
year.children= year.children.filter(function(month){
return month.children.length>0;
});
return year.children.length>0;
});
}
然后您的观点将如下所示:
<input type="search" ng-model="query" ng-change="updateFilteredData(query)"
placeholder="Search..." />
<div class="year" ng-repeat="year in filteredData | orderBy:'name':true">
<h1>{{year.name}}</h1>
<div class="month" ng-repeat="month in year.children | orderBy:sortMonth:true">
<h3>{{month.name}}</h3>
<div class="project" ng-repeat="project in month.children | orderBy:'name'">
<p>{{project.name}}</p>
</div>
</div>
</div>
$filter
? 效率:$diggest
周期的性质会降低效率。唯一的问题是,这个解决方案不像自定义$filter
那样容易重复使用。但是,该自定义$filter
也不会非常可重用,因为它的逻辑非常依赖于这个具体的数据结构。
如果您需要在IE8上工作,则必须使用jQuery替换filter
和map
函数,或者确保定义这些函数,如下所示:
(顺便说一下:如果你需要IE8支持,那么使用jQuery完全没有问题。)
过滤强>:
if (!Array.prototype.filter) {
Array.prototype.filter = function(fun/*, thisArg*/) {
'use strict';
if (this === void 0 || this === null) {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
if (typeof fun !== 'function') {
throw new TypeError();
}
var res = [];
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (var i = 0; i < len; i++) {
if (i in t) {
var val = t[i];
if (fun.call(thisArg, val, i, t)) {
res.push(val);
}
}
}
return res;
};
}
<强>地图
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
var T, A, k;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
var O = Object(this);
var len = O.length >>> 0;
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
if (thisArg) {
T = thisArg;
}
A = new Array(len);
k = 0;
while(k < len) {
var kValue, mappedValue;
if (k in O) {
kValue = O[ k ];
mappedValue = callback.call(T, kValue, k, O);
A[ k ] = mappedValue;
}
k++;
}
return A;
};
}
我要感谢JB Nizet his feedback。
答案 2 :(得分:0)
对于那些感兴趣的人:昨天我找到了解决这个问题的另一种方法,这让我觉得效率很低。在键入查询时,将再次为每个孩子调用这些函数。不如Josep的解决方案好。
function MainCtrl($scope) {
$scope.query = '';
$scope.searchString = function () {
return function (item) {
var string = JSON.stringify(item).toLowerCase();
var words = $scope.query.toLowerCase();
if (words) {
var filterBy = words.split(/\s+/);
if (!filterBy.length) {
return true;
}
} else {
return true;
}
return filterBy.every(function (word) {
var exists = string.indexOf(word);
if(exists !== -1){
return true;
}
});
};
};
};
在视图中:
<div class="year" ng-repeat="year in data | filter:searchString() | orderBy:'name':true">
<h1>{{year.name}}</h1>
<div class="month" ng-repeat="month in year.children | filter:searchString() | orderBy:sortMonth:true">
<h3>{{month.name}}</h3>
<div class="project" ng-repeat="project in month.children | filter:searchString() | orderBy:'name'">
<p>{{project.name}}</p>
</div>
</div>
</div>