AngularJS 中多特定模型属性的滤波(OR 关系)

看看这里的例子: http://docs.angularjs.org/api/ng.filter:filter

您可以使用 <input ng-model="search">根据任何电话属性进行搜索,也可以使用 <input ng-model="search.name">根据名称进行搜索,并且按照名称对结果进行适当的过滤(输入电话号码不会返回任何结果,正如预期的那样)。

假设我有一个带有“ name”属性、“ phone”属性和“ secret”属性的模型,那么如何通过 都有过滤“ name”和“ phone”属性,通过 没有过滤“ secret”属性?因此,本质上,用户可以键入一个名字或电话号码,并且 ng-repeat会正确地进行过滤,但是即使用户键入的值等于“机密”值的一部分,它也不会返回任何东西。

谢谢。

177028 次浏览

Here is the plunker

New plunker with cleaner code & where both the query and search list items are case insensitive

Main idea is create a filter function to achieve this purpose.

From official doc

function: 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.

<input ng-model="query">


<tr ng-repeat="smartphone in smartphones | filter: search ">

$scope.search = function(item) {
if (!$scope.query || (item.brand.toLowerCase().indexOf($scope.query) != -1) || (item.model.toLowerCase().indexOf($scope.query.toLowerCase()) != -1) ){
return true;
}
return false;
};

Update

Some people might have a concern on performance in real world, which is correct.

In real world, we probably would do this kinda filter from controller.

Here is the detail post showing how to do it.

in short, we add ng-change to input for monitoring new search change

and then trigger filter function.

You can pass an Object as the parameter to your filter expression, as described in the API Reference. This object can selectively apply the properties you're interested in, like so:

<input ng-model="search.name">
<input ng-model="search.phone">
<input ng-model="search.secret">
<tr ng-repeat="user in users | filter:{name: search.name, phone: search.phone}">

Here's a Plunker

Heads up...this example works great with AngularJS 1.1.5, but not always as well in 1.0.7. In this example 1.0.7 will initialize with everything filtered out, then work when you start using the inputs. It behaves like the inputs have non-matching values in them, even though they start out blank. If you want to stay on stable releases, go ahead and try this out for your situation, but some scenarios may want to use @maxisam's solution until 1.2.0 is released.

I inspired myself from @maxisam's answer and created my own sort function and I'd though I'd share it (cuz I'm bored).

Situation I want to filter through an array of cars. The selected properties to filter are name, year, price and km. The property price and km are numbers (hence the use of .toString). I also want to control for uppercase letters (hence .toLowerCase). Also I want to be able to split up my filter query into different words (e.g. given the filter 2006 Acura, it finds matches 2006 with the year and Acura with the name).

Function I pass to filter

        var attrs = [car.name.toLowerCase(), car.year, car.price.toString(), car.km.toString()],
filters = $scope.tableOpts.filter.toLowerCase().split(' '),
isStringInArray = function (string, array){
for (var j=0;j<array.length;j++){
if (array[j].indexOf(string)!==-1){return true;}
}
return false;
};


for (var i=0;i<filters.length;i++){
if (!isStringInArray(filters[i], attrs)){return false;}
}
return true;
};

Hope this answer will help,Multiple Value Filter

And working example in this fiddle

arrayOfObjectswithKeys | filterMultiple:{key1:['value1','value2','value3',...etc],key2:'value4',key3:[value5,value6,...etc]}

Here's simple solution for those who want a quick filter against an object:

<select>
<option ng-repeat="card in deck.Cards | filter: {Type: 'Face'}">\{\{card.Name}}</option>
</select>

The array filter lets you mimic the object you are trying to filter. In the above case, the following classes would work just fine:

var card = function(name, type) {
var _name = name;
var _type = type;


return {
Name: _name,
Type: _type
};
};

And where the deck might look like:

var deck = function() {
var _cards = [new card('Jack', 'Face'),
new card('7', 'Numeral')];


return {
Cards: _cards
};
};

And if you want to filter multiple properties of the object just separate field names by a comma:

<select>
<option ng-repeat="card in deck.Cards | filter: {Type: 'Face', Name: 'Jack'}">\{\{card.Name}}</option>
</select>

EDIT: Here's a working plnkr that provides an example of single and multiple property filters:

http://embed.plnkr.co/i0wCx6l1WPYljk57SXzV/

I like to keep is simple when possible. I needed to group by International, filter on all the columns, display the count for each group and hide the group if no items existed.

Plus I did not want to add a custom filter just for something simple like this.

        <tbody>
<tr ng-show="fusa.length > 0"><td colspan="8"><h3>USA (\{\{fusa.length}})</h3></td></tr>
<tr ng-repeat="t in fusa = (usa = (vm.assignmentLookups | filter: {isInternational: false}) | filter: vm.searchResultText)">
<td>\{\{$index + 1}}</td>
<td ng-bind-html="vm.highlight(t.title, vm.searchResultText)"></td>
<td ng-bind-html="vm.highlight(t.genericName, vm.searchResultText)"></td>
<td ng-bind-html="vm.highlight(t.mechanismsOfAction, vm.searchResultText)"></td>
<td ng-bind-html="vm.highlight(t.diseaseStateIndication, vm.searchResultText)"></td>
<td ng-bind-html="vm.highlight(t.assignedTo, vm.searchResultText)"></td>
<td ng-bind-html="t.lastPublished | date:'medium'"></td>
</tr>
</tbody>
<tbody>
<tr ng-show="fint.length > 0"><td colspan="8"><h3>International (\{\{fint.length}})</h3></td></tr>
<tr ng-repeat="t in fint = (int = (vm.assignmentLookups | filter: {isInternational: true}) | filter: vm.searchResultText)">
<td>\{\{$index + 1}}</td>
<td ng-bind-html="vm.highlight(t.title, vm.searchResultText)"></td>
<td ng-bind-html="vm.highlight(t.genericName, vm.searchResultText)"></td>
<td ng-bind-html="vm.highlight(t.mechanismsOfAction, vm.searchResultText)"></td>
<td ng-bind-html="vm.highlight(t.diseaseStateIndication, vm.searchResultText)"></td>
<td ng-bind-html="vm.highlight(t.assignedTo, vm.searchResultText)"></td>
<td ng-bind-html="t.lastPublished | date:'medium'"></td>
</tr>
</tbody>

