Angularjs $ index incorrecto después de orderBy

92

Soy nuevo en Angular.js y tengo algunos problemas para ordenar mi matriz y trabajar en esos datos ordenados.

Tengo una lista con elementos y quiero ordenarla por "Store.storeName", que está funcionando hasta ahora. Pero después de ordenar los datos, mi función de eliminación ya no funciona. Creo que eso se debe a que el índice $ es incorrecto después de ordenar, por lo que se eliminan los datos incorrectos.

¿Cómo puedo solucionar eso? ¿Ordenar los datos en el alcance y no en la vista? ¿Como hacer eso?

Aquí hay un código relevante:

En la vista:

<tr ng-repeat="item in items | orderBy:'Store.storeName'">
                <td><input class="toggle" type="checkbox" ng-model="item.Completed"></td>
                <td>{{item.Name}}</td>
                <td>{{item.Quantity}} Stk.</td>
                <td>{{item.Price || 0 | number:2}} €</td>                
                <td>{{item.Quantity*item.Price|| 0 | number:2}} €</td>
                <td>{{item.Store.storeName}}</td> 
                <td><a><img src="img/delete.png" ng-click="removeItem($index)">{{$index}}</a></td>
            </tr>

Y en mi controlador tengo esta función de eliminación, que debería eliminar los datos específicos:

$scope.removeItem = function(index){
        $scope.items.splice(index,1);
    }

Esto funciona muy bien antes de realizar el pedido en la Vista. Si falta algo importante, déjeme ahora.

¡Gracias!

FuzzBuzz
fuente

Respuestas:

140

En su lugar, o basándose en el $index, que, como ha notado, apuntará al índice en una matriz ordenada / filtrada, puede pasar el elemento en sí a su removeItemfunción:

<a><img src="img/delete.png" ng-click="removeItem(item)">{{$index}}</a>

y modifique la removeItemfunción para encontrar un índice usando el indexOfmétodo de una matriz de la siguiente manera:

$scope.removeItem = function(item){
   $scope.items.splice($scope.items.indexOf(item),1);
}
pkozlowski.opensource
fuente
1
@ pkozlowski.opensource ¡Eres un genio! Puede pasar un elemento, no un índice ... ¡¡Guau !! Gracias hombre.
good_evening
Array indexOf no está disponible en Internet Explorer 8 y versiones anteriores.
Peter Hedberg
4
El título de la pregunta pregunta sobre $ index incorrecto después de orderBy, que esta respuesta no aborda. Hay casos en los que necesita el valor de $ index correcto (como la selección de turno en una lista). ¿Cómo obtenemos el valor de $ index correcto después de que se haya aplicado el filtro orderBy?
ClearCloud8
también puede crear una nueva matriz a partir de la lista ordenada dentro de la plantilla haciendo algo como esto: "ele en ordenado_array = (matriz | filtro: filtro | orderBy: order_by)"
Porlune
¡Ordenado! Gracias hermano.
CENT1PEDE
23

Comencé a aprender angular y enfrenté problemas similares, y según la respuesta de @ pkozlowski-opensource, lo resolví solo con algo como

<a>
  <img src="img/delete.png" ng-click="removeItem(items.indexOf(item))">
  {{items.indexOf(item)}}
</a> 
ad_nm
fuente
1
Esto es mejor y tan simple en lugar de crear un filtro personalizado, etc. +1
Rafique Mohammed
1
¿cómo no es esta la respuesta correcta? Esto resolvió mi problema
Cyrus Zei
19

Tuve el mismo problema y otras respuestas en este tema no son adecuadas para mi situación.

He resuelto mi problema con el filtro personalizado:

angular.module('utils', []).filter('index', function () {
    return function (array, index) {
        if (!index)
            index = 'index';
        for (var i = 0; i < array.length; ++i) {
            array[i][index] = i;
        }
        return array;
    };
});

