Benutzerdefinierte Sortierfunktion in ng-repeat


113

Ich habe eine Reihe von Kacheln, die eine bestimmte Anzahl anzeigen, je nachdem, welche Option vom Benutzer ausgewählt wird. Ich möchte jetzt eine Sortierung nach der angezeigten Nummer implementieren.

Der folgende Code zeigt, wie ich ihn implementiert habe (indem Sie einen Wert im Bereich der übergeordneten Karten abrufen / festlegen). Da die orderBy-Funktion eine Zeichenfolge verwendet, habe ich versucht, eine Variable im Kartenbereich namens curOptionValue festzulegen und danach zu sortieren, aber es scheint nicht zu funktionieren.

Es stellt sich also die Frage, wie ich eine benutzerdefinierte Sortierfunktion erstellen kann.

<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>

und 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];
    }
}]);

Antworten:


192

Tatsächlich kann der orderByFilter nicht nur eine Zeichenfolge, sondern auch eine Funktion als Parameter verwenden. Aus der orderByDokumentation: https://docs.angularjs.org/api/ng/filter/orderBy ):

Funktion: Getter-Funktion. Das Ergebnis dieser Funktion wird mit dem Operator <, =,> sortiert.

Sie können also Ihre eigene Funktion schreiben. Wenn Sie beispielsweise Karten vergleichen möchten, die auf einer Summe von opt1 und opt2 basieren (ich denke, der Punkt ist, dass Sie eine beliebige Funktion haben können), würden Sie in Ihren Controller schreiben:

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

und dann in Ihrer Vorlage:

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

Hier ist die funktionierende jsFiddle

Die andere erwähnenswerte Sache ist, dass dies orderBynur ein Beispiel für AngularJS-Filter ist. Wenn Sie also ein ganz bestimmtes Bestellverhalten benötigen, können Sie Ihren eigenen Filter schreiben (obwohl orderBydies für die meisten Anwendungsfälle ausreichen sollte).


Das ist schön, aber ist es auch möglich, einen Filter dafür zu erstellen?
Hugo der Hungrige

Ja, es funktioniert immer noch. Hier ist eine aktualisierte Version
Jahller

Warum gibt es keine Beschreibung für mehrere Parameter in Dokumenten? Übrigens: Danke, es funktioniert. :)
Mayankcpdixit

Wissen Sie, wie ich mit diesem Ansatz mit mehreren Kriterien umgehen kann? Wie kann ich mehrere Werte von der myValueFunction zurückgeben?
Akcasoy

26

Die akzeptierte Lösung funktioniert nur für Arrays, nicht jedoch für Objekte oder assoziative Arrays. Da Angular von der JavaScript-Implementierung der Array-Aufzählung abhängt, kann die Reihenfolge der Objekteigenschaften leider nicht konsistent gesteuert werden. Einige Browser durchlaufen möglicherweise die Objekteigenschaften lexikografisch, dies kann jedoch nicht garantiert werden.

zB Bei folgender Zuordnung:

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

und die Direktive <ul ng-repeat="(key, card) in cards | orderBy:myValueFunction">ng-repeat kann unabhängig von der Sortierreihenfolge über "card1" vor "card2" iterieren.

Um dies zu umgehen, können wir einen benutzerdefinierten Filter erstellen, um das Objekt in ein Array zu konvertieren, und dann eine benutzerdefinierte Sortierfunktion anwenden, bevor wir die Sammlung zurückgeben.

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;
  };
});

Wir können in Verbindung mit benutzerdefinierten Filtern nicht über (Schlüssel-, Wert-) Paarungen iterieren (da die Schlüssel für Arrays numerische Indizes sind), daher sollte die Vorlage aktualisiert werden, um auf die Namen der injizierten Schlüssel zu verweisen.

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

Hier ist eine funktionierende Geige, die einen benutzerdefinierten Filter für ein assoziatives Array verwendet: http://jsfiddle.net/av1mLpqx/1/

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


1
Ich weiß, ich sollte nicht, aber ich muss nur - Danke.
Elia Weiss

7

Der folgende Link erklärt Filter in Angular sehr gut. Es zeigt, wie es möglich ist, eine benutzerdefinierte Sortierlogik innerhalb einer ng-Wiederholung zu definieren. http://toddmotto.com/everything-about-custom-filters-in-angular-js

Für das Sortieren von Objekten mit Eigenschaften ist dies der Code, den ich verwendet habe: (Beachten Sie, dass diese Sortierung die Standard-JavaScript-Sortiermethode ist und nicht winkelspezifisch.) Spaltenname ist der Name der Eigenschaft, für die die Sortierung durchgeführt werden soll.

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

0

So fügen Sie die Richtung zusammen mit der orderBy-Funktion ein:

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

wo

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

emmmmm, nur vorübergehender Gedanke, ich beobachte, dass du geschrieben hast, myOrderbyFunction()stattdessen sollst du myOrderbyFunctionohne schreiben (), es wird für jeweils zwei Elementpaare aufgerufen, um eine benutzerdefinierte Sortierung bereitzustellen.
Victor
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.