Função de classificação personalizada em ng-repeat

113

Eu tenho um conjunto de blocos que exibem um certo número dependendo da opção selecionada pelo usuário. Agora, gostaria de implementar uma classificação por qualquer número mostrado.

O código abaixo mostra como eu o implementei (obtendo / definindo um valor no escopo dos cards pai). Agora, como a função orderBy usa uma string, tentei definir uma variável no escopo do cartão chamada curOptionValue e classificar por isso, mas não parece funcionar.

Portanto, a questão é: como criar uma função de classificação personalizada?

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

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

Respostas:

192

Na verdade, o orderByfiltro pode tomar como parâmetro não apenas uma string, mas também uma função. Da orderBydocumentação: https://docs.angularjs.org/api/ng/filter/orderBy ):

função: função getter. O resultado desta função será classificado usando o operador <, =,>.

Então, você pode escrever sua própria função. Por exemplo, se você gostaria de comparar os cartões com base na soma de opt1 e opt2 (estou inventando, o que quero dizer é que você pode ter qualquer função arbitrária), você escreveria em seu controlador:

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

e então, em seu modelo:

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

Aqui está o jsFiddle funcional

A outra coisa que vale a pena observar é que orderByé apenas um exemplo de filtros AngularJS, portanto, se você precisar de um comportamento de ordenação muito específico, poderá escrever seu próprio filtro (embora orderBydeva ser suficiente para a maioria dos casos de uso).

pkozlowski.opensource
fonte
Isso é bom, mas é possível criar um filtro para isso também?
hugo der hungrige
Sim, ainda está funcionando. Aqui está uma versão atualizada
jahller
Por que não há descrição sobre vários parâmetros nos documentos ?? BTW: Obrigado, funciona. :)
mayankcpdixit
Você sabe como posso lidar com vários critérios com essa abordagem? Como posso retornar vários valores de myValueFunction?
Akcasoy
26

A solução aceita funciona apenas em arrays, mas não em objetos ou arrays associativos. Infelizmente, como Angular depende da implementação JavaScript da enumeração de array, a ordem das propriedades do objeto não pode ser controlada de forma consistente. Alguns navegadores podem iterar por meio das propriedades do objeto lexicograficamente, mas isso não pode ser garantido.

por exemplo, dada a seguinte atribuição:

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

e a diretiva <ul ng-repeat="(key, card) in cards | orderBy:myValueFunction">, ng-repeat pode iterar sobre "card1" antes de "card2", independentemente da ordem de classificação.

Para contornar isso, podemos criar um filtro personalizado para converter o objeto em uma matriz e, em seguida, aplicar uma função de classificação personalizada antes de retornar a coleção.

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

Não podemos iterar em pares (chave, valor) em conjunto com filtros personalizados (uma vez que as chaves para matrizes são índices numéricos), portanto, o modelo deve ser atualizado para fazer referência aos nomes de chave injetados.

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

Aqui está um violino funcional utilizando um filtro personalizado em uma matriz associativa: http://jsfiddle.net/av1mLpqx/1/

Referência: https://github.com/angular/angular.js/issues/1286#issuecomment-22193332

David
fonte
1
Eu sei que não deveria, mas eu só tenho que - Obrigado.
Elia Weiss de
7

O link a seguir explica filtros em Angular extremamente bem. Mostra como é possível definir a lógica de classificação personalizada em uma repetição de ng. http://toddmotto.com/everything-about-custom-filters-in-angular-js

Para classificar objetos com propriedades, este é o código que usei: (observe que essa classificação é o método de classificação JavaScript padrão e não específico para angular) Nome da coluna é o nome da propriedade na qual a classificação deve ser realizada.

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

Para incluir a direção junto com a função orderBy:

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

Onde

defaultSortDirection = 0; // 0 = Ascending, 1 = Descending
Ben
fonte
emmmmm, apenas pensando, observei que você escreveu em myOrderbyFunction()vez disso, você escreverá myOrderbyFunctionsem (), ele é chamado para cada dois pares de elementos para que você forneça uma classificação personalizada.
Victor