Is it possible to exclude items in an ng-repeat?
For example, I have something similar to the following (simplified to keep things shorter):
<div class="row" data-ng-repeat="entry in data.feed.entry | orderBy:'gsx$timestamp.$t':true">
{{entry.gsx$jobID.$t}}
</div>
I have another scope object called exclusionData that is structured in a similar fashion. I want to exclude any items in my first ng-repeat that appear in an exclusionData.feed.entry.gsx$jobID.$t
Or is there an easier way to do this back in my controller (i.e. do the exclusion in data right away)? Both the data and the exclusionData is sourced from two different JSON feeds.
This will exclude the matching data simply adding an ng-if, and adding a bang to make it falsy.
<div
class="row"
data-ng-repeat="(key, entry) in data.feed.entry | orderBy:'gsx$timestamp.$t':true"
ng-if="!exclusionData.feed[key]gsx$jobID.$t">
{{entry.gsx$jobID.$t}}
</div>
You can either use a filter shown below, or just an ng-if/ng-show
<div class="row" data-ng-repeat="entry in data.feed.entry | orderBy:'gsx$timestamp.$t':true | filter: {gsx$exlucde.$t: true}">
{{entry.gsx$jobID.$t}}
</div>
data.feed.entry = [{
gsx$jobID.$t: 'something',
gsx$exlucde.$t: true,
gsx$timestamp.$t: '1/1/1990'
}]
the easiest way to do this is with a filter directive in your repeat, but you could also do this in your controller.
It depends on how your application looks like from a OO design perspective.
You can create a filter like this:
angular.module('appFilters', []).filter('yourFilterName', function() {
return function(input, key) {
var myList = [];
for(var i = 0; i < input.length; i++){
var found = _.find($scope.exclusionData, function() { // You can replace this with your own function.
// Your own logic here
});
if(found){
myList.push(input[i]);
}
}
return myList;
};
});
I am assuming you have underscore included in you project, but you can implement your own search function instead.
The in your template you pass the filter like this:
<div class="row" data-ng-repeat="entry in data.feed.entry | yourFilterName | orderBy:'gsx$timestamp.$t':true">
{{entry.gsx$jobID.$t}}
</div>
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.
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.
I have a big object that looks something like this :
scope.marketplaces = {
first_example : { ... },
second_example : { ... },
...
};
What I'm trying to do is loop through the big object like this :
<section>
<ul>
<li ng-repeat="(key, value) in marketplaces"></li>
</ul>
</section>
And inside the loop, loop again each object, but instead of appending to DOM something like :
<li> ... first level loop ...
<li> ... second level loop ... </li>
</li>
I would like to have just one <li></li> despite the level I'm looping through. The reason why I want it like that is because I need the key from the first loop to be referenced down the level loops and still have just one li.
I've read that ng-repeat="friend in friends | filter:searchText" could do what I want but I'm not sure if in the filter expression I can dynamically specify the key or something else that is needed instead of searchText ( I guess that is an already know property of the object ).
So my question is, can I achieve what I just explained with the filter or is there another way of doing it ?
I hope I've understood question: you would like to have one ngRepeat on a nested object. So kind of linearize object.
First approach would be to create filter:
app.filter('linear', function() {
return function(value) {
var result = {};
for(var key in value) {
for(var cKey in value[key]) {
result[key+'_'+cKey] = value[key][cKey];
}
}
return result;
};
});
and in thml:
<li ng-repeat="(key, value) in marketplaces | linear">
{{key}}: {{value}}
</li>
But unfortunally AngularJS have problems when in filter you create new elements. You can have error message in console kind of:
10 $digest iterations reached
Without hacks with $$hash you can't achieve that functionality for now (Please correct me if I am wrong).
So I think for now the solution would be to watch 'marketplaces' in controller and create new object using same code as in controller that use in ngRepeat:
$scope.$watch('marketplaces', function(value) {
var result = {};
for(var key in value) {
for(var cKey in value[key]) {
result[key+'_'+cKey] = value[key][cKey];
}
}
$scope.linearMarketplaces = result;
}, true);
And in HTML:
<li ng-repeat="(key, value) in linearMarketplaces">
{{key}}: {{value}}
</li>
Plunker with both solutions: http://plnkr.co/edit/pYWFhqSqKAa9gpzek77P?p=preview