ng-repeat auto sorting incorrectly - angularjs

I know in my current version of Angular, ng-repeat auto sorts. My problem is that its sorting doesn't appear to be correct.
Taking an object with numbered keys:
{
"1": "value",
"2": "value",
"10": "value"
}
ng-repeat sorts it 1, 10, 2. I'm familiar with this sort of sorting and throwing a 0 on the front of number should fix it. However adding that 0 requires additional code and would need to be stripped out for display.
Likewise, converting to an array caused ng-repeat to loop through all of the empty values (3-9) and creates excess elements, as well as generating a duplicate error.
How can I make ng-repeat sort an object by keys as if they were integers?

I was not able to find a solution that didn't change your data structure, but here is an example of how it can be done by using keys to sort the object into an array. Here is the html:
<ul ng-controller="ExampleController">
<li ng-repeat="item in filtered">{{ item.value }}</li>
</ul>
And here is the code:
controller('ExampleController', ['$scope', function($scope) {
$scope.list = {
"1": "1 Value",
"10": "10 Value",
"5": "5 Value",
"2": "2 Value"
};
$scope.$watch('list', function (newVal) {
$scope.filtered = Object.keys(newVal).map(function (key) {
return { key: key, value: newVal[key] };
});
});
}]);
The output of this code looks like this:
1 Value
2 Value
5 Value
10 Value
Note that this creates an array of key/value pair objects which is easier to work with. Here is a plunker example of it working: http://plnkr.co/edit/Q0BYLeMzTZmd1k8VzTlQ?p=preview

Related

angularjs filter multidimensional object json

I wnt to use ng-repeat to display a list filtered by an object value. Here is a plukr of my attempt https://plnkr.co/edit/vD4UfzM4Qg7c0WGTeY18?p=preview
The following returns all of my JSON names as expected.
<li ng-repeat="item in collection_data">{{navitem.name}}</li>
now i want to filter and only show the names of the items that have "foreign_lang": "es", like in this json snippet
{
"id": "ddb06ba2-6348-4d45-9e63-a6fa3632e5c2",
"created_at": "2015-10-12T18:34:15.668Z",
"updated_at": "2016-04-14T15:55:37.433Z",
"custom_attributes": {
"Display Name": "Activos en Español",
"foreign_lang": "es",
"display_boxes": "false"
},
},
so i made this filter function
$scope.filterByDisplay = function() {
$filter('filter')($scope.collection_data, ['foreign_lang', 'es']);
}
and called it like this.
<li ng-repeat="item in collection_data" | filter: filterByDisplay>{{navitem.name}}</li>
I did not get any console errors but i got nothing returned.
How do I properly filter through this collection to only return items with 'foreign_lang', 'es' as a value in the json? See the plunkr to see a working example https://plnkr.co/edit/vD4UfzM4Qg7c0WGTeY18?p=preview
Third attempt (since the question was revised). Use the filter function to check each object individually, and returning only those that pass the truth test.
$scope.filterByDisplay = function(value) {
return (value.content)
&& (value.content.custom_attributes)
&& (value.content.custom_attributes.foreign_lang === "es");
}
Updated Plunk - Using Filter Function

How do I filter angular results by id and other keys?

I want to filter results by id, or rating and various other keys, I'm using this data structure:
[
{
"id": "1"
"Description": "desc 1",
"Rating": "rating 1",
"MainImage": "image.jpg"
},
{
"id":"1"
"Description": "desc 2",
"Rating": "rating 2",
"MainImage": "image.jpg"
},
{
"id": "2"
"Description": "desc 3",
"Rating": "rating 3",
"MainImage": "image.jpg"
}
]
This data is returned from a promise and is assigned to $scope.results. In the template there is an ng-repeat to iterate over the results. This is working fine, my question is:
How do I filter the results by id so for example only the results with the id of 1 are displayed? I had this working but it wasn't the most efficient. I reassigned the filtered results back to $scope.results which did work but then the entire data structure had been replaced by the one containing the filtered results. That obviously wasn't going to work and I did a work around but I know this isn't the best way.
I need a custom filter that will be able to handle filtering using 3 different select lists so for example a rating select list, a productId and a productName.
How exactly would I write this function?
<div class="product" data-ng-repeat="product in products | filter:searchFilter"></div>
I ended up doing something that I found here and created a function in the backend. Something like:
$scope.searchFilter = function (item) {
return (item.id === $scope.results.id)
}
This code isn't exactly what I've used but that's the general idea. Seems to work :)
//This will filter the product list based on all 3 criteria
<div class="product" data-ng-repeat="product in products | filter:{rating:selectedRating, id:selectedId,productName:selectedProduct }"></div>
This is how I do it.
<input type="number" ng-modal="idFilter:selectedID">
<div ng-repeat="result in results | idFilter:selectedID | track by $index">
<something-repeated>
</div>
<script>
angular.module('whatever').filter('idFilter', function(){
return function(results, selectedID){
return results.filter(function(result){
return result.id == selectedID;
});
}
});
</script>

ng-repeat gets wrong $index when sorting

