Clicar em uma caixa de seleção com ng-click não atualiza o modelo


85

Clicar em uma caixa de seleção e chamar o ng-click: o modelo não é atualizado antes que o ng-click seja ativado, então o valor da caixa de seleção é apresentado incorretamente na IU:

Isso funciona no AngularJS 1.0.7 e parece estar quebrado no Angualar 1.2-RCx.

<div ng-app="myApp" ng-controller="Ctrl">
<li  ng-repeat="todo in todos">
  <input type='checkbox' ng-click='onCompleteTodo(todo)' ng-model="todo.done">
    {{todo.text}}
</li> 
<hr>
task: {{todoText}}
<hr><h2>Wrong value</h2>
     done: {{doneAfterClick}}

e controlador:

angular.module('myApp', [])
  .controller('Ctrl', ['$scope', function($scope) {
    $scope.todos=[
        {'text': "get milk",
         'done': true
         },
        {'text': "get milk2",
         'done': false
         }
        ];


   $scope.onCompleteTodo = function(todo) {
    console.log("onCompleteTodo -done: " + todo.done + " : " + todo.text);
    $scope.doneAfterClick=todo.done;
    $scope.todoText = todo.text;

   };
}]);

Broken Fiddle c / Angular 1.2 RCx - http://jsfiddle.net/supercobra/ekD3r/

Working fidddle w / Angular 1.0.0 - http://jsfiddle.net/supercobra/8FQNw/


3
Também quebrado para mim, agora que atualizei o Angular para 1.2+
ac360

Também quebrado na v1.2.24.
Vincent P

Respostas:


165

Que tal mudar

<input type='checkbox' ng-click='onCompleteTodo(todo)' ng-model="todo.done">

para

<input type='checkbox' ng-change='onCompleteTodo(todo)' ng-model="todo.done">

Dos documentos :

Avalie a expressão dada quando o usuário altera a entrada. A expressão não é avaliada quando a mudança de valor vem do modelo.

Observe que esta diretiva precisa ngModelestar presente.


3
isso também parece estar quebrado na versão 1.2.7
JvdBerg

Santa lâmpada, Batman! Achei que estava fazendo outra coisa completamente errada, mas acabou sendo tão simples quanto isso.
Adam Marshall

1
Resposta muito útil! +1 Angular doc -1
neurix de

e se você precisar dos dados do evento para evitar a falha?
user1943442


9

A ordem em que ng-clicke ng-modelserão executados é ambígua (uma vez que nenhum dos dois definiu explicitamente o seu priority). A solução mais estável para isso seria evitar usá-los no mesmo elemento.

Além disso, você provavelmente não deseja o comportamento que os exemplos mostram; você deseja que o checkboxresponda a cliques no texto completo do rótulo , não apenas na caixa de seleção. Portanto, a solução mais limpa seria envolver o input(com ng-model) dentro de um label(com ng-click):

<label ng-click="onCompleteTodo(todo)">
  <input type='checkbox' ng-model="todo.done">
  {{todo.text}}
</label>

Exemplo de trabalho: http://jsfiddle.net/b3NLH/1/


Muito obrigado! Esta é a única solução que funcionou para mim!
DaniCE

Esta solução ainda é a melhor!
Ellisan

8

Por que você não usa

$watch('todo',function(.....

Ou outra solução seria definir o todo.doneretorno de chamada do ng-click e usar apenas o ng-click

<div ng-app="myApp" ng-controller="Ctrl">
<li  ng-repeat="todo in todos">
<input type='checkbox' ng-click='onCompleteTodo(todo)'>
    {{todo.text}} {{todo.done}}

e

$scope.onCompleteTodo = function(todo) {
        todo.done = !todo.done; //toggle value
        console.log("onCompleteTodo -done: " + todo.done + " : " + todo.text);
        $scope.current = todo;
}

2
Veja a resposta de @kakoni, usei ng-change em vez de ng-click e o tempo funciona muito bem. Isso permite manter a ligação bidirecional e é uma abordagem muito mais limpa.
Michael Moser de

6

Substituir ng-model por ng-CHECK funciona para mim.


Exatamente o que eu queria. Obrigado!
Isaac

Apenas funcionou para mim a partir de todas as soluções disponíveis aqui.
thatzprem

2

É uma espécie de hack, mas encerrá-lo em um tempo limite parece realizar o que você está procurando:

angular.module('myApp', [])
    .controller('Ctrl', ['$scope', '$timeout', function ($scope, $timeout) {
    $scope.todos = [{
        'text': "get milk",
        'done': true
    }, {
        'text': "get milk2",
            'done': false
    }];

    $scope.onCompleteTodo = function (todo) {
        $timeout(function(){
            console.log("onCompleteTodo -done: " + todo.done + " : " + todo.text);
            $scope.doneAfterClick = todo.done;
            $scope.todoText = todo.text;
        });
    };
}]);

1

A ordem entre ng-modele ng-clickparece ser diferente e é algo em que você provavelmente não deve confiar. Em vez disso, você poderia fazer algo assim:

<div ng-app="myApp" ng-controller="Ctrl">
<li  ng-repeat="todo in todos">
<input type='checkbox' ng-model="todo.done" ng-click='onCompleteTodo(todo)'>
    {{todo.text}} {{todo.done}}
</li> 
    <hr>
        task: {{current.text}}
        <hr>
            <h2>Wrong value</h2>
         done: {{current.done}}
</div>

E seu roteiro:

angular.module('myApp', [])
    .controller('Ctrl', ['$scope', function($scope) {

        $scope.todos=[
            {'text': "get milk",
             'done': true
             },
            {'text': "get milk2",
             'done': false
             }
            ];

        $scope.current = $scope.todos[0];


       $scope.onCompleteTodo = function(todo) {
            console.log("onCompleteTodo -done: " + todo.done + " : " + todo.text);
    //$scope.doneAfterClick=todo.done;
    //$scope.todoText = todo.text;
       $scope.current = todo;

   };
}]);

A diferença aqui é que sempre que você clica em uma caixa, ela define essa caixa como "atual" e, em seguida, exibe esses valores na visualização. http://jsfiddle.net/QeR7y/


0

Normalmente, isso se deve a outra diretiva entre seu controlador ng e sua entrada que está criando um novo escopo. Quando o select grava seu valor, ele o grava no escopo mais recente, de forma que ele grava neste escopo ao invés do pai que está mais longe.

A melhor prática é nunca vincular diretamente a uma variável no escopo em um ng-model, isso também é conhecido como sempre incluindo um "ponto" em seu ngmodel. Para uma explicação melhor disso, confira este vídeo de John:

http://www.youtube.com/watch?v=DTx23w4z6Kc

Solução de: https://groups.google.com/forum/#!topic/angular/7Nd_me5YrHU


Seria ótimo se você fornecer um marcador de salto #t=5m08sem seu link do YouTube para que não seja necessário assistir o vídeo completo. Veja mattcutts.com/blog/link-to-youtube-minute-second
Volker E.

0

Acabei de substituir ng-modelpor ng-checkede funcionou para mim.

Esse problema foi quando eu atualizei minha versão angular de 1.2.28para1.4.9

Verifique também se isso ng-changeestá causando algum problema aqui. Tive que remover meu ng-changetambém para fazê-lo funcionar.


-1
.task{ng:{repeat:'task in model.tasks'}}
  %input{type:'checkbox',ng:{model:'$parent.model.tasks[$index].enabled'}}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.