I have a simple ng-repeat like that:
<input type="text" ng-model="vm.filter">
<div ng-repeat="tenant in vm.tenants | filter : vm.filter : vm.contains">
</div>
Now I want to filter tenants based on the value of the filter, e.g. to find if a name of a tenant contains the filter expression like that
function contains(actual, expected) {
return actual.name.indexOf(expected) >= 0;
}
What I do not understand is why I get a tenant.name in the contains function instead of the tenant itself. I know for a simple case I can do something like filter | {name: vm.filter} | vm.contains, but what if I want to do filtering based on multiple properties (e.g. name, phone, etc.)
What I do not understand is why I get a tenant.name in the contains
function instead of the tenant itself.
What's happening is that angular is providing the comparator all levels of the object in an attempt to do a deep filtering i.e. it attempts to match your filter to all hierarchy levels in the object. If you console.log all the values, you'll see that you get the full object too (after all the properties).
One way to make this work would be something like
...
<div ng-repeat="tenant in vm.tenants | filter : vm.contains(vm.filter)">
...
and then
...
contains: function(filter) {
return function(tenant) {
return tenant.name.indexOf(filter) !== -1;
}
}
...
Fiddle - https://jsfiddle.net/81384sd7/
You can use an object to store the filters you want to apply. For example:
$scope.filterBy = {
name: "Bob",
phone: "07"
};
Then configure fields to edit the filters:
<input type="text" ng-model="filterBy.name" />
<input type="text" ng-model="filterBy.phone" />
Then just filter by this object.
<div ng-repeat="tenant in vm.tenants | filter : filterBy">
Take a look at my previous answer which is very similar except also allows selections to be made before a filter being applied manually with a button:
Applying a angularjs filter after "Apply" button click
I hope this helps.
You'll want to test for the presence of both those conditions and return the value if both are present. Something like this:
function contains(actual, expected) {
if (actual.name.indexOf(expected) >= 0 && actual.phone.indexOf(expected) >= 0) {
return actual;
}
}
At the moment I have a json result which is displayed in an ng-repeat that I would like to have filtered based on a separate object or array of data:
Controller.js
$scope.jsonResult = [
{
"id": "a123"
},
{
"id": "b456"
}
]
HTML
<span ng-repeat="r in jsonResult">{{r.id}}</span>
What I am attempting to achieve is to create a separate array of info and then use this data to filter the ng-repeat results that display in my HTML.
$scope.itemsToFilter = ["b456","foo"]
Since an item within my itemsToFilter array matches an object within my jsonResult scope, I would like that not to display within my ng-repeat within the HTML. Is this something I should write a custom filter for? Sorry I am very new to Angular.
You can create an Angular filter which returns the array filtered by items' ids which match your blacklist:
angular.module("yourAppName")
.filter("filterArrayByBlacklist", function() {
blackList = ["b456","foo"];
return function(array) {
return array.filter(function(item) {
return blackList.indexOf(item.id) === -1; // item id not found in blacklist
});
};
});
You can then filter the ng-repeat by your filter function:
<span ng-repeat="r in jsonResult | filterArrayByBlacklist">{{r.id}}</span>
See this plnkr for a demo: http://plnkr.co/edit/VK3jiVBpL0e1G06WmPZq
The easiest way is by using a custom filter. A quick filter can be written like this:
$scope.filterByStrings = function (item) {
return ($scope.itemsToFilter.indexOf(item.id) === -1);
};
And called like this:
<div ng-repeat="item in data | filter:filterByStrings ">
<p>{{item.id}}</p>
</div>
Here is the fiddle.
$scope.items = [];
var ref = new Firebase("https://****.firebaseio.com/");
var query = ref.limit(5);
angularFire(query, $scope, "items");
<div ng-repeat="item in items" >
<span>{{item.title}}</span>
</div>
How would you sort these by "title"? I've seen a couple similar questions but the solutions don't seem to work, at least for this situation.
If $scope.items were an array of objects (as implied by $scope.items = []), you would be able to use the angular orderBy filter:
<!-- This example will NOT work with Firebase -->
<div ng-repeat="item in items | orderBy:'title'">
<span>{{item.title}}</span>
</div>
However, setting items to an array as you did is misleading, because you are actually storing (and retrieving) your "items" as a list of key value pairs, not an array. Sorting objects in javascript is more complicated in general, but one way to achieve what you want is a custom filter:
app.filter('orderObjectBy', function(){
return function(input, attribute) {
if (!angular.isObject(input)) return input;
var array = [];
for(var objectKey in input) {
array.push(input[objectKey]);
}
function compare(a,b) {
if (a[attribute] < b[attribute])
return -1;
if (a[attribute] > b[attribute])
return 1;
return 0;
}
array.sort(compare);
return array;
}
});
Now you can use your filter to order by any of your item properties:
<div ng-repeat="item in items | orderObjectBy:'title'">
<span>{{item.title}}</span>
</div>
Here is a fiddle to demonstrate. As #bennlich noted below, your custom filter could stop at the array conversion and then you could use the normal orderBy filter, as suggested here.
And just to be thorough I should note that you could also sort "server-side" with Firebase priorities.
I have a search box that takes a series of terms, separated by spaces, that are split into an array using string.split(' '). Is there a way to apply an arbitrary and potentially large number of filters from that array?
Similar to Thad, but actually creating a custom filter module rather than cluttering up the controller. This is how it has worked for me:
<input type="text" ng-model="query">
<li ng-repeat="object in objects | filterFunction:query"> {{object.state}}
.filter('filterFunction', function() {
return function (objects, query) {
// objects is the array being filtered
// query is the value you passed in
array = query.split(' ');
for (var i = 0, len = objects.length; i < len; i++) {
// filter the crap out of these objects
}
}
});
I think that this is where you will need to use a predicate function filter instead of a string filter...
like this (pseudocode):
<!--html-->
<ul>
<li ng-repeat="object in objects | filter:filterFunction">
{{object.description}}
</li>
</ul>
<!-- controller -->
$scope.objects = [ your data here ];
$scope.filterFunction = function(searchVal) {
// write code that returns true or false depending
// on whether or not after you split searchVal any
// of the elements are in your data
}
I haven't tried this, but this is how the documentation says to do it.
-Thad
great multi-filter here:
https://gist.github.com/i8ramin/5825377
Angular JS filter that behaves much like the built in filter one, but allows you to filter with multiple values, space separated. For example "big orange round"
How can i get a reversed array in angular?
i'm trying to use orderBy filter, but it needs a predicate(e.g. 'name') to sort:
<tr ng-repeat="friend in friends | orderBy:'name':true">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
<td>{{friend.age}}</td>
<tr>
Is there a way to reverse original array, without sorting.
like that:
<tr ng-repeat="friend in friends | orderBy:'':true">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
<td>{{friend.age}}</td>
<tr>
I would suggest using a custom filter such as this:
app.filter('reverse', function() {
return function(items) {
return items.slice().reverse();
};
});
Which can then be used like:
<div ng-repeat="friend in friends | reverse">{{friend.name}}</div>
See it working here: Plunker Demonstration
This filter can be customized to fit your needs as seen fit. I have provided other examples in the demonstration. Some options include checking that the variable is an array before performing the reverse, or making it more lenient to allow the reversal of more things such as strings.
This is what i used:
<alert ng-repeat="alert in alerts.slice().reverse()" type="alert.type" close="alerts.splice(index, 1)">{{$index + 1}}: {{alert.msg}}</alert>
Update:
My answer was OK for old version of Angular.
Now, you should be using
ng-repeat="friend in friends | orderBy:'-'"
or
ng-repeat="friend in friends | orderBy:'+':true"
from https://stackoverflow.com/a/26635708/1782470
Sorry for bringing this up after a year, but there is an new, easier solution, which works for Angular v1.3.0-rc.5 and later.
It is mentioned in the docs:
"If no property is provided, (e.g. '+') then the array element itself is used to compare where sorting". So, the solution will be:
ng-repeat="friend in friends | orderBy:'-'" or
ng-repeat="friend in friends | orderBy:'+':true"
This solution seems to be better because it does not modify an array and does not require additional computational resources (at least in our code). I've read all existing answers and still prefer this one to them.
Simple solution:- (no need to make any methods)
ng-repeat = "friend in friends | orderBy: reverse:true"
You can reverse by the $index parameter
<tr ng-repeat="friend in friends | orderBy:'$index':true">
You can just call a method on your scope to reverse it for you, like this:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.0.5/angular.min.js"></script>
<script>
angular.module('myApp', []).controller('Ctrl', function($scope) {
$scope.items = [1, 2, 3, 4];
$scope.reverse = function(array) {
var copy = [].concat(array);
return copy.reverse();
}
});
</script>
</head>
<body ng-controller="Ctrl">
<ul>
<li ng-repeat="item in items">{{item}}</li>
</ul>
<ul>
<li ng-repeat="item in reverse(items)">{{item}}</li>
</ul>
</body>
</html>
Note that the $scope.reverse creates a copy of the array since Array.prototype.reverse modifies the original array.
if you are using 1.3.x, you can use the following
{{ orderBy_expression | orderBy : expression : reverse}}
Example List books by published date in descending order
<div ng-repeat="book in books|orderBy:'publishedDate':true">
source:https://docs.angularjs.org/api/ng/filter/orderBy
If you are using angularjs version 1.4.4 and above,an easy way to sort is using the "$index".
<ul>
<li ng-repeat="friend in friends|orderBy:$index:true">{{friend.name}}</li>
</ul>
view demo
When using MVC in .NET with Angular you can always use OrderByDecending() when doing your db query like this:
var reversedList = dbContext.GetAll().OrderByDecending(x => x.Id).ToList();
Then on the Angular side, it will already be reversed in some browsers (IE). When supporting Chrome and FF, you would then need to add orderBy:
<tr ng-repeat="item in items | orderBy:'-Id'">
In this example, you'd be sorting in descending order on the .Id property. If you're using paging, this gets more complicated because only the first page would be sorted. You'd need to handle this via a .js filter file for your controller, or in some other way.
You can also use .reverse(). It's a native array function
<div ng-repeat="friend in friends.reverse()">{{friend.name}}</div>
That's because you are using JSON Object. When you face such problems then change your JSON Object to JSON Array Object.
For Example,
{"India":"IN","America":"US","United Kingdon":"UK"} json object
[{"country":"India","countryId":"IN"},{"country":"America","countryId":"US"},{"country":"United Kingdon","countryId":"UK"}]
The orderBy filter performs a stable sorting as of Angular 1.4.5. (See the GitHub pull request https://github.com/angular/angular.js/pull/12408.)
So it is sufficient to use a constant predicate and reverse set to true:
<div ng-repeat="friend in friends | orderBy:0:true">{{friend.name}}</div>
I found something like this, but instead of array i use objects.
Here is my solution for objects:
Add custom filter:
app.filter('orderObjectBy', function() {
return function(items, field, reverse){
var strRef = function (object, reference) {
function arr_deref(o, ref, i) {
return !ref ? o : (o[ref.slice(0, i ? -1 : ref.length)]);
}
function dot_deref(o, ref) {
return !ref ? o : ref.split('[').reduce(arr_deref, o);
}
return reference.split('.').reduce(dot_deref, object);
};
var filtered = [];
angular.forEach(items, function(item) {
filtered.push(item);
});
filtered.sort(function (a, b) {
return (strRef(a, field) > strRef(a, field) ? 1 : -1);
});
if(reverse) filtered.reverse();
return filtered;
};
});
Which can then be used like
<div ng-repeat="(key, value) in items | orderObjectBy:'field.any.deep':true">
If you need old browser support, you will need to define the reduce function (this is only available in ECMA-262 mozilla.org)
// Production steps of ECMA-262, Edition 5, 15.4.4.21
// Reference: http://es5.github.io/#x15.4.4.21
if (!Array.prototype.reduce) {
Array.prototype.reduce = function(callback /*, initialValue*/) {
'use strict';
if (this == null) {
throw new TypeError('Array.prototype.reduce called on null or undefined');
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
var t = Object(this), len = t.length >>> 0, k = 0, value;
if (arguments.length == 2) {
value = arguments[1];
} else {
while (k < len && !(k in t)) {
k++;
}
if (k >= len) {
throw new TypeError('Reduce of empty array with no initial value');
}
value = t[k++];
}
for (; k < len; k++) {
if (k in t) {
value = callback(value, t[k], k, t);
}
}
return value;
};
}
I had gotten frustrated with this problem myself and so I modified the filter that was created by #Trevor Senior as I was running into an issue with my console saying that it could not use the reverse method. I also, wanted to keep the integrity of the object because this is what Angular is originally using in a ng-repeat directive. In this case I used the input of stupid (key) because the console will get upset saying there are duplicates and in my case I needed to track by $index.
Filter:
angular.module('main').filter('reverse', function() {
return function(stupid, items) {
var itemss = items.files;
itemss = itemss.reverse();
return items.files = itemss;
};
});
HTML:
<div ng-repeat="items in items track by $index | reverse: items">
Im adding one answer that no one mentioned. I would try to make the server do it if you have one. Clientside filtering can be dangerous if the server returns a lot of records. Because you might be forced to add paging. If you have paging from the server then the client filter on order, would be in the current page. Which would confuse the end user. So if you have a server, then send the orderby with the call and let the server return it.
Useful tip:
You can reverse you're array with vanilla Js: yourarray .reverse()
Caution: reverse is destructive, so it will change youre array, not only the variable.
I would sugest using array native reverse method is always better choice over creating filter or using $index.
<div ng-repeat="friend in friends.reverse()">{{friend.name}}</div>
Plnkr_demo.