que se puede utilizar de esta manera:

<tr ng-repeat="item in items | index | orderBy:'Store.storeName'">

y luego en HTML puede usar en item.indexlugar de $index.

Este método es adecuado para las colecciones de objetos.

Por favor, tenga en cuenta que este filtro personalizado debe ser el primero en la lista de todos los filtros aplicados (orderBy, etc.) y agregará la propiedad adicional index(el nombre es personalizable) en cada objeto de la colección.

milla
fuente
¿Podría explicar por qué las otras respuestas no son adecuadas para su situación?
pkozlowski.opensource
1
@ pkozlowski.opensource Esto es mucho más limpio. También dependiendo de los eventos que se puedan adjuntar y de la complejidad de los elementos en indexOf mucho más eficiente. Además $scope.items.splice($scope.items.indexOf(item),1);, no funcionará como se esperaba para elementos duplicados.
martin
1
@martin debe respaldar sus afirmaciones sobre el rendimiento con números reales. Un filtro tiene una gran desventaja de ejecutarse en todos y cada uno de los ciclos de $ digest, así que no creo que ayude con el rendimiento ...
pkozlowski.opensource
@ pkozlowski.opensource Eso es cierto, y se ejecuta dos veces por cada ciclo de $ digest. Lo importante es "dependiendo de los eventos que se puedan adjuntar", el rendimiento es importante cuando no se tiene control de la velocidad, por ejemplo, evento de desplazamiento sin estrangulamiento: caso extremo, lo sé.
martin
@mile Bueno, tengo duplicados y esto es lo que estaba buscando, solo un poco triste que Angular no realiza un seguimiento o el índice original en una variable $. Lo intenté (key, item) in itemsy tampoco funciona. (la clave no se mantiene en el original)
Rouche
4

Prueba esto:

$scope.remove = function(subtask) {

    var idx = $scope.subtasks.indexOf(subtask),
        st = $scope.currentTask.subtasks[idx];

    // remove from DB
    SubTask.remove({'subtaskId': subtask.id});

    // remove from local array
    $scope.subtasks.splice(idx,1);

}

Puede encontrar una explicación detallada en esta entrada de mi blog.

Dmitry Dushkin
fuente
2

En caso de que alguien necesite usar $index, puede dar un nombre a la matriz ordenada / filtrada:

<tr ng-repeat="item in sortedItems = (items | orderBy:'Store.storeName') track by $index">

Vea mi respuesta aquí .

hmk
fuente
Creo que esta respuesta está bien, si carece de detalles. Creo que lo que significa hmk es que una vez que la lista filtrada se ha dejado de lado, como antes, el índice se puede usar en su contra (es decir, "sortedItems [$ index]") para recuperar la entrada deseada.
Jeremythuff
1

Solo hubiera dejado un comentario, pero no tengo la "reputación".

La solución de mile es exactamente lo que necesitaba también. Para responder a la pregunta de pkozlowski.opensource: cuando tiene ngRepeats anidados , una lista dinámica (por ejemplo, donde permite eliminaciones), o ambos (que es mi caso), el uso $indexno funciona porque será el índice incorrecto para el back-end Los datos después de ordenarlos y usarlos ngInitpara almacenar en caché el valor tampoco funcionan porque no se vuelven a evaluar cuando cambia la lista.

Tenga en cuenta que la solución de mile permite personalizar el nombre de la propiedad del índice adjunto pasando un parámetro <tr ng-repeat="item in items | index:'originalPosition' | orderBy:'Store.storeName'">

Mi versión modificada:

.filter( 'repeatIndex', function repeatIndex()
{
// This filter must be called AFTER 'filter'ing 
//  and BEFORE 'orderBy' to be useful.
    return( function( array, index_name )
    {
        index_name = index_name || 'index';
        array.forEach( function( each, i )
        {each[ index_name ] = i;});
        return( array );
    });
})
MarkMYoung
fuente