Let suppose I have an array of strings like ["don't worry", "worry", "Always be happy and don't worry" ]
When I search for worry,using default search filter on ng-repeat, it gives me all three results irrespective of the position at which worry is in the string to be searched.
I am trying to make a custom search filter for ng-repeat such that on searching worry I should get results sorted according to the position of "worry" in the original string.
So upon searching for word "worry" in the above array, my expected output will be
["worry", "don't worry", "Always be happy and don't worry"].
Is this possible?
Could you try this?
app.filter('wordsFilter', function() {
return function(items, word) {
var filtered = [];
angular.forEach(items, function(item) {
if(item.indexOf(word) !== -1){
filtered.push(item);
}
});
filtered.sort(function(a,b){
if(a.indexOf(word) < b.indexOf(word)) return -1;
else if(a.indexOf(word) > b.indexOf(word)) return 1;
else return 0;
});
return filtered;
};
});
I hope it helps!
Related
I am building an application in NodeJS and AngularJS.
I am building a multi-column search functionality where the user can type in search keywords into separate searchboxes (at the top of each column) and retrieve the results based on the column.
So far I have a single searchbox that searches all attributes at the same time.
How can I implement multiple individual searchboxes that will return results based on multiple attributes?
Note: I want to implement this on the server-side for performance reasons. (I know that I can simply use HTML attributes | filter:column1 | filter:column2 but want to avoid this technique if possible).
Here is the code I have so far. I am thinking that I need to pass in some sort of "searchBy" variable that is set on the view and then update the search method to search by multiple query/attribute pairs.
//Search service factory
//Initialize filtered items and get search results
function search(items, query) {
this.filteredItems = $filter('filter')(items, function (item) {
for(var attr in item) {
if (searchMatch(item[attr], query))
return true;
}
return false;
});
return this.filteredItems;
}
function searchMatch(haystack, needle) {
if (!needle) {
return true;
}
return haystack.toString().toLowerCase().indexOf(needle.toLowerCase()) !== -1;
};
//Controller
vm.filteredItems = vm.search(vm.unfilteredItems, vm.query);
//View
input(type='text', ng-model='vm.query', ng-change='vm.search(vm.unfilteredItems, vm.query)', placeholder='Search')
I was able to solve this by first creating an array of objects for each search box then repeating those boxes in the view with the ng-repeat attribute.
//Controller
var vm = this;
var vm.unfilteredItems; //data source query removed for brevity
//Initialize search inputs
vm.search_by_inputs = [
{search_column: 'id', search_query: ''},
{search_column: 'requester', search_query: ''},
{search_column: 'dataowner', search_query: ''}
];
function initSearch() {
vm.filtered_items = vm.search(vm.unfiltered_items, vm.search_by_inputs);
}
//View
input.input-large.search-query(type='text', value='{{search_by.search_query}}', ng-model='search_by.search_query' ng-change='vm.initSearch()', placeholder='Search')
The next step is to loop over the search_by_inputs object in the controller and create a new object with only the inputs that have search values entered into the searchboxes in the view. Then in the search method the built-in "filter" component iterates each item, and inside that loop each of the search terms is checked against that value with the column name that matches the property.
/*
* Create new array of objects with only elements that have search values to optimize loop inside filter
* #search_by_inputs array of objects each has a key search_column and a value search_query
*/
function optimizeSearchProperties(search_by_inputs) {
search_by_properties = [];
for (var i = 0, len = search_by_inputs.length; i < len; i++) {
//If this column input box has query text
if (search_by_inputs[i].search_query) {
search_by_properties.push(search_by_inputs[i]);
}
}
return search_by_properties;
}
/*
* #haystack search item
* #needle search term
*/
function searchMatch(haystack, needle) {
if (!needle) {
return true;
}
return haystack.toString().toLowerCase().indexOf(needle.toLowerCase()) !== -1;
}
/*
* Create filtered items object by filtering search results
* #items original array of objects returned by database query result
* #search_by_inputs array of objects each has a key search_column and a value search_query
*/
function search(items, search_by_inputs) {
var search_by_properties = optimizeSearchProperties(search_by_inputs);
//If there are no search properties input by requester then return all items
if (search_by_properties.length === 0) {
this.filtered_items = items;
return this.filtered_items;
}
this.filtered_items = $filter('filter')(items, function (item) {
var search_result = true;
//Loop over all search by input textboxes
for (var n = 0, len = search_by_properties.length; n < len; n++) {
//If there is no query text
if (!search_by_properties[n].search_query) {
//Continue to next element in array
continue;
//Else if element has a property that matches search input column name
} else if (item[search_by_properties[n].search_column]) {
if (!searchMatch(item[search_by_properties[n].search_column], search_by_properties[n].search_query)) {
search_result = false;
break;
}
}
}
return search_result;
});
return this.filtered_items;
}
I would be glad to have some feedback on this solution in terms of optimization, performance, technique, etc. Thanks!
I have a scope array called $scope.groups
$scope.groups = [{
id: 1,
name: "Group 1"
},
{
id: 2,
name: "Group 2"
}]
When I updated one of the groups I need to check if that updated group exists in the groups array however when i filter the array it checks the group i need to update as well so it outputs "Group exists".
function ifGroupExists(GroupName,GroupId) {
var match;
match = $scope.groups.filter(function (item) { return angular.lowercase(item.name) === angular.lowercase(GroupName); });
if (match.length > 0) {
console.log("group exists");
return true;
}
else {
console.log("group does not exists");
return false;
}
}
This code works if im adding a totally new group to the array however how do i edit this so that it doesnt check the group currently being updated and have it so it only checks the other groups to see if there is a match.
Can someone help? Im sure there is a simple way to do this. Just cant seem to figure it out..
It can be simple like, just pass the currently updating group to function as well, and ignore it in the filter.
Code should be like :
function ifGroupExists(groupName_filter, groupName_current) {
var match;
match = $scope.groups.filter(function (item) {
return (angular.lowercase(item.name) === angular.lowercase(groupName_filter) &&
angular.lowercase(item.name) !== angular.lowercase(groupName_current));
});
return (match.length > 0);
}
and I hope you need console.log only during development :), so return can be simplified.
Further: If you have groupName_current as $scope property, then use it directly, no need to pass.
I've a custom function used as a filter. How can I get the index of the current element filtered.
<tr ng-repeat="(idx, line) in items | filter:inRange">....</tr>
//this is the filter
$scope.inRange = function(item) {
//how to get the index here?
};
Please note that I do not want to use indexOf
var idx = $scope.items.indexOf(item);
As in another answer on SO with the same kind of issue on filters
Filters don't work on individual items in the array, they transform the entire array into another array.
When defined as filter, inRange will receive the whole items array, not single items.
myModule.filter('inRange', function() {
return function(items) {
var filtered = [];
angular.forEach(items, function(item, index) {
// do whatever you want here with the index
filtered.push(item);
});
return filtered;
}
});
I'm trying to create a robust filtering system with basic HTML & angular.js, so far with the help of a few kind people on here, I've ended up with this:
http://codepen.io/liamtarpey/pen/jfvDK
It works fine, the only issue is if you type something backwards, for example if the user searches for: 'taco mexican' or even 'mexican taco', you'd get no results at all.
Is there a way to use the filter as more of a keyword filter rather than a string filter?
You will have to iterate through every property for every word in the search term. This works:
$scope.search = function(user) {
var match = true;
$scope.query.split(" ").forEach(function(word) {
if (match === true) {
var wordMatch = false;
for (var prop in user) {
if (user.hasOwnProperty(prop)) {
if(user[prop].toLowerCase().indexOf(word.toLowerCase()) > -1) wordMatch = true;
}
}
}
match = wordMatch;
});
return match
}
I have a list with the items (name: String, age: Int, checked:Boolean).
This list is displayed with an ng-repeat.
I want to enable the user to search the list using a searchfield, but the search must not affect the checked-values.
the search should only trigger, if the users enters something in the searchfield
if the seachfield is filled, the search should filter as usual, but the checked items must not be filtered out.
I tried to create a custom filter. I have problems in understanding the $filter('filter') function with my OR-logic.
Could anyone help me untie the knot in my brain?
app.filter('mySearchFilter', function($filter) {
return function(data, searchText) {
if(!searchText || searchText.length === 0) {
return data;
}
console.log(searchText);
return $filter('filter')(data, searchText);
//how can I provide additional, OR-concatinated, filter-criteria?
}
});
Check out my plunk for the minimal-example-code.
http://plnkr.co/edit/3xHLOrSPD3XZy2K9U2Og?p=preview
As I understand you, you are looking for a union function. Underscore provide such a function. With this you may write your filter in this way:
app.filter('mySearchFilter', function($filter) {
return function(data, searchText) {
if(!searchText || searchText.length === 0) {
return data;
}
var allChecked = data.filter(function(d){return d.checked});
var allMatched = $filter('filter')(data, searchText);
return _.union(allMatched, allChecked);
}
});
have a look at: http://documentcloud.github.io/underscore/#union and don't forget to include the script:
<script src="http://documentcloud.github.io/underscore/underscore.js"></script>
PLUNKR