Funzione di ordinamento personalizzata in ng-repeat


113

Ho una serie di tessere che visualizzano un certo numero a seconda dell'opzione selezionata dall'utente. Vorrei ora implementare un ordinamento in base al numero visualizzato.

Il codice seguente mostra come l'ho implementato (ottenendo / impostando un valore nell'ambito della scheda madre). Ora, poiché la funzione orderBy accetta una stringa, ho provato a impostare una variabile nell'ambito della carta chiamata curOptionValue e ordinare in base a quella, ma non sembra funzionare.

Quindi la domanda diventa: come creare una funzione di ordinamento personalizzata?

<div ng-controller="aggViewport" >
<div class="btn-group" >
    <button ng-click="setOption(opt.name)" ng-repeat="opt in optList" class="btn active">{{opt.name}}</button>
</div>
<div id="container" iso-grid width="500px" height="500px">
    <div ng-repeat="card in cards" class="item {{card.class}}" ng-controller="aggCardController">
        <table width="100%">
            <tr>
                <td align="center">
                    <h4>{{card.name}}</h4>
                </td>
            </tr>
            <tr>
                <td align="center"><h2>{{getOption()}}</h2></td>
            </tr>
        </table>        
    </div>
</div>

e controller:

module.controller('aggViewport',['$scope','$location',function($scope,$location) {
    $scope.cards = [
        {name: card1, values: {opt1: 9, opt2: 10}},
        {name: card1, values: {opt1: 9, opt2: 10}}
    ];

    $scope.option = "opt1";

    $scope.setOption = function(val){
        $scope.option = val;
    }

}]);

module.controller('aggCardController',['$scope',function($scope){
    $scope.getOption = function(){
        return $scope.card.values[$scope.option];
    }
}]);

Risposte:


192

In realtà il orderByfiltro può prendere come parametro non solo una stringa ma anche una funzione. Dalla orderBydocumentazione: https://docs.angularjs.org/api/ng/filter/orderBy ):

funzione: funzione Getter. Il risultato di questa funzione verrà ordinato utilizzando l'operatore <, =,>.

Quindi, potresti scrivere la tua funzione. Ad esempio, se desideri confrontare le carte in base alla somma di opt1 e opt2 (lo sto inventando, il punto è che puoi avere qualsiasi funzione arbitraria) dovresti scrivere nel tuo controller:

$scope.myValueFunction = function(card) {
   return card.values.opt1 + card.values.opt2;
};

e poi, nel tuo modello:

ng-repeat="card in cards | orderBy:myValueFunction"

Ecco il jsFiddle funzionante

L'altra cosa degna di nota è che orderByè solo un esempio di filtri AngularJS, quindi se hai bisogno di un comportamento di ordinamento molto specifico potresti scrivere il tuo filtro (anche se orderBydovrebbe essere sufficiente per la maggior parte dei casi d'uso).


È carino, ma è possibile creare un filtro anche per quello?
hugo der hungrige

Sì, funziona ancora. Ecco una versione aggiornata
jahller

Perché non esiste una descrizione di più parametri nei documenti? BTW: Grazie, funziona. :)
mayankcpdixit

Sai come posso gestire più criteri con questo approccio? Come posso restituire più valori da myValueFunction?
akcasoy

26

La soluzione accettata funziona solo sugli array, ma non sugli oggetti o sugli array associativi. Sfortunatamente, poiché Angular dipende dall'implementazione JavaScript dell'enumerazione dell'array, l'ordine delle proprietà degli oggetti non può essere controllato in modo coerente. Alcuni browser possono scorrere le proprietà degli oggetti in modo lessicografico, ma ciò non può essere garantito.

ad esempio, dato il seguente incarico:

$scope.cards = {
  "card2": {
    values: {
      opt1: 9,
      opt2: 12
    }
  },
  "card1": {
    values: {
      opt1: 9,
      opt2: 11
    }
  }
};

e la direttiva <ul ng-repeat="(key, card) in cards | orderBy:myValueFunction">, ng-repeat può iterare su "card1" prima di "card2", indipendentemente dall'ordinamento.

Per ovviare a ciò, possiamo creare un filtro personalizzato per convertire l'oggetto in un array e quindi applicare una funzione di ordinamento personalizzata prima di restituire la raccolta.

myApp.filter('orderByValue', function () {
  // custom value function for sorting
  function myValueFunction(card) {
    return card.values.opt1 + card.values.opt2;
  }

  return function (obj) {
    var array = [];
    Object.keys(obj).forEach(function (key) {
      // inject key into each object so we can refer to it from the template
      obj[key].name = key;
      array.push(obj[key]);
    });
    // apply a custom sorting function
    array.sort(function (a, b) {
      return myValueFunction(b) - myValueFunction(a);
    });
    return array;
  };
});

Non è possibile iterare gli accoppiamenti (chiave, valore) insieme a filtri personalizzati (poiché le chiavi per gli array sono indici numerici), quindi il modello dovrebbe essere aggiornato per fare riferimento ai nomi delle chiavi inseriti.

<ul ng-repeat="card in cards | orderByValue">
    <li>{{card.name}} {{value(card)}}</li>
</ul>

Ecco un violino funzionante che utilizza un filtro personalizzato su un array associativo: http://jsfiddle.net/av1mLpqx/1/

Riferimento: https://github.com/angular/angular.js/issues/1286#issuecomment-22193332


1
So che non dovrei ma devo solo - Grazie.
Elia Weiss

7

Il seguente collegamento spiega estremamente bene i filtri in Angular. Mostra come sia possibile definire una logica di ordinamento personalizzata all'interno di un ng-repeat. http://toddmotto.com/everything-about-custom-filters-in-angular-js

Per l'ordinamento di oggetti con proprietà, questo è il codice che ho usato: (Notare che questo ordinamento è il metodo di ordinamento JavaScript standard e non specifico per angolare) Nome colonna è il nome della proprietà su cui deve essere eseguito l'ordinamento.

self.myArray.sort(function(itemA, itemB) {
    if (self.sortOrder === "ASC") {
        return itemA[columnName] > itemB[columnName];
    } else {
        return itemA[columnName] < itemB[columnName];
    }
});

0

Per includere la direzione insieme alla funzione orderBy:

ng-repeat="card in cards | orderBy:myOrderbyFunction():defaultSortDirection"

dove

defaultSortDirection = 0; // 0 = Ascending, 1 = Descending

emmmmm, solo passando il pensiero ho notato che hai scritto myOrderbyFunction()invece devi scrivere myOrderbyFunctionsenza (), viene invocato per ogni due coppie di elementi in modo da fornire un ordinamento personalizzato.
Victor
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.