0

I have the following JSON data and I'd like for Angular to do an ngRepeat for each key inside Specification and be displayed in the same order as they are in the object:

"Specification": {
    "Screen": "15.4\" Retina display",
    "Resolution": "2880 x 1800",
    "HDD": "512GB SSD",
    "RAM": "16GB RAM",
    "CPU": "Intel Core i7 2.30 Ghz Processor",
    "Software": "Mac OS X 10.9 Mavericks"
}

This is my HTML:

<ul>
    <li ng-repeat="(key, value) in selectedProduct.Specification">
        <span ng-class="{{key | lowercase}}">{{value}}</span>
    </li>
</ul>

The above code outputs the data in a random order, not the order of appearance in the array.

leaksterrr
  • 3,800
  • 7
  • 34
  • 65
  • 2
    Okkkkkkkk but what's the issue? Does that code not work? Unexpected output? – Sterling Archer Aug 18 '14 at 16:48
  • This will be relevant. http://stackoverflow.com/questions/19676694/ng-repeat-directive-sort-the-data-when-using-key-value – jessegavin Aug 18 '14 at 16:54
  • @SterlingArcher Pressed submit too early, the above code outputs the keys in a random order. – leaksterrr Aug 18 '14 at 16:59
  • Angular team has decided not to support orderBy when using an object in ng-repeat. See [the discussion](https://github.com/angular/angular.js/issues/1286), and there are some workarounds in comments. – runTarm Aug 18 '14 at 17:20

3 Answers3

3

Some browsers (e.g. Chrome) will re-arrange the keys alphabetically, so there is no reliable, cross-browser way to retain the key order for an object. You should either turn it into an array, or specify the key order separately (e.g. as a separate array).


UPDATE

As runTurm correctly pointed out, in ngRepeat specifically the keys are ordered alphabetically by Angular. So, the easiest way to retain a specific order would be to use a separate array for holding the keys in desired order:

/* CONTROLLER: */
$scope.keys = ["Screen", "Resolution", "HDD", "RAM", "CPU", "Software"];
$scope.selectedProduct = {
    Specification: {
        ...
    },
    ...
};

<!-- VIEW: -->
<ul>
    <li ng-repeat="key in keys">
        <span class="{{key | lowercase}}">{{selectedProduct.Specification[key]}}</span>
    </li>
</ul>

If you find it more readable or if you plan to use selectedProduct.Specification[key] in multiple places, you could use ngInit to alias it as value:

    <li ng-repeat="key in keys" ng-init="value=selectedProduct.Specification[key]">
        <span class="{{key | lowercase}}">{{value}}</span>
    </li>

See, also, this short demo.

gkalpak
  • 47,844
  • 8
  • 105
  • 118
  • Can you provide a reference? try using `for-in` in chrome, it doesn't re-arrange the keys alphabetically. – ryeballar Aug 18 '14 at 17:21
  • The keys are sorted by intention in the `ng-repeat` code, see [ngRepeat.js#L289](https://github.com/angular/angular.js/blob/v1.2.22/src/ng/directive/ngRepeat.js#L289). And there is a discussion [here](https://github.com/angular/angular.js/issues/6210). – runTarm Aug 18 '14 at 17:29
1

You can create a filter that will return an array object representation of your key-value object via angular.forEach().

DEMO

JAVASCRIPT

  .controller('Ctrl', function($scope, $filter) {
    var specs = {
      "Screen": "15.4\" Retina display",
      "Resolution": "2880 x 1800",
      "HDD": "512GB SSD",
      "RAM": "16GB RAM",
      "CPU": "Intel Core i7 2.30 Ghz Processor",
      "Software": "Mac OS X 10.9 Mavericks"
    };

    $scope.specs = $filter('keyValue')(specs);
  })

  .filter('keyValue', function() {
    return function(object) {
      var array = [];

      angular.forEach(object, function(value, index) {
        array.push({
          value: value,
          key: index
        });
      });

      return array;
    };
  });

HTML

<ul>
  <li ng-repeat="item in specs">
    <span ng-class="{{item.key | lowercase}}">{{item.value}}</span>
  </li>
</ul>
ryeballar
  • 29,658
  • 10
  • 65
  • 74
0

You cannot do this directly in ANgular but can either modify your object being bound to the view or can create a custom filter for same. I have used the Object.keys() method to return keys and then use the same for computed object.

angular.module('app', []).controller('controller', function($scope){
    var data = {"Specification": {
    "Screen": "15.4\" Retina display",
    "Resolution": "2880 x 1800",
    "HDD": "512GB SSD",
    "RAM": "16GB RAM",
    "CPU": "Intel Core i7 2.30 Ghz Processor",
    "Software": "Mac OS X 10.9 Mavericks"
    }};

    var filter = Object.keys(data.Specification), 
        b = [];
    angular.forEach(filter, function(key, value){
       b.push(data.Specification[key]);
    });

    $scope.selectedProducts = b;
});

Demo

Aditya Singh
  • 15,810
  • 15
  • 45
  • 67