I have simple object:
"socials": {
"f": "#/facebook/",
"B": "#/behance/",
"d": "#/dribble/",
"L": "#/linkedin/",
"P": "#/pinterest/"
}
When I try to use ng-repeat="(key, value) in data.socials" it sorts like this: B L P d f.
$index catches wrong index, just as it is ordered by already. How can I order it correctly as it is in jSON file? Or maybe how can I get correct index to use orderBy: correctIndex? I found a fiddle that shows an issue exactly like this: http://jsfiddle.net/zMjVp/
ngRepeat iterates over object keys in alphabetical order to manage tracking items by $index (see the code for ngRepeat on Github).
To control the order of iteration in ngRepeat you should convert your object structure into an Array which has guaranteed iteration ordering:
JavaScript:
var jsonData = {
"socials": {
"f": "#/facebook/",
"B": "#/behance/",
"d": "#/dribble/",
"L": "#/linkedin/",
"P": "#/pinterest/"
}
};
var socialsList = [];
angular.forEach(jsonData.socials, function(value, key){
socialsList.push({
key: key,
value: value
});
});
$scope.socialsList = socialsList;
HTML:
Then update your HTML to iterate over the list instead of the object.
<div ng-repeat="item in socialsList">
<p> {{item.key}} : {{item.value}} </p>
</div>
NOTE:
Unless you're using the delete operator you can generally rely on the order of the for...in loop. It's not guaranteed, but it's generally reliable. See the MDN documentation on for...in loops

In AngularJS, is it possible to filter inside of a specific element of an array using only a view?

I've got some data like this:
people = [
{
names: [
{first: "Joe", last: "Smith"},
{first: "Joseph", last: "Smith"},
...
]
},
...
]
In other words, an array of objects with an array of names. For example, a person could be called "Joe Smith" or "Joseph Smith". How can I use a filter to only search the first element of names? IE: If I typed in "Jo" or "Smith" it would find the first person. But, if I typed in "seph" it wouldn't.
I've been looking at the examples on this page, but there isn't really an example of filtering inside arrays. Here's what I've tried but it gives me an error:
<input ng-model="search.names[0].$">
TypeError: Cannot set property '$' of undefined
Working Code
Input HTML
<input ng-model="searchTerm">
Results
<tr ng-repeat="p in people | filter:searchFunc">...</tr>
Controller
$scope.searchFunc = function(person) {
var firstPersonsName = [person.names[0]]; // Wrapping in array since the 'filter' $filter expects an array
var matches = $filter('filter')(firstPersonsName, $scope.searchTerm); // Running firstPersonsName through filter searching for $scope.searchTerm
return matches.length > 0;
}
Plunker Demo
Answer to the question in your title
I played around with filter and it doesn't seem like you can go beyond one level deep when specifying a pattern object for it e.g. ng-model="search.names" works but ng-model="search.names.otherVal" doesn't.
Also, even if filter supported going beyond one level deep, you still wouldn't be able to do ng-model="search.names[0]". This is because filter expects the input to be an array, but the elements of your names array are all objects e.g. people[0].names[0] == {first: "Joe", last: "Smith"} so filtering will never work.
The only way to do what you are asking purely through the view and no extra code in your controller is to just create your own custom filter that handles your case.
Would this do the trick?
Say that your input is
<input ng-model="search.name" type="text">
Then, you can display results like this:
<div ng-repeat="person in people[0].names | filter: alias">...</div>
Controller:
$scope.alias = function (object) {
if (object.first.match(new RegExp($scope.search.name))) {
return true;
}
return false;
};

Angular ng-repeat filter with predicate function not works as expected

This is my first time playing with AngularJS, and actually, I'm following the getting-started tutorial. It came to my mind that I would tweak the tutorial scripts to my understandings, by just adding a little that was not in the tutorial.
Basically, the phone object used in the tutorial was:
{
"age": 1,
"id": "motorola-xoom",
"imageUrl": "img/phones/motorola-xoom.0.jpg",
"name": "MOTOROLA XOOM™",
"snippet": "The Next, Next Generation..."
}
What I was trying to do was to add an auto populated select box for order the list:
<select ng-model="orderProp">
<option ng-repeat="(key, value) in phones[0]" value="{{key}}">
{{labels[key]}}
</option>
</select>
and added a model labels to the controller:
$scope.labels = {
"name": "Phone name",
"snippet": "Description",
"age": "Newest",
};
It was working as expected, except that I only wanted to filter the 3 properties above, so I think it would be easy to add a custom predicated function for filtering like this:
$scope.isPhonePropFilterable = function (propName) {
console.log('it DOES NOT get here!!!');
return propName == 'name' || propName != 'snippet' || propName != 'age';
};
and added this to the ng-repeat
<option ng-repeat="(key, value) in phones[0] | filter:isPhonePropFilterable" value="{{key}}">
To my suprise, it was not as easy as I thought, my filter function was not called.
See it here: plunker
Did I do anything wrong?
edited: ng-repeat filter supports filtering array only, not object. The filter function returnes if array param is not an array...
Well, it was my fault. Ng-repeat filter supports array only, it did only mention array in the docs. And checking the filter function, it returned if array param is not an array....

Resources