I have a complex object (a user object) that has nested arrays and nested objects within it.
I have a search filter that is relatively complicated as well (checkboxes determining which items are returned, along with a search input).
Currently I search in an object like so:
for(var key in item){
if(item[key] && item[key].length && String(item[key]).toLowerCase().indexOf($rootScope.filt.searchFilter.toLowerCase()) !== -1){
realSave = true;
}
}
However, this only works for the first layer of objects within an item; I need to also search for objects within objects.
How can i do this? Is this a simpler way than the above? (Note, I can't just use ng-repeat="item in items | searchFilter" as this needs to also parse checkboxes and return values accordingly.
Try
realSave = (JSON.stringify(item).indexOf($rootScope.filt.searchFilter.toLowerCase()) !== -1)
(It's a long line, scroll to the right)
It will transform your whole object into a single string, then you can search for the sub-string you're looking for anywhere inside it.
You can use angular filter like this:
app.controller('MainCtrl', function($scope, $filter) {
$scope.a = [{name : 'pit'}, {name : {a : 'pit'}}, {name : { a : { b : 'pit'}}}];
$scope.find = $filter('filter')($scope.a, 'pit');
});
http://plnkr.co/edit/TenLILkXJ0zwqMVtAj35?p=preview
Related
I was getting [ngRepeat:dupes]: error in my angularjs code.
So I searched on stackoverflow and found the solution which stated to use track by $index. This is working for me now. But, it displays the item multiple times if the same key is present multiple times. I want to display only one item if the same key exists multiple times.
here is my sample code from the view :
<div ng-repeat="user in users | filter:myFilter track by $index">
</div>
here is the current output :
Insted of showing these two cards, I want the code to show only one card as they both are the same.
How do I do it?
pass array into a function and create an array without the repeating values
like myFunction(users) so the repeat section will become ng-repeat="user in myFunction(users)". Write the function yourself.
I fixed it like this by creating a filter:
var app = angular.module('angularTable', []);
app.filter('unique', function() {
// we will return a function which will take in a collection
// and a keyname
return function(collection, keyname) {
// we define our output and keys array;
var output = [],
keys = [];
// we utilize angular's foreach function
// this takes in our original collection and an iterator function
angular.forEach(collection, function(item) {
// we check to see whether our object exists
var key = item[keyname];
// if it's not already part of our keys array
if(keys.indexOf(key) === -1) {
// add it to our keys array
keys.push(key);
// push this item to our final output array
output.push(item);
}
});
// return our array which should be devoid of
// any duplicates
return output;
};
});
app.controller('listdata', function($scope, $http) {
your controller logic for the users array
});
});
In the view :
<div ng-repeat="user in users |unique:'name' | filter:myFilter track by $index">
</div>
I have a simple button that when clicked, it will filter a list and return the filtered list:
var originalArray = [{name: "A", number: 1},{name: "B", number: 2},....]
And here is the filter function
function filterList(filterName, filterNumber) {
var filteredList = angular.copy(originalArray);
filteredList = filteredList.filter(function(item){
return item.name === name
}
return filteredList
}
My question is am I using the right way to implement this feature? suppose that user clicks search button 10000 times ! do I have a 10000 copy of my originalArray?
As filter returns just an array, use that instead and you won't need to use angular.copy
function filterList(filterName, filterNumber) {
return originalArray.filter(function(item){
return item.name === name
}
}
That said there are better ways of doing this if you're doing this from a view. Angular already has built in tools for filtering.
As str commented - you don't need to copy the array, filter returns you a new array with only the appropriate items.
You should take a look at ngFilter - there is an example there and it looks very much what you are looking for, effortless.
In angular js filter, if i write "subhadeep" then "pal" then first it filters with the value subhadeep and then it filters the value with pal. But in my requirement i need to design a filter which will return those object which contain either "subhadeep" or "pal" or both.
So filter will return those JSON object which contains either of the value so that i can populate those in ng-repeat.
JSON structure can vary . So i need a code that will search through all fields of each JSON objec. Please help.
Something like this:
angular.module('myApp')
.filter('myFilter', function(object, requiredValues) {
var filteredObject = {};
angular.forEach(object, function(value, key) {
if(requiredValues.indexOf(value) !== -1)
filteredObject[key] = value;
});
return filteredObject;
}
I have the following code in my controller:
$scope.filteredTransactions = $scope.invoiceTransactionsObject.transactions.concat(); // make a copy of the initial array
if ($scope.searchTerm.message)
{
var search = $scope.searchTerm.message;
$scope.filteredTransactions = $filter('filter')($scope.filteredTransactions, ({ message: search } || { item: search }));
}
I want to be able to filter by typing some value and search either in the message column or item column. According to How to filter multiple values (OR operation) in angularJS it should work, but it doesn't and if I type something that can be found in the message, it works, but when I type something from the item, it returns empty array.
Do you see where is my mistake?
Update Deleted irrelevant/mistaken initial answer
Since you're applying $filter inside a JS script, and it doesn't use any of the advanced features of $filter, I'd switch over to the JS-native method of filtering an array:
$scope.filteredTransactions = $scope.invoiceTransactionsObject.transactions.concat(); // make a copy of the initial array
if ($scope.searchTerm.message)
{
var search = $scope.searchTerm.message;
$scope.filteredTransactions = $scope.filteredTransactions.filter(function (trans) {
return trans.message.toLowerCase().indexOf(search) >= 0 || trans.item.toLowerCase().indexOf(search) >= 0;
});
}
...assuming that $filter is case-insensitive and matches anywhere in the string. If that's not the functionality of $filter and/or not what you want, you'd adjust the code accordingly.
Please, consider the following example:
Template:
<body ng-controller="MainCtrl">
Search: <input ng-model="search.$" ><br>
Search by tag: <input ng-model="search.tag" >
<p ng-repeat="item in items | filter:search">
<span>{{item.content}}</span> <span>{{item.tag}}</span>
</p>
</body>
Script:
app.controller('MainCtrl', function ($scope, $filter, filterFilter) {
$scope.items = [
{content:'3333113', tag:['a','b','c']},
{content:'111111g', tag:['b','c','d']},
{content:'345345', tag:[]},
{content:'2221122', tag:['c','d','e']},
{content:'2221122', tag:[]},
{content:'333', tag:[]}
];
});
When searching via the first input ng-model="search.$" with any data everything is Ok.
When searching via the seond input ng-model="search.tag" search does work by tags like a, b, but when it is cleared the restored array lacks the items which had empty search value, e.g. {content:'2221122', tag:[]} in this example.
jsBin example
Why does it happen ? Is there an easy way to avoid it ?
Short answer:
here is a working code: http://jsbin.com/AwunOyAt/2
You need a directive to make search.tag undefined when it's empty:
Directive:
app.directive('modelUndefined', function(){
return {
require: 'ngModel',
link: function(scope,elm,attrs,ngModel){
ngModel.$parsers.push(function(val){
return val === "" ? undefined : val;
});
}
}
});
html:
<input ng-model="search.tag" model-undefined>
Long answer:
From the docs of filter:filter:
In HTML Template Binding
{{ filter_expression | filter:expression:comparator }}
Parameters#expression
Object: A pattern object can be used to filter specific properties on objects contained by array. For example {name:"M", phone:"1"} predicate will return an array of items which have property name containing "M" and property phone containing "1". A special property name $ can be used (as in {$:"text"}) to accept a match against any property of the object. That's equivalent to the simple substring match with a string as described above.
As you can see, the filter expression can be an object with more then one predicates.
how to trace it?
Initially ngModel won't set search.tag until there is an input so it's still undefined.
First I pass an input into search.$, the search object looks like so:
$scope.search = {
'$' : 'something'
}
Then I'll pass something into search.tag, the search object:
$scope.search = {
'$' : 'something',
'tag': 'anything'
}
But when I clear it then the search object still have the tag property
$scope.search = {
'$' : 'something',
'tag': ''
}
filter:filter filters based on both predicates, this is the source code:
case "object":
// jshint +W086
for (var key in expression) {
(function(path) {
if (typeof expression[path] == 'undefined') return;
predicates.push(function(value) {
return search(path == '$' ? value : (value && value[path]), expression[path]);
});
})(key);
In our case the expression is the search object , and the paths are $ and tag.
See this line: if (typeof expression[path] == 'undefined') return;
If we set search.tag = undefined , the filter ignores it.
But If we set search.tag = '' this tag path is added to the predicates check array.
How to enforce ngModel to make search.tag undefined when it's empty?
See the directive above, you need to use ngModelController#$parsers to change the way the view value is converted when it updates the model.
I do not know if it helps but I put in the following:
<p>{{search.tag == undefined}}</p>
It then showed that initially it is undefined und later on it is an empty string (if you empty the input). The search results then kind of make sense.
Looks like there is a change in AngularJS version 1.2.10. Using https://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js does not eliminate empty entries.