Función de clasificación personalizada en ng-repeat

113

Tengo un conjunto de mosaicos que muestran un cierto número dependiendo de la opción seleccionada por el usuario. Ahora me gustaría implementar una ordenación por cualquier número que se muestre.

El siguiente código muestra cómo lo he implementado (obteniendo / estableciendo un valor en el alcance de las tarjetas principales). Ahora, debido a que la función orderBy toma una cadena, traté de establecer una variable en el alcance de la tarjeta llamada curOptionValue y ordenar por eso, pero no parece funcionar.

Entonces la pregunta es, ¿cómo puedo crear una función de clasificación 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>

y 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];
    }
}]);
usuario1167650
fuente

Respuestas:

192

En realidad, el orderByfiltro puede tomar como parámetro no solo una cadena sino también una función. De la orderBydocumentación: https://docs.angularjs.org/api/ng/filter/orderBy ):

función: función Getter. El resultado de esta función se ordenará mediante el operador <, =,>.

Entonces, podrías escribir tu propia función. Por ejemplo, si desea comparar tarjetas en función de una suma de opt1 y opt2 (lo estoy inventando, el punto es que puede tener cualquier función arbitraria), escribiría en su controlador:

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

y luego, en tu plantilla:

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

Aquí está el jsFiddle de trabajo

La otra cosa que vale la pena señalar es que orderByes solo un ejemplo de filtros AngularJS, por lo que si necesita un comportamiento de ordenación muy específico, puede escribir su propio filtro (aunque orderBydebería ser suficiente para la mayoría de los casos de uso).

pkozlowski.opensource
fuente
Eso es bueno, pero ¿es posible crear un filtro para eso también?
hugo der hungrige
Sí, todavía está funcionando. Aquí hay una versión actualizada
jahller
¿Por qué no hay una descripción sobre varios parámetros en los documentos? BTW: Gracias, funciona. :)
mayankcpdixit
¿Sabes cómo puedo manejar múltiples criterios con este enfoque? ¿Cómo puedo devolver varios valores de myValueFunction?
Akcasoy
26

La solución aceptada solo funciona en matrices, pero no en objetos o matrices asociativas. Desafortunadamente, dado que Angular depende de la implementación de JavaScript de la enumeración de matrices, el orden de las propiedades del objeto no se puede controlar de manera consistente. Algunos navegadores pueden iterar a través de las propiedades del objeto de forma lexicográfica, pero esto no se puede garantizar.

por ejemplo, dada la siguiente asignación:

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

y la directiva <ul ng-repeat="(key, card) in cards | orderBy:myValueFunction">, ng-repeat puede iterar sobre "card1" antes de "card2", independientemente del orden de clasificación.

Para solucionar esto, podemos crear un filtro personalizado para convertir el objeto en una matriz y luego aplicar una función de clasificación personalizada antes de devolver la colección.

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

No podemos iterar sobre emparejamientos (clave, valor) junto con filtros personalizados (ya que las claves para las matrices son índices numéricos), por lo que la plantilla debe actualizarse para hacer referencia a los nombres de claves inyectados.

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

Aquí hay un violín que funciona utilizando un filtro personalizado en una matriz asociativa: http://jsfiddle.net/av1mLpqx/1/

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

David
fuente
1
Sé que no debería, pero tengo que ... Gracias.
Elia Weiss
7

El siguiente enlace explica los filtros en Angular extremadamente bien. Muestra cómo es posible definir una lógica de clasificación personalizada dentro de una repetición ng. http://toddmotto.com/everything-about-custom-filters-in-angular-js

Para clasificar objetos con propiedades, este es el código que he usado: (Tenga en cuenta que esta clasificación es el método de clasificación estándar de JavaScript y no es específico de angular) Nombre de columna es el nombre de la propiedad en la que se realizará la clasificación.

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

Para incluir la dirección junto con la función orderBy:

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

dónde

defaultSortDirection = 0; // 0 = Ascending, 1 = Descending
Ben
fuente
emmmmm, solo de pasada, observo que escribiste myOrderbyFunction()en su lugar, debes escribir myOrderbyFunctionsin (), se invoca para cada dos pares de elementos para que puedas proporcionar una clasificación personalizada.
Victor