Hacer clic en una casilla de verificación con ng-click no actualiza el modelo

85

Al hacer clic en una casilla de verificación y llamar a ng-click: el modelo no se actualiza antes de que ng-click se active, por lo que el valor de la casilla de verificación se presenta incorrectamente en la interfaz de usuario:

Esto funciona en AngularJS 1.0.7 y parece roto en 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}}

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

   };
}]);

Violín roto con Angular 1.2 RCx - http://jsfiddle.net/supercobra/ekD3r/

Violín de trabajo con Angular 1.0.0 - http://jsfiddle.net/supercobra/8FQNw/

supercobra
fuente
3
También está roto para mí ahora que he actualizado Angular a 1.2+
ac360
También roto en v1.2.24.
Vincent P

Respuestas:

165

Que tal cambiar

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

a

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

De los documentos :

Evaluar la expresión dada cuando el usuario cambia la entrada. La expresión no se evalúa cuando el cambio de valor proviene del modelo.

Tenga en cuenta que esta directiva requiere ngModelestar presente.

kakoni
fuente
3
esto también parece estar roto en la versión 1.2.7
JvdBerg
¡Santa bombilla, Batman! Pensé que estaba haciendo otra cosa completamente mal, pero resultó ser tan simple como esto.
Adam Marshall
1
¡Respuesta muy útil! +1 Angular doc -1
neurix
¿Qué sucede si necesita los datos del evento para evitar valores predeterminados?
user1943442
9

El orden en el que ng-clicky ng-modelse ejecutará es ambigua (ya que ni establecer explícitamente su priority). La solución más estable para esto sería evitar usarlos en el mismo elemento.

Además, probablemente no desee el comportamiento que muestran los ejemplos; desea checkboxque responda a los clics en el texto completo de la etiqueta , no solo a la casilla de verificación. Por lo tanto, la solución más limpia sería envolver el input(con ng-model) dentro de un label(con ng-click):

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

Ejemplo de trabajo: http://jsfiddle.net/b3NLH/1/

musicalmente_ut
fuente
¡Muchas gracias! ¡Esta es la única solución que me funcionó!
DaniCE
¡Esta solución sigue siendo la mejor!
Ellisan
8

¿Por qué no usas

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

O otra solución sería configurar el todo.doneinterior de la devolución de llamada ng-click y solo usar 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}}

y

$scope.onCompleteTodo = function(todo) {
        todo.done = !todo.done; //toggle value
        console.log("onCompleteTodo -done: " + todo.done + " : " + todo.text);
        $scope.current = todo;
}
GuillaumeA
fuente
2
Vea la respuesta de @kakoni, utilicé ng-change en lugar de ng-click y el tiempo funciona muy bien. Esto le permite mantener el enlace bidireccional y es un enfoque mucho más limpio.
Michael Moser
6

Reemplazar ng-model con ng-comprobado funciona para mí.

zzjove
fuente
Justo lo que quería. ¡Gracias!
Isaac
Me funcionó con todas las soluciones disponibles aquí.
thatzprem
2

Es una especie de truco, pero envolverlo en un tiempo de espera parece lograr lo que está buscando:

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;
        });
    };
}]);
Brian Lewis
fuente
1

El orden entre ng-modely ng-clickparece ser diferente y es algo en lo que probablemente no deberías confiar. En su lugar, podría hacer algo como esto:

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

Y tu guión:

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;

   };
}]);

Lo que es diferente aquí es que cada vez que hace clic en un cuadro, establece ese cuadro como lo que es "actual" y luego muestra esos valores en la vista. http://jsfiddle.net/QeR7y/

Manny D
fuente
0

Por lo general, esto se debe a otra directiva entre su ng-controller y su entrada que está creando un nuevo alcance. Cuando la selección escribe su valor, lo escribirá en el alcance más reciente, por lo que lo escribiría en este alcance en lugar del padre que está más lejos.

La mejor práctica es nunca vincular directamente a una variable en el alcance en un ng-model, esto también se conoce como incluir siempre un "punto" en su ngmodel. Para una mejor explicación de esto, mira este video de John:

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

Solución de: https://groups.google.com/forum/#!topic/angular/7Nd_me5YrHU

fergusrg
fuente
Sería genial si proporcionas un marcador de salto #t=5m08sen tu enlace de YouTube para que no sea necesario ver el video completo. Ver mattcutts.com/blog/link-to-youtube-minute-second
Volker E.
0

Simplemente reemplacé ng-modelcon ng-checkedy funcionó para mí.

Este problema fue cuando actualicé mi versión angular de 1.2.28a1.4.9

También verifique si ng-changeestá causando algún problema aquí. Tuve que quitarme mi ng-changetambién para que funcionara.

thatzprem
fuente
-1
.task{ng:{repeat:'task in model.tasks'}}
  %input{type:'checkbox',ng:{model:'$parent.model.tasks[$index].enabled'}}
Andrew WC Marrón
fuente