reorder list randomly in ng-repeat - angularjs

I have an ng-repeat as following :
<ul>
<li ng-repeat="o in villes | limitTo:5">{{o.nomVille}}</li>
</ul>
I want to reorder the villes list randomly before I limit it to 5, so every time I open my page I get 5 different villes each time.
is there a filter in angularjs who can do that for me ?
edit :
I created a costum filter to randomize that list as following :
.filter('random', function() {
return function(val) {
let shuffle = (a) => {
let r = [];
while (arr.length)
r.push(
arr.splice( (Math.floor(Math.random() * arr.length)) , 1)[0]
);
return shuffle(val);
}
};
});
and in ng-repeat I did this :
<li ng-repeat="o in villes | random | limitTo:5">{{o.nomVille}}</li>
but I cant no longer see anything in my page.
this is the example on jsfiddle : https://jsfiddle.net/z10wwhcv/

You'd have to build a custom filter function that randomizes the order and then apply the filters in the correct order (random before limit).
Have a look at this for details:
Using AngularJS how could I randomize the order of a collection?

If you want the order to change each time you load the page you can't do it as a filter as that will presumably change on each digest cycle. You need to store villes on the scope somewhere and generate it in a random order when the page loads, e.g. in your controller function for that page.

use the filter in the controller (which is also a best practice performance boost):
$scope.shuffled = $filter('random',$scope.villes)
you'll need to inject the service in the controller
this is what your filter should look like ( not tested but should work ):
.filter('random', function() {
return function(a) {
var r = [];
while (a.length)
r.push(
a.splice((Math.floor(Math.random() * a.length)), 1)[0]
);
return r;
}
}

This is the solution I created :
shufflemodule.filter('shuffle', function() {
var shuffledArr = [],
shuffledLength = 0;
return function(arr) {
var o = arr.slice(0, arr.length);
if (shuffledLength == arr.length) return shuffledArr;
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
shuffledArr = o;
shuffledLength = o.length;
return o;
};
});
http://jsfiddle.net/aimad_majdou/6mngvo38/

Related

Angular loop through JSON with a limited number

Angular has a built-in feature to loop through JSON, for example I can do:
$scope.users = data.users
But I want to only loop through a number of users to enhance the performance like:
$scope.users = data.users[5] to data.users[10]
How would I be able to do this?
P.S. for pure javascript I can do:
for(var i = 5; i <= 11; i++) {
var user = data.users[i];
}
Of course this cannot add user to the scope.
One way is to use limitTo filter in view
<div ng-repeat="item in items | limitTo : limit.itemCount : limit.startIndex">
And in controller set variables that you can easily change to show different items or quantities
$scope.limit={
startIndex :0,
itemCount : 10
}
$scope.next = function(){
$scope.limit.startIndex += $scope.limit.itemCount;
}
You can use the slice method of an array.
$scope.users = data.users.slice(5, 11);

how to pass an argument to angularjs custom filter

i want to be able to pass different price point to the filter but the way it defined right now it not working unless i hard code it in eg priceArray.push(value.price)
var priceSumUp = function ($log){
return function(arrayObj, nameValue){
var priceArray = [],
totalPrice = 0;
function sumUp(first, second){return first + second}
ng.forEach(arrayObj, function(value, key, obj){
priceArray.push(value.price);
});
totalPrice = priceArray.reduce(sumUp, 0);
//console.log(priceArray);
return totalPrice;
};
}
<div class="" ng-bind-html="vm.getisSelected | priceSumUp:price"></div>
I think what you are looking for is this...
First make sure you are passing the right argument. Either change it to
$scope.price = 'price'
// or
<div ng-bind="items | priceSumUp:'price'"></div>
Next you can use the array-like ECMA object properties to access the item...
return function(arrayObj, v){
var priceArray = arrayObj.map(function(obj){
return obj[v]
})
...
}
See my plunker here this would also allow for say...
<div ng-bind="items | priceSumUp:'tax'"></div>

count value in a array into ng-repeat

I have this data : http://www.monde-du-rat.fr/API/moulinette/radio/posts.json , from cakephp app, it's song by song, with attached votes
I use it as a service into a angularjs app, and i displayed it like this in html :
<div ng-repeat="song in songs | orderBy:'Radio.idSong' | notesong:'Radiovote'" class="list-group-item" id="{{song.Radio.idSong}}" ng-class="{ 'active' : songPlayed_name == song.Radio.name }" ng-if="songs">
<span>{{song.Radio.idSong}} - {{song.Radio.title}}</span><br />
<span>{{note}}%</span>
</div>
So, i want to count each attached vote, and define with values 'good' or 'bad', the % of likes
I try to made this filter :
/* notesong */
app.filter('notesong', function() {
return function(input) {
// counter init
var countGood = 0;
// if there is no votes, so note is zero
if (!angular.isArray(input)) {
var note = 0;
} else {
// loop for each vote (from Radiovote array, each value)
angular.forEach(input, function () {
if (input.value == 'good') {
countGood = countGood + 1;
}
});
var note = (countGood * input.length) / 100;
}
// final return
return note;
};
});
It's not working apparently (no errors, and no data displayed), so, what is the correct way ?
You are applying the filter in the wrong place. Instead of using it on the ng-repeat you should use it on the property you want to bind, like this:
<div ng-repeat="song in songs | orderBy:'Radio.idSong'" class="list-group-item" id="{{song.Radio.idSong}}" ng-class="{ 'active' : songPlayed_name == song.Radio.name }" ng-if="songs">
<span>{{song.Radio.idSong}} - {{song.Radio.title}}</span><br />
<span>{{song.Radiovote | notesong}}%</span>
</div>
There's also a problem with the way you are looping the votes in your filter. Update the following lines:
// loop for each vote (from Radiovote array, each value)
angular.forEach(input, function (item) {
if (item.value == 'good') {
countGood = countGood + 1;
}
});

Filter a ng-repeat with values from an array

I have this table with a ng-repeat.
ng-repeat="project in projects"
I have a property in project, prj_city. I'd like to filter this value.
I can do this with:
ng-repeat="project in projects | filter={prj_city: <value>}
But I want the <value> to be an array with multiple cities instead of a string. Is there any easy way to do this or do I have to do this filter manually in my controller?
Most likely a custom filter in the controller, should be easy enough tho:
var filteredCities = ["LosAngelos", "etc.."];
$scope.arrayFilter = function(project) {
for (var i = 0; i < filteredCities.length; i++) {
if (filteredCities[i] == project.prj_city)
return true;
}
return false
}
And the call:
ng-repeat="project in projects | filter: arrayFilter"
You need to create a filter function on your controller. Something like:
$scope.filteredCities = function(city) {
return ($scope.userFilteredCities.indexOf(city) !== -1);
};
$scope.userFilteredCities;//List of filtered cities
Define the following function in your controller:
// use a map for faster filtering
var acceptedCityMap = {};
angular.forEach(acceptedCities, function(city) {
// case insensitive search. But you're not forced to
acceptedCityMap[city.toLowerCase()] = true;
});
$scope.isProjectedAccepted = function(project) {
// case insensitive search. But you're not forced to
return acceptedCityMap[project.prj_city.toLowerCase()];
}
And then in your view:
ng-repeat="project in projects | filter:isProjectAccepted"

AngularJS Filter-- two filters with an 'or' relationship

Just wondering: in AngularJS, is there a native way to filter such that it has an 'or' relationship instead of the 'and' ?
for example:
<tr ng-repeat="account in accounts | filter1 *OR* filter2 *OR* filter3" >
so if any of the filters match, it returns that object. As of right now, all three have to pass in order for it to show up.
Thanks a lot,
Y
You could write a custom filter. It could accept the names of other filters and check each one of them to see if each item in the accounts array is a valid match against any of the filters. The following example shows kind of the idea, thought I haven't actually run the code to see if it works, so forgive me for any typos:
app.filter('anyOf', function($filter) {
return function(){
var array = arguments[0];
var result = [];
angular.forEach(array, function(item){
for(var i=1; i<arguments.length; i++){
var filter = $filter(arguments[i]);
if(filter([item]).length){
result.push(item);
break;
}
}
});
return result;
}
});
And then you would use it like so:
<tr ng-repeat="account in accounts | anyOf:'filter1':'filter2':'filter3'" >
I think it should work, though its not very efficient because its taking every item in the input array and checking it against any of the filters. But that might work if your accounts array wasn't super long.
You can use $filter to call filters in your own code, so you can pass the filter names as arguments to a new filter to merge the results (JSFIDDLE).
app.filter('merge', function($filter) {
return function(input, extra) {
var result = [];
for (var i = 1; i < arguments.length; i++) {
angular.forEach($filter(arguments[i])(input), function(item) {
if (result.indexOf(item) < 0) {
result.push(item);
}
});
}
return result;
};
});
And you can pass arguments to your filter like this:
<ul>
<li ng-repeat="item in items|merge:'color':'rating'">
{{item.rating}}: {{item.color}}
</li>
</ul>
If you wanted to pass arguments to the filter you're merging, you could parse them yourself in the merge filter:
<h3>Merge brown and rating >= 5</h3>
<ul>
<li ng-repeat="item in items|mergeWithArgs:['color','brown']:['rating',5]">
{{item.rating}}: {{item.color}}
</li>
</ul>
Code:
app.filter('mergeWithArgs', function($filter) {
return function(input) {
console.dir(arguments);
var result = [];
for (var i = 1; i < arguments.length; i++) {
var params = arguments[i];
var filter = $filter(params[0]);
// remove filter name, prepend input
params.splice(0, 1, input);
angular.forEach(filter.apply(this, params), function(item) {
if (result.indexOf(item) < 0) {
result.push(item);
}
});
}
return result;
};
});

Resources