I\'m working through the AngularJS tutorial, and understand the basics of
However, the out of the box implementation seems limited to just filter the list of items
Not a one liner but still quite short and fun
app.filter("filterall",function($filter) {
return function(arr,t){
(t?t.split(/\s+/):[]).forEach(function(v){ arr = $filter('filter')(arr,v); });
return arr;
};
});
Here's my version. It uses JonoWilko's method of using the built in filterFilter
combined with surfbud's AND/OR flag (defaulted to "AND"
).
JavaScript
angular.module('filters').filter('searchFilter', function($filter) {
return function(inputArray, searchText, booleanOp) {
booleanOp = booleanOp || 'AND';
var searchTerms = (searchText || '').toLowerCase().split(/\s+/);
if (booleanOp === 'AND') {
var result = inputArray;
searchTerms.forEach(function(searchTerm) {
result = $filter('filter')(result, searchTerm);
});
} else {
var result = [];
searchTerms.forEach(function(searchTerm) {
result = result.concat($filter('filter')(inputArray, searchTerm));
});
}
return result;
};
});
CoffeeScript
angular.module('filters').filter 'searchFilter', ($filter)->
(inputArray, searchText='', booleanOp = 'AND')->
searchTerms = searchText.toLowerCase().split(/\s+/)
if booleanOp is 'AND'
result = inputArray
searchTerms.forEach (word)->
result = $filter('filter')(result, word)
else
result = []
searchTerms.forEach (word)->
result = result.concat $filter('filter')(inputArray, word)
result
'AND' Usage (default)
<div ng-repeat="product in products | searchFilter: searchInputText"></div>
'OR' Usage
<div ng-repeat="product in products | searchFilter: searchInputText : 'OR'"></div>
Some improvements to the above custom filter:
Instead of using a loop within a loop, counts, and indexOf, this one uses regular expressions to achieve a logical AND and also a logical OR depending on the third argument to the filter (input array of strings, search terms, AND or OR).
Have a look at the forked Fiddle with the two types of filter and results:
http://jsfiddle.net/jonjon/Cx3Pk/23/
angular.module('app', [])
.filter("myFilter", function () {
return function (input, searchText, AND_OR) {
var returnArray = [],
// Split on single or multi space
splitext = searchText.toLowerCase().split(/\s+/),
// Build Regexp with Logical AND using "look ahead assertions"
regexp_and = "(?=.*" + splitext.join(")(?=.*") + ")",
// Build Regexp with logicial OR
regexp_or = searchText.toLowerCase().replace(/\s+/g, "|"),
// Compile the regular expression
re = new RegExp((AND_OR == "AND") ? regexp_and : regexp_or, "i");
for (var x = 0; x < input.length; x++) {
if (re.test(input[x])) returnArray.push(input[x]);
}
return returnArray;
}
});
You can do a multiple word search on a object as follows:
.filter("myFilter", function(){
return function(input, searchText){
var returnArray = [];
var searchTextSplit = searchText.toLowerCase().split(' ');
for(var x = 0; x < input.length; x++){
var count = 0;
for(var y = 0; y < searchTextSplit.length; y++){
angular.forEach(input[x], function(item){
if(item.toLowerCase().indexOf(searchTextSplit[y]) !== -1){
count++;
}
});
}
if(count == searchTextSplit.length){
returnArray.push(input[x]);
}
}
return returnArray;
}
});
Working demo in js fiddle
Please see surfbuds answer below as it is superior
Just roll with your own filter:
.filter("myFilter", function(){
return function(input, searchText){
var returnArray = [];
var searchTextSplit = searchText.toLowerCase().split(' ');
for(var x = 0; x < input.length; x++){
var count = 0;
for(var y = 0; y < searchTextSplit.length; y++){
if(input[x].toLowerCase().indexOf(searchTextSplit[y]) !== -1){
count++;
}
}
if(count == searchTextSplit.length){
returnArray.push(input[x]);
}
}
return returnArray;
}
});
jsfiddle: http://jsfiddle.net/Cq3PF/
This filter makes sure that all search words are found.
Alternatively you could use the default Angular filter within your custom filter like so:
angular.module('app').filter("multiWordFilter", function($filter){
return function(inputArray, searchText){
var wordArray = searchText ? searchText.toLowerCase().split(/\s+/) : [];
var wordCount = wordArray.length;
for(var i=0;i<wordCount;i++){
inputArray = $filter('filter')(inputArray, wordArray[i]);
}
return inputArray;
}
});
This could be embellished further with user2005009's AND_OR comparator.