Cliquer sur une case à cocher avec ng-click ne met pas à jour le modèle

85

En cliquant sur une case à cocher et en appelant ng-click: le modèle n'est pas mis à jour avant que ng-click ne démarre, donc la valeur de la case à cocher est mal présentée dans l'interface utilisateur:

Cela fonctionne dans AngularJS 1.0.7 et semble cassé dans 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}}

et contrôleur:

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;

   };
}]);

Violon cassé avec Angular 1.2 RCx - http://jsfiddle.net/supercobra/ekD3r/

Fidddle de travail avec Angular 1.0.0 - http://jsfiddle.net/supercobra/8FQNw/

supercobra
la source
3
Également cassé pour moi maintenant que j'ai mis à jour Angular vers 1.2+
ac360
Également cassé dans la v1.2.24.
Vincent P

Réponses:

165

Que diriez-vous de changer

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

à

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

À partir de la documentation :

Évaluez l'expression donnée lorsque l'utilisateur modifie l'entrée. L'expression n'est pas évaluée lorsque le changement de valeur provient du modèle.

Notez que cette directive doit ngModelêtre présente.

kakoni
la source
3
cela semble également être cassé dans la version 1.2.7
JvdBerg
Ampoule sacrée, Batman! Je pensais que je faisais quelque chose de complètement faux, mais cela s'est avéré aussi simple que cela.
Adam Marshall
1
Réponse très utile! +1 Angular doc -1
neurix
Que faire si vous avez besoin des données d'événement pour empêcher Par défaut?
user1943442
11

Comme indiqué dans https://github.com/angular/angular.js/issues/4765 , le passage de ng-click à ng-change semble résoudre ce problème (j'utilise Angular 1.2.14)

Peter Hollingsworth
la source
3
Solution la plus simple et la plus simple. +1 à Griffindor :)
Somaiah Kumbera
9

L'ordre dans lequel ng-clicket ng-modelsera exécuté est ambigu (puisque ni l'un ni l'autre ne les définissent explicitement priority). La solution la plus stable à cela serait d'éviter de les utiliser sur le même élément.

En outre, vous ne voulez probablement pas du comportement que les exemples montrent; vous voulez que le checkboxréponde aux clics sur le texte complet de l' étiquette , pas seulement sur la case à cocher. Par conséquent, la solution la plus propre serait d'envelopper le input(avec ng-model) dans un label(avec ng-click):

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

Exemple de travail: http://jsfiddle.net/b3NLH/1/

musicalement_ut
la source
Merci beaucoup! C'est la seule solution qui a fonctionné pour moi!
DaniCE
Cette solution est toujours la meilleure!
Ellisan
8

Pourquoi n'utilisez-vous pas

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

Ou une autre solution serait de définir l' todo.doneintérieur du rappel ng-click et d'utiliser uniquement 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}}

et

$scope.onCompleteTodo = function(todo) {
        todo.done = !todo.done; //toggle value
        console.log("onCompleteTodo -done: " + todo.done + " : " + todo.text);
        $scope.current = todo;
}
GuillaumeA
la source
2
Voir la réponse @kakoni, j'ai utilisé ng-change au lieu de ng-click et le timing fonctionne très bien. Cela vous permet de conserver la liaison bidirectionnelle et constitue une approche beaucoup plus propre.
Michael Moser
6

Remplacer ng-model par ng-checked fonctionne pour moi.

zzjove
la source
Juste ce que je voulais. Merci!
Isaac le
Je viens de travailler pour moi à partir de toutes les solutions disponibles ici.
thatzprem
2

C'est une sorte de hack mais l'envelopper dans un délai d'attente semble accomplir ce que vous recherchez:

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

L'ordre entre ng-modelet ng-clicksemble être différent et c'est quelque chose sur lequel vous ne devriez probablement pas vous fier. Au lieu de cela, vous pouvez faire quelque chose comme ceci:

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

Et votre script:

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;

   };
}]);

Ce qui est différent ici, c'est que chaque fois que vous cliquez sur une case, cela définit cette case comme ce qui est "actuel" et affiche ensuite ces valeurs dans la vue. http://jsfiddle.net/QeR7y/

Manny D
la source
0

Cela est généralement dû à une autre directive entre votre ng-controller et votre entrée qui crée une nouvelle portée. Lorsque le select écrit sa valeur, il l'écrira dans la portée la plus récente, de sorte qu'il l'écrira dans cette portée plutôt que dans le parent qui est plus éloigné.

La meilleure pratique est de ne jamais se lier directement à une variable sur la portée dans un ng-model, ceci est également connu comme toujours inclure un "point" dans votre ngmodel. Pour une meilleure explication, regardez cette vidéo de John:

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

Solution de: https://groups.google.com/forum/#!topic/angular/7Nd_me5YrHU

fergusrg
la source
Ce serait génial si vous fournissez un marqueur de saut #t=5m08sdans votre lien YouTube afin qu'il ne soit pas nécessaire de regarder la vidéo complète. Voir mattcutts.com/blog/link-to-youtube-minute-second
Volker E.
0

Je viens de remplacer ng-modelavec ng-checkedet cela a fonctionné pour moi.

Ce problème s'est produit lorsque j'ai mis à jour ma version angulaire de 1.2.28à1.4.9

Vérifiez également si votre ng-changeproblème est ici. Je devais aussi supprimer mon ng-changepour le faire fonctionner.

thatzprem
la source
-1
.task{ng:{repeat:'task in model.tasks'}}
  %input{type:'checkbox',ng:{model:'$parent.model.tasks[$index].enabled'}}
Andrew WC Brown
la source