Ordinamento AngularJS per proprietà


93

Quello che sto cercando di fare è ordinare alcuni dati per proprietà. Ecco un esempio che pensavo dovrebbe funzionare ma non funziona.

Parte HTML:

<div ng-app='myApp'>
    <div ng-controller="controller">
    <ul>
        <li ng-repeat="(key, value) in testData | orderBy:'value.order'">
            {{value.order}}. {{key}} -> {{value.name}}
        </li>
    </ul>
    </div>
</div>

Parte JS:

var myApp = angular.module('myApp', []);

myApp.controller('controller', ['$scope', function ($scope) {

    $scope.testData = {
        C: {name:"CData", order: 1},
        B: {name:"BData", order: 2},
        A: {name:"AData", order: 3},
    }

}]);

E il risultato:

  1. A -> AData
  2. B -> BData
  3. C -> CData

... che IMHO dovrebbe assomigliare a questo:

  1. C -> CData
  2. B -> BData
  3. A -> AData

Mi sono perso qualcosa (qui è pronto JSFiddle per sperimentare)?

Risposte:


148

Il filtro orderBy di AngularJS supporta solo gli array, nessun oggetto. Quindi devi scrivere un piccolo filtro, che fa l'ordinamento per te.

Oppure cambia il formato dei dati che gestisci (se hai influenza su questo). Un array contenente oggetti è ordinabile in base al filtro orderBy nativo.

Ecco il mio filtro orderObjectBy per AngularJS:

app.filter('orderObjectBy', function(){
 return function(input, attribute) {
    if (!angular.isObject(input)) return input;

    var array = [];
    for(var objectKey in input) {
        array.push(input[objectKey]);
    }

    array.sort(function(a, b){
        a = parseInt(a[attribute]);
        b = parseInt(b[attribute]);
        return a - b;
    });
    return array;
 }
});

Utilizzo a tuo avviso:

<div class="item" ng-repeat="item in items | orderObjectBy:'position'">
    //...
</div>

L'oggetto in questo esempio necessita di un attributo di posizione, ma hai la flessibilità di utilizzare qualsiasi attributo negli oggetti (contenente un numero intero), solo per definizione in vista.

JSON di esempio:

{
    "123": {"name": "Test B", "position": "2"},
    "456": {"name": "Test A", "position": "1"}
}

Ecco un violino che ti mostra l'utilizzo: http://jsfiddle.net/4tkj8/1/


1
Questo è fantastico. Ho aggiunto l'opzione per ordinare asc / desc: app.filter ('orderObjectBy', function () {return function (input, attribute, direction) {if (! Angular.isObject (input)) return input; var array = [] ; for (var objectKey in input) {array.push (input [objectKey]);} array.sort (function (a, b) {a = parseInt (a [attributo]); b = parseInt (b [attributo]) ; return direction == 'asc'? a - b: b - a;}); return array;}}); In HTML: <tr ng-repeat = "val in list | orderObjectBy: 'prop': 'asc'">
Jazzy

@Armin e se questo fosse un oggetto come un array? vale a dire{1:'Example 1', 2:'Example 2', 3:'Example 3', ...}
Eugene

8
Ottima risposta MA abbiamo perso la chiave dell'oggetto in questo modo. Per mantenerlo, aggiungilo semplicemente creando nuove righe di array nel filtro, ad es. for(var objectKey in input) { input[objectKey]['_key'] = objectKey; array.push(input[objectKey]); }Come quello che possiamo usare<div ng-repeat="value in object | orderObjectBy:'order'" ng-init="key = value['_key']">
Nicolas Janel

7
Vale la pena di notare che Angular.js fa supporto ordinamento per una struttura in un array di oggetti ora: ... | orderBy: 'name'.
Wildhoney

2
@Wildhoney Questa domanda riguarda l'ordinazione di oggetti con chiavi, non array contenenti oggetti.
Armin

31

È abbastanza facile, fallo così

$scope.props = [{order:"1"},{order:"5"},{order:"2"}]

ng-repeat="prop in props | orderBy:'order'"

33
Questo non funziona per gli array associativi, che è l'argomento della domanda.
MFB

7

Non dimenticare che parseInt () funziona solo per i valori Integer. Per ordinare i valori di stringa è necessario scambiare questo:

array.sort(function(a, b){
  a = parseInt(a[attribute]);
  b = parseInt(b[attribute]);
  return a - b;
});

con questo:

array.sort(function(a, b){
  var alc = a[attribute].toLowerCase(),
      blc = b[attribute].toLowerCase();
  return alc > blc ? 1 : alc < blc ? -1 : 0;
});

6

