Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ng-repeat directive sort the data when using (key, value)

Tags:

angularjs

I have a code something like this with ng-repeat = "(key,value) in data". In Controller:

  $scope.Dates = {"Today":"30",
                  "This Week":"42",
                  "This Month": "Oct",
                  "This Quarter" : "Bad",
                  "This Year" : 2013
                                }

and ng-repeat directive as

<div ng-repeat="(key,value) in Dates">
{{key}} ==> {{value}}
</div>

The output comes in sorted order as

This Month ==> Oct
This Quarter ==> Bad
This Week ==> 42 
This Year ==> 2013
Today ==> 30

How to get rid of this sorting(strange) as I want keys to be used in code.. I checked google group but there was a fiddle for using two arrays of which one was storing the key values. http://jsfiddle.net/Saulzar/puhML/3/b . Don't want to go with this approach.

like image 331
icanbeacoder Avatar asked Oct 30 '13 07:10

icanbeacoder


People also ask

How do you pass an index in NG-repeat?

Each ng-repeat creates a child scope with the passed data, and also adds an additional $index variable in that scope. So what you need to do is reach up to the parent scope, and use that $index . Save this answer.

How does ng-repeat work?

Definition and Usage The ng-repeat directive repeats a set of HTML, a given number of times. The set of HTML will be repeated once per item in a collection. The collection must be an array or an object. Note: Each instance of the repetition is given its own scope, which consist of the current item.

Why is NG-repeat not working?

Solution 1. There are two mistakes in your code: In your table, you have to wrap the properties between {{ and }}, for example {{employee. Fname}} instead of just employee.

How do you use two ng-repeat in a table?

NEST TWO ng-repeatThe first ng-repeat in the tr tag will create the rows and the second one in the td tag will create one column per element in the collection. By using one ng-repeat for the row and an other for the column, the rows and the columns of your table is now driven by a subset.


4 Answers

This is limitation of JavaScript not Angular.

From ECMAScript Third Edition:

4.3.3 An object is a member of the type Object. It is an unordered collection of properties each of which contains a primitive value, object, or function. A function stored in a property of an object is called a method.

From ECMAScript Language Specification

The [...] order of enumerating the properties [...] is not specified.

Angular sorts object keys explicitly in order to provide at least some sort of persistent behavior.

Workaround is to iterate over extracted keys:

<div ng-repeat="key in keys(Dates)">
  {{key}} ==> {{Dates[key]}}
</div>
$scope.keys = function(obj){
  return obj? Object.keys(obj) : [];
}

$scope.Dates = {
  "Today":"30",
  "This Week":"42",
  "This Month": "Oct",
  "This Quarter" : "Bad",
  "This Year" : 2013
};
like image 122
Stewie Avatar answered Oct 17 '22 18:10

Stewie


EDIT: I've filed a bug, feel free to +1

ECMAScript does not specify order in which keys should be iterated, however all major browsers implement objects as linked hash map (preserves order) and a lot of js libraries depend on this behavior and so we've got used to it and it's hardly going to change.

Angular on the other hand (which is totally unexpected) sort it alphabetically. I've inspected the source code myself, it is hard-coded there and it would be nice if it got resolved one day. Otherwise the (k, v) in obj feature is completely useless.

You really cant do anything with that, tricking angular to think your result is an array is not useful for anything, because you would need numeric keys then...

If this is okay, you can define getter for length:

Object.defineProperty(yourResultObjectOrPrototype, 'length', {
  get: function(){
    return Object.keys(this).length;
  }
})

Otherwise you'll need some kind of filter which will iterate object using for(var k in obj) and store result in array.

like image 29
Kamil Tomšík Avatar answered Oct 17 '22 17:10

Kamil Tomšík


There is actually an answer: it's called orderBy and not sort:

https://docs.angularjs.org/api/ng/filter/orderBy

{{ orderBy_expression | orderBy : expression : reverse}}

<tr ng-repeat="friend in friends | orderBy:'-age'">
      <td>{{friend.name}}</td>
      <td>{{friend.phone}}</td>
      <td>{{friend.age}}</td>
    </tr>

Your list just needs to be a list, and you might need an index to know set order

$scope.Dates = [{index:1, "Today":"30"},
                  {index:2,"This Week":"42"},
                  {index:3,"This Month": "Oct"},
                  {index:4,"This Quarter" : "Bad"},
                  {index:5,"This Year" : 2013}]

and then

<tr ng-repeat="(key, value) in Dates | orderBy:'index'">
          <td>{{key}}</td>
          <td>{{value}}</td>
         </tr>
like image 7
MrE Avatar answered Oct 17 '22 17:10

MrE


This is fixed as of Angular 1.4.

See details here:

Previously, the order of items when using ngRepeat to iterate over object properties was guaranteed to be consistent by sorting the keys into alphabetic order.

Now, the order of the items is browser dependent based on the order returned from iterating over the object using the for key in obj syntax.

It seems that browsers generally follow the strategy of providing keys in the order in which they were defined...

like image 6
rasmi Avatar answered Oct 17 '22 16:10

rasmi