Angularjs errados $ index após orderBy

92

Eu sou novo em Angular.js e tenho alguns problemas para classificar meu array e trabalhar com esses dados classificados.

Tenho uma lista com itens e desejo classificá-la por "Store.storeName", que está funcionando até agora. Mas depois de classificar os dados, minha função de exclusão não está mais funcionando. Acho que é porque o $ index está errado após a classificação e, portanto, os dados errados são excluídos.

Como posso resolver isso? Ordenando os dados no escopo e não na visualização? Como fazer isso?

Aqui está algum código relevante:

Na visualização:

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

E no meu controlador eu tenho esta função de exclusão, que deve excluir os dados específicos:

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

Isso funciona muito bem antes de fazer o pedido na Visualização. Se algo importante estiver faltando, por favor, deixe-me agora.

Obrigado!

FuzzBuzz
fonte

Respostas:

140

Em vez de retransmitir no $index- que - como você notou - apontará para o índice em uma matriz classificada / filtrada, você pode passar o próprio item para sua removeItemfunção:

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

e modifique a removeItemfunção para encontrar um índice usando o indexOfmétodo de uma matriz da seguinte maneira:

$scope.removeItem = function(item){
   $scope.items.splice($scope.items.indexOf(item),1);
}
pkozlowski.opensource
fonte
1
@ pkozlowski.opensource Você é um gênio! Você pode passar um item, não um índice .. Uau !! Obrigado cara.
good_evening
Array indexOf não está disponível no Internet Explorer 8 e inferior.
Peter Hedberg
4
O título da pergunta está perguntando sobre $ index errado após orderBy, que esta resposta não aborda. Há casos em que você precisa do valor de $ index correto (como seleção de deslocamento em uma lista). Como obtemos o valor correto de $ index depois que o filtro orderBy foi aplicado?
ClearCloud8
você também pode criar uma nova matriz a partir da lista ordenada dentro do modelo fazendo algo assim: "ele in order_array = (array | filter: filter | orderBy: order_by)"
Porlune
Arrumado! Obrigado mano.
CENT1PEDE
23

Comecei a aprender angular e enfrentei problemas semelhantes, e com base na resposta de @ pkozlowski-opensource, resolvi apenas com algo como

<a>
  <img src="img/delete.png" ng-click="removeItem(items.indexOf(item))">
  {{items.indexOf(item)}}
</a> 
ad_nm
fonte
1
Isso é melhor e tão simples ao invés de criar um filtro personalizado etc. +1
Rafique Mohammed
1
como esta não é a resposta correta? Isso resolveu meu problema
Cyrus Zei
19

Tive o mesmo problema e outras respostas neste tópico não são adequadas para a minha situação.

Resolvi meu problema com o 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 pode ser usado desta forma:

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

e em HTML você pode usar em item.indexvez de $index.

Este método é adequado para coleções de objetos.

Por favor, leve em consideração que este filtro personalizado deve ser o primeiro na lista de todos os filtros aplicados (orderBy etc.) e irá adicionar a propriedade adicional index(o nome é personalizável) em cada objeto da coleção.

milha
fonte
Você poderia explicar por que as outras respostas não são adequadas para sua situação?
pkozlowski.opensource
1
@ pkozlowski.opensource Isso é muito mais limpo. Também dependendo de quais eventos podem ser anexados e da complexidade dos itens em indexOf, muito mais eficiente. Também $scope.items.splice($scope.items.indexOf(item),1);não funcionará como esperado para itens duplicados.
Martin
1
@martin, você deve fazer backup de suas afirmações sobre desempenho com números reais. Um filtro tem uma grande desvantagem de ser executado em cada ciclo de $ digest, então não acho que isso ajude no desempenho ...
pkozlowski.opensource
@ pkozlowski.opensource Isso é verdade e é executado duas vezes para cada ciclo de $ digest. O importante é "dependendo de quais eventos podem ser anexados", o desempenho é importante quando você não tem controle da taxa, por exemplo, evento de rolagem não regulado - caso extremo eu sei.
Martin
@mile Bem, eu tenho duplicatas e isso é o que eu estava procurando, só um pouco triste que o Angular não mantém o controle ou o índice original em uma variável $. eu tentei (key, item) in itemse não funciona também. (a chave não é mantida no original)
Rouche
4

Experimente isto:

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

}

Você pode encontrar uma explicação detalhada nesta entrada em meu blog.

Dimitry
fonte
2

Caso alguém precise usar $index, você pode dar um nome à matriz classificada / filtrada:

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

Veja minha resposta aqui .

hmk
fonte
Acho que esta resposta está bem, embora falte em detalhes. Acho que hmk significa que, uma vez que a lista filtrada tenha sido separada, como acima, o índice pode ser usado contra ela (ou seja, "SortItems [$ index]") para recuperar a entrada desejada.
Jeremythuff
1

Eu teria deixado um comentário, mas não tenho a "reputação".

A solução de milha é exatamente o que eu precisava também. Para responder à pergunta de pkozlowski.opensource: quando você tem ngRepeats aninhados , uma lista dinâmica (por exemplo, onde você permite remoções) ou ambos (que é o meu caso), usar $indexnão funciona porque será o índice errado para o back-end dados depois de classificar e usarngInit para armazenar o valor em cache também não funcionam porque não são reavaliados quando a lista muda.

Observe que a solução da milha permite que o nome da propriedade do índice anexado seja personalizado passando um parâmetro <tr ng-repeat="item in items | index:'originalPosition' | orderBy:'Store.storeName'">

Minha versão ajustada:

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