Come puoi vedere nel codice di angular-JS ( https://github.com/angular/angular.js/blob/master/src/ng/filter/orderBy.js ) ng-repeat non funziona con gli oggetti. Ecco un trucco con sortFunction.

http://jsfiddle.net/sunnycpp/qaK56/33/

<div ng-app='myApp'>
    <div ng-controller="controller">
    <ul>
        <li ng-repeat="test in testData | orderBy:sortMe()">
            Order = {{test.value.order}} -> Key={{test.key}} Name=:{{test.value.name}}
        </li>
    </ul>
    </div>
</div>

myApp.controller('controller', ['$scope', function ($scope) {

    var testData = {
        a:{name:"CData", order: 2},
        b:{name:"AData", order: 3},
        c:{name:"BData", order: 1}
    };
    $scope.testData = _.map(testData, function(vValue, vKey) {
        return { key:vKey, value:vValue };
    }) ;
    $scope.sortMe = function() {
        return function(object) {
            return object.value.order;
        }
    }
}]);

4

secondo http://docs.angularjs.org/api/ng.filter:orderBy , orderBy ordina un array. Nel tuo caso stai passando un oggetto, quindi dovrai implementare la tua funzione di ordinamento.

o passa un array -

$scope.testData = {
    C: {name:"CData", order: 1},
    B: {name:"BData", order: 2},
    A: {name:"AData", order: 3},
}

dai un'occhiata a http://jsfiddle.net/qaK56/


So che funziona con gli array .. Quindi la soluzione è scrivere la mia funzione di ordinamento?
PrimosK

Il tuo jsfiddle! = Il tuo codice che hai pubblicato. Questo è il jsfiddle giusto per il tuo esempio qui: jsfiddle.net/qaK56/92
mrzmyr

3

Dovresti davvero migliorare la tua struttura JSON per risolvere il tuo problema:

$scope.testData = [
   {name:"CData", order: 1},
   {name:"BData", order: 2},
   {name:"AData", order: 3},
]

Allora potresti farlo

<li ng-repeat="test in testData | orderBy:order">...</li>

Il problema, penso, è che le variabili (chiave, valore) non sono disponibili per il filtro orderBy e non dovresti comunque memorizzare i dati nelle tue chiavi


2
dillo a Firebase;)
MFB

per quanto ne so, crei i dati in Firebase
Joshua Wooward

Se archivi un array in Firebase, utilizza un UID come chiave per l'array. Ci sono molte situazioni in cui non si ottiene il controllo sulla struttura dei dati, quindi il tuo suggerimento potrebbe essere un po 'radicale.
MFB

memorizzare una matrice di cosa? Si ha sempre il controllo sulla struttura dei dati. Inoltre l'OP non menziona nulla su Firebase.
Joshua Wooward

2

Ecco cosa ho fatto e funziona.
Ho appena usato un oggetto a stringa.

$scope.thread = [ 
  {
    mostRecent:{text:'hello world',timeStamp:12345678 } 
    allMessages:[]
  }
  {MoreThreads...}
  {etc....}
]

<div ng-repeat="message in thread | orderBy : '-mostRecent.timeStamp'" >

se volessi ordinare per testo lo farei

orderBy : 'mostRecent.text'

2

Aggiungerò la mia versione aggiornata del filtro che è in grado di supportare la sintassi successiva:

ng-repeat="(id, item) in $ctrl.modelData | orderObjectBy:'itemProperty.someOrder':'asc'

app.filter('orderObjectBy', function(){

         function byString(o, s) {
            s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
            s = s.replace(/^\./, '');           // strip a leading dot
            var a = s.split('.');
            for (var i = 0, n = a.length; i < n; ++i) {
                var k = a[i];
                if (k in o) {
                    o = o[k];
                } else {
                    return;
                }
            }
            return o;
        }

        return function(input, attribute, direction) {
            if (!angular.isObject(input)) return input;

            var array = [];
            for(var objectKey in input) {
                if (input.hasOwnProperty(objectKey)) {
                    array.push(input[objectKey]);
                }
            }

            array.sort(function(a, b){
                a = parseInt(byString(a, attribute));
                b = parseInt(byString(b, attribute));
                return direction == 'asc' ? a - b : b - a;
            });
            return array;
        }
    })

Grazie ad Armin e Jason per le loro risposte in questo thread e ad Alnitak in questo thread .


1

La risposta di Armin + un controllo rigoroso per i tipi di oggetto e le chiavi non angolari come $resolve

app.filter('orderObjectBy', function(){
 return function(input, attribute) {
    if (!angular.isObject(input)) return input;

    var array = [];
    for(var objectKey in input) {
      if (typeof(input[objectKey])  === "object" && objectKey.charAt(0) !== "$")
        array.push(input[objectKey]);
    }

    array.sort(function(a, b){
        a = parseInt(a[attribute]);
        b = parseInt(b[attribute]);
        return a - b;
    });

    return array;
 }
})

1

Quanto segue consente l'ordinamento degli oggetti in base alla chiave O in base a una chiave all'interno di un oggetto .

Nel modello puoi fare qualcosa come:

    <li ng-repeat="(k,i) in objectList | orderObjectsBy: 'someKey'">

O anche:

    <li ng-repeat="(k,i) in objectList | orderObjectsBy: 'someObj.someKey'">

Il filtro:

app.filter('orderObjectsBy', function(){
 return function(input, attribute) {
    if (!angular.isObject(input)) return input;

    // Filter out angular objects.
    var array = [];
    for(var objectKey in input) {
      if (typeof(input[objectKey])  === "object" && objectKey.charAt(0) !== "$")
        array.push(input[objectKey]);
    }

    var attributeChain = attribute.split(".");

    array.sort(function(a, b){

      for (var i=0; i < attributeChain.length; i++) {
        a = (typeof(a) === "object") && a.hasOwnProperty( attributeChain[i]) ? a[attributeChain[i]] : 0;
        b = (typeof(b) === "object") && b.hasOwnProperty( attributeChain[i]) ? b[attributeChain[i]] : 0;
      }

      return parseInt(a) - parseInt(b);
    });

    return array;
 }
})

è normale che le chiavi di (k, i) in objectList siano trasformate in 0 e 1?
Ken Vernaillen
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.