http://plnkr.co/edit/A2IG03FLYnEYMpZ5RxEm?p=preview

Here is a case sensitive search that also separates your search into words to search in each model as well. Only when it finds a space will it try to split the query into an array and then search each word.

It returns true if every word is at least in one of the models.

$scope.songSearch = function (row) {
var query = angular.lowercase($scope.query);
if (query.indexOf(" ") > 0) {
query_array = query.split(" ");
search_result = false;
for (x in query_array) {
query = query_array[x];
if (angular.lowercase(row.model1).indexOf(query || '') !== -1 || angular.lowercase(row.model2).indexOf(query || '') !== -1 || angular.lowercase(row.model3).indexOf(query || '') !== -1){
search_result = true;
} else {
search_result = false;
break;
}
}
return search_result;
} else {
return (angular.lowercase(row.model1).indexOf(query || '') !== -1 || angular.lowercase(row.model2).indexOf(query || '') !== -1 || angular.lowercase(row.model3).indexOf(query || '') !== -1);
}
};

If you're open to use third party libraries,'Angular Filters' with a nice collection of filters may be useful:

https://github.com/a8m/angular-filter#filterby

collection | filterBy: [prop, nested.prop, etc..]: search

There are a bunch of good solutions here but I'd suggest going the other route with this. If searching is the most important thing and the 'secret' property is not used on the view at all; my approach for this would be to use the built-in angular filter with all its benefits and simply map the model to a new array of objects.

Example from the question:

$scope.people = members.map(function(member) {
return {
name: member.name,
phone: member.phone
}});

Now, in html simply use the regular filter to search both these properties.

<div ng-repeat="member in people | filter: searchString">

Filter can be a JavaScript object with fields and you can have expression as:

ng-repeat= 'item in list | filter :{property:value}'

Pretty straight forward approach using filterBy in angularJs

For working plnkr follow this link

http://plnkr.co/edit/US6xE4h0gD5CEYV0aMDf?p=preview

This has specially used a single property to search against multiple column but not all.

<!DOCTYPE html>
<html ng-app="myApp">


<head>
<script data-require="angular.js@1.4.1" data-semver="1.4.1" src="https://code.angularjs.org/1.4.1/angular.js"></script>
<script data-require="angular-filter@0.5.2" data-semver="0.5.2" src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.2/angular-filter.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>


<body ng-controller="myCtrl as vm">
<input type="text" placeholder="enter your search text" ng-model="vm.searchText" />
<table>
<tr ng-repeat="row in vm.tableData | filterBy: ['col1','col2']: vm.searchText">
<td>\{\{row.col1}}</td>
<td>\{\{row.col2}}</td>
<td>\{\{row.col3}}</td>
</tr>
</table>
<p>This will show filter from <b>two columns</b> only(col1 and col2) . Not from all. Whatever columns you add into filter array they
will be searched. all columns will be used by default if you use <b>filter: vm.searchText</b></p>
</body>
</html>

controller

// Code goes here
(function(){


var myApp = angular.module('myApp',['angular.filter']);


myApp.controller('myCtrl', function($scope){
var vm= this;
vm.x = 20;


vm.tableData = [
{
col1: 'Ashok',
col2: 'Tewatia',
col3: '12'
},{
col1: 'Babita',
col2: 'Malik',
col3: '34'
},{
col1: 'Dinesh',
col2: 'Tewatia',
col3: '56'
},{
col1: 'Sabita',
col2: 'Malik',
col3: '78'
}
];
})


})();

I might be very late but this is what I ended up doing. I made a very general filter.

angular.module('app.filters').filter('fieldFilter', function() {
return function(input, clause, fields) {
var out = [];
if (clause && clause.query && clause.query.length > 0) {
clause.query = String(clause.query).toLowerCase();
angular.forEach(input, function(cp) {
for (var i = 0; i < fields.length; i++) {
var haystack = String(cp[fields[i]]).toLowerCase();
if (haystack.indexOf(clause.query) > -1) {
out.push(cp);
break;
}
}
})
} else {
angular.forEach(input, function(cp) {
out.push(cp);
})
}
return out;
}


})

Then use it like this

<tr ng-repeat-start="dvs in devices |  fieldFilter:search:['name','device_id']"></tr>

Your search box be like

<input ng-model="search.query" class="form-control material-text-input" type="text">

where name and device_id are properties in dvs

I solved this simply:

<div ng-repeat="Object in List | filter: (FilterObj.FilterProperty1 ? {'ObjectProperty1': FilterObj.FilterProperty1} : '') | filter:(FilterObj.FilterProperty2 ? {'ObjectProperty2': FilterObj.FilterProperty2} : '')">