Como responder a cliques em uma checkbox de seleção em uma diretiva AngularJS?

Eu tenho uma diretiva AngularJS que processa uma coleção de entidades no seguinte modelo:

Title
{{e.title}}

Como você pode ver, é uma

onde cada linha pode ser selecionada individualmente com sua própria checkbox de seleção, ou todas as linhas podem ser selecionadas de uma só vez com uma checkbox de seleção principal localizada no

. UI muito clássico.

Qual é a melhor maneira de:

  • Selecione uma única linha (ou seja, quando a checkbox de seleção estiver marcada, adicione o id da entidade selecionada a um array interno e adicione uma class CSS ao
contendo a entidade para refletir seu estado selecionado)?
  • Selecione todas as linhas de uma vez? (isto é, as ações descritas anteriormente para todas as linhas na
  • )

    Minha implementação atual é adicionar um controlador personalizado à minha diretiva:

     controller: function($scope) { // Array of currently selected IDs. var selected = $scope.selected = []; // Update the selection when a checkbox is clicked. $scope.updateSelection = function($event, id) { var checkbox = $event.target; var action = (checkbox.checked ? 'add' : 'remove'); if (action == 'add' & selected.indexOf(id) == -1) selected.push(id); if (action == 'remove' && selected.indexOf(id) != -1) selected.splice(selected.indexOf(id), 1); // Highlight selected row. HOW?? // $(checkbox).parents('tr').addClass('selected_row', checkbox.checked); }; // Check (or uncheck) all checkboxes. $scope.selectAll = function() { // Iterate on all checkboxes and call updateSelection() on them?? }; } 

    Mais especificamente, eu me pergunto:

    • O código acima pertence a um controlador ou deve entrar em uma function de link ?
    • Como o jQuery não está necessariamente presente (o AngularJS não o requer), qual é a melhor maneira de fazer a travessia de DOM? Sem jQuery, estou tendo dificuldade em selecionar o pai
    de uma determinada checkbox de seleção ou selecionar todas as checkboxs de seleção no modelo.
  • Passar $event para updateSelection() não parece muito elegante. Não há uma maneira melhor de recuperar o estado (marcado / desmarcado) de um elemento que acabou de ser clicado?
  • Obrigado.

    É assim que venho fazendo esse tipo de coisa. Angular tende a favorecer a manipulação declarativa do dom em vez de um imperativo (pelo menos é assim que venho brincando com ele).

    A marcação

     
    Title
    {{e.title}}

    E no controlador

     var updateSelected = function(action, id) { if (action === 'add' && $scope.selected.indexOf(id) === -1) { $scope.selected.push(id); } if (action === 'remove' && $scope.selected.indexOf(id) !== -1) { $scope.selected.splice($scope.selected.indexOf(id), 1); } }; $scope.updateSelection = function($event, id) { var checkbox = $event.target; var action = (checkbox.checked ? 'add' : 'remove'); updateSelected(action, id); }; $scope.selectAll = function($event) { var checkbox = $event.target; var action = (checkbox.checked ? 'add' : 'remove'); for ( var i = 0; i < $scope.entities.length; i++) { var entity = $scope.entities[i]; updateSelected(action, entity.id); } }; $scope.getSelectedClass = function(entity) { return $scope.isSelected(entity.id) ? 'selected' : ''; }; $scope.isSelected = function(id) { return $scope.selected.indexOf(id) >= 0; }; //something extra I couldn't resist adding :) $scope.isSelectedAll = function() { return $scope.selected.length === $scope.entities.length; }; 

    EDIT : getSelectedClass() espera a entidade inteira, mas ela estava sendo chamada apenas com o ID da entidade, que agora está corrigida

    Eu prefiro usar as diretivas ngModel e ngChange ao lidar com checkboxs de seleção . ngModel permite vincular o estado marcado / desmarcado da checkbox de seleção a uma propriedade na entidade:

      

    Sempre que o usuário entity.isChecked ou desmarcar a checkbox de seleção, o valor entity.isChecked também será alterado.

    Se isso é tudo que você precisa, você nem precisa das diretivas ngClick ou ngChange. Como você tem a checkbox de seleção “Marcar tudo”, obviamente precisa fazer mais do que apenas definir o valor da propriedade quando alguém marcar uma checkbox de seleção.

    Ao usar ngModel com uma checkbox de seleção, é melhor usar ngChange em vez de ngClick para manipular events marcados e desmarcados. ngChange é feito apenas para esse tipo de cenário. Ele faz uso do ngModelController para binding de dados (ele adiciona um ouvinte à matriz $viewChangeListeners do ngModelController. Os ouvintes nessa matriz são chamados após o valor do modelo ter sido configurado, evitando esse problema ).

      

    … e no controlador …

     var model = {}; $scope.model = model; // This property is bound to the checkbox in the table header model.allItemsSelected = false; // Fired when an entity in the table is checked $scope.selectEntity = function () { // If any entity is not checked, then uncheck the "allItemsSelected" checkbox for (var i = 0; i < model.entities.length; i++) { if (!model.entities[i].isChecked) { model.allItemsSelected = false; return; } } // ... otherwise ensure that the "allItemsSelected" checkbox is checked model.allItemsSelected = true; }; 

    Da mesma forma, a checkbox de seleção "Marcar tudo" no header:

        

    ... e ...

     // Fired when the checkbox in the table header is checked $scope.selectAll = function () { // Loop through all the entities and set their isChecked property for (var i = 0; i < model.entities.length; i++) { model.entities[i].isChecked = model.allItemsSelected; } }; 

    CSS

    Qual é a melhor maneira de ... adicionar uma class CSS ao

    contendo a entidade para refletir seu estado selecionado?

    Se você usar a abordagem ngModel para a vinculação de dados, tudo o que você precisa fazer é adicionar a diretiva ngClass ao elemento

    para adicionar ou remover dinamicamente a class sempre que a propriedade da entidade for alterada:

      

    Veja o Plunker completo aqui .

    A resposta de Liviu foi extremamente útil para mim. Espero que isso não seja uma má forma, mas eu fiz um violino que pode ajudar alguém no futuro.

    Duas peças importantes que são necessárias são:

      $scope.entities = [{ "title": "foo", "id": 1 }, { "title": "bar", "id": 2 }, { "title": "baz", "id": 3 }]; $scope.selected = [];