Angularjs faux $ index après orderBy

92

Je suis nouveau sur Angular.js et j'ai des problèmes pour trier mon tableau et travailler sur ces données triées.

J'ai une liste d'articles et je veux donc la trier par "Store.storeName", qui fonctionne jusqu'à présent. Mais après avoir trié les données, ma fonction de suppression ne fonctionne plus. Je pense que c'est parce que l'index $ est erroné après le tri, et donc les mauvaises données sont supprimées.

Comment puis-je résoudre ça? Commander les données dans la portée et non dans la vue? Comment faire ça?

Voici un code pertinent:

Dans la vue:

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

Et dans mon contrôleur, j'ai cette fonction de suppression, qui devrait supprimer les données spécifiques:

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

Cela fonctionne bien avant de commander dans la vue. S'il manque quelque chose d'important, laissez-moi maintenant.

Merci!

FuzzBuzz
la source

Réponses:

140

Au lieu de cela ou en reliant le $index- qui - comme vous l'avez remarqué - pointera vers l'index dans un tableau trié / filtré, vous pouvez passer l'élément lui-même à votre removeItemfonction:

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

et modifiez la removeItemfonction pour trouver un index en utilisant la indexOfméthode d'un tableau comme suit:

$scope.removeItem = function(item){
   $scope.items.splice($scope.items.indexOf(item),1);
}
pkozlowski.opensource
la source
1
@ pkozlowski.opensource Vous êtes un génie! Vous pouvez passer un élément, pas un index .. Wow !! Merci mec.
good_evening
Array indexOf n'est pas disponible dans Internet Explorer 8 et versions antérieures.
Peter Hedberg
4
Le titre de la question pose des questions sur le mauvais $ index après orderBy, auquel cette réponse ne répond pas. Dans certains cas, vous avez besoin de la valeur $ index correcte (comme la sélection par décalage dans une liste). Comment obtenir la valeur correcte de $ index après l'application du filtre orderBy?
ClearCloud8
vous pouvez également créer un nouveau tableau à partir de la liste ordonnée dans le modèle en faisant quelque chose comme ceci: "ele in orders_array = (array | filter: filter | orderBy: order_by)"
Porlune
Soigné! Merci mec.
CENT1PEDE
23

J'ai commencé à apprendre angulaire et j'ai rencontré des problèmes similaires, et sur la base de la réponse de @ pkozlowski-opensource, je l'ai résolu avec quelque chose comme

<a>
  <img src="img/delete.png" ng-click="removeItem(items.indexOf(item))">
  {{items.indexOf(item)}}
</a> 
ad_nm
la source
1
C'est mieux et si simple que de créer un filtre personnalisé, etc. +1
Rafique Mohammed
1
comment n'est-ce pas la bonne réponse? Cela a résolu mon problème
Cyrus Zei
19

J'ai eu le même problème et les autres réponses dans ce sujet ne sont pas adaptées à ma situation.

J'ai résolu mon problème avec le filtre personnalisé:

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

qui peut être utilisé de cette façon:

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

puis en HTML, vous pouvez utiliser à la item.indexplace de $index.

Cette méthode convient aux collections d'objets.

Veuillez prendre en compte que ce filtre personnalisé doit être le premier dans la liste de tous les filtres appliqués (orderBy, etc.) et qu'il ajoutera la propriété supplémentaire index(le nom est personnalisable) dans chaque objet de la collection.

mile
la source
Pourriez-vous expliquer pourquoi les autres réponses ne conviennent pas à votre situation?
pkozlowski.opensource
1
@ pkozlowski.opensource C'est beaucoup plus propre. En fonction également des événements qui pourraient être attachés et de la complexité des éléments dans indexOf beaucoup plus efficaces. Aussi $scope.items.splice($scope.items.indexOf(item),1);ne fonctionnera pas comme prévu pour les éléments en double.
martin
1
@martin, vous devez sauvegarder vos affirmations concernant les performances avec des nombres réels. Un filtre a un énorme inconvénient d'être exécuté sur chaque cycle de $ digest, donc je ne pense pas que cela aide à la performance ...
pkozlowski.opensource
@ pkozlowski.opensource C'est vrai, et il s'exécute deux fois pour chaque cycle $ digest. L'important est "en fonction des événements qui pourraient être attachés", les performances sont importantes lorsque vous n'avez aucun contrôle sur le taux, par exemple, un événement de défilement sans limitation - cas extrême que je connais.
martin
@mile Eh bien, j'ai des doublons et c'est ce que je cherchais, juste un peu triste qu'Angular ne garde pas la trace ou l'index d'origine dans une variable $. j'ai essayé (key, item) in itemset ça ne marche pas non plus. (la clé n'est pas conservée à l'original)
Rouche
4

Essaye ça:

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

}

Vous pouvez trouver des explications détaillées dans cette entrée de mon blog.

Dimitry
la source
2

Au cas où quelqu'un aurait besoin d'utiliser $index, vous pouvez donner un nom au tableau trié / filtré:

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

Voir ma réponse ici .

hmk
la source
Je pense que cette réponse est bonne, si elle manque de détails. Je pense que ce que signifie hmk, c'est qu'une fois que la liste filtrée a été mise de côté, comme ci-dessus, l'index peut être utilisé contre elle (c'est-à-dire "sortedItems [$ index]") pour récupérer l'entrée désirée.
Jeremythuff
1

J'aurais juste laissé un commentaire, mais je n'ai pas la "réputation".

La solution de mile est exactement ce dont j'avais besoin. Pour répondre à la question de pkozlowski.opensource: lorsque vous avez imbriqué des ngRepeats, une liste dynamique (par exemple où vous autorisez les suppressions), ou les deux (ce qui est mon cas), utiliser $indexne fonctionne pas car ce sera le mauvais index pour le back-end les données après le tri et l'utilisation ngInitpour mettre en cache la valeur ne fonctionnent pas non plus car elles ne sont pas réévaluées lorsque la liste change.

Notez que la solution de mile permet de personnaliser le nom de la propriété d'index attaché en passant un paramètre <tr ng-repeat="item in items | index:'originalPosition' | orderBy:'Store.storeName'">

Ma version modifiée:

.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
la source