AngularJS: la liaison ng-model ne se met pas à jour lorsqu'elle est modifiée avec jQuery


98

Ceci est mon HTML:

<input id="selectedDueDate" type="text" ng-model="selectedDate" />

Lorsque je tape dans la boîte, le modèle est mis à jour via le mécanisme de liaison bidirectionnelle. Doux.

Cependant, quand je fais cela via JQuery ...

$('#selectedDueDate').val(dateText);

Il ne met pas à jour le modèle. Pourquoi?


1
Pourquoi feriez-vous le deuxième pour commencer? Vous utilisez un framework, puis décidez de le contourner et de définir la valeur via la manipulation DOM. Meilleur conseil que l'on puisse donner avec angular aux débutants: oubliez que jquery existe. dans 90% des cas, angular sera suffisant et les 10% restants peuvent être atteints via jqlite à l'intérieur du lien d'une directive (l'élément est en fait un élément enveloppé de jqlite prêt à être manipulé).
Annulé le

1
question très importante
Syed Md. Kamruzzaman

il y a de très bonnes raisons pour lesquelles vous voudrez peut-être changer les modèles angulaires avec la manipulation dom, peut-être que vous êtes un développeur travaillant avec un outil de test A / B et que vous voulez remplir des formulaires dans les coulisses, ou vous travaillez sur un script greasemonkey pour remplir automatiquement les formulaires.
Shadaez

Réponses:


134

Angular n'est pas au courant de ce changement. Pour cela, vous devez appeler $scope.$digest()ou effectuer le changement à l'intérieur de $scope.$apply():

$scope.$apply(function() { 
   // every changes goes here
   $('#selectedDueDate').val(dateText); 
});

Voir ceci pour mieux comprendre la vérification sale

MISE À JOUR : Voici un exemple


J'ai fait ceci comme vous dites: fiddle.jshell.net/AladdinMhaimeed/agvTz/8 mais cela ne fonctionne pas
Aladdin Mhemed

1
Voir ce fiddle.jshell.net/agvTz/38 Vous devriez appeler le function$ scope. $ Apply en passant une fonction comme argument. Et $ apply devrait être appelé lorsque vous apporterez les modifications sur $ scope, donc à l'intérieur de onSelect. Ah, je m'attends à ce que vous mettiez la manipulation DOM à l'intérieur du contrôleur juste pour l'exemple, dans une vraie application, c'est faux;)
Renan Tomal Fernandes

alors que dois-je faire pour faire une bonne pratique angulaire? séparer la manipulation DOM en une directive?
Aladdin Mhemed

2
Oui, voici un exemple fiddle.jshell.net/agvTz/39 la directive peut être utilisée autant de fois que vous le souhaitez avec un simple datepicker="scopeKeyThatYouWant"dans l'entrée
Renan Tomal Fernandes

Appelez simplement .. $ scope. $ Digest () pour régler. va-t-il ralentir?
Thant Zin

29

Utilisez simplement;

$('#selectedDueDate').val(dateText).trigger('input');

Je l'ai utilisé trigger('input')avec un datepicker dans son onSelectévénement. Il met à jour correctement la valeur.
iman

Cela a fait l'affaire pour moi. Je mettais à jour la valeur d'une entrée liée à partir d'un test de directive et l'encapsulation de l' .val('something'appel dans un $apply(ou l'appel $digestpar la suite) ne fonctionnait pas.
Tom Seldon

2
Après des heures de recherche, c'est celui qui a fonctionné! Je vous remercie!
Dan

Alhamdulillah, parfait, ça marche pour moi. merci de sauver mes heures
Syed Md. Kamruzzaman

Vrai. J'utilisais $('#selectedDueDate').val(dateText).trigger('change');ce qui ne fonctionnait pas pour moi. .trigger('input');fonctionne comme la magie
jafarbtech

17

J'ai trouvé que si vous ne mettez pas la variable directement dans la portée, elle se met à jour de manière plus fiable.

Essayez d'utiliser un "dateObj.selectedDate" et dans le contrôleur, ajoutez le selectedDate à un objet dateObj comme suit:

$scope.dateObj = {selectedDate: new Date()}

Cela a fonctionné pour moi.


4
Cela a fonctionné pour moi aussi. J'avais perdu plus de 2 heures à essayer de comprendre pourquoi la liaison bidirectionnelle ne fonctionnait pas. Cela fonctionnait bien sur la page, mais la portée du contrôleur n'était pas mise à jour. Ensuite, j'ai essayé votre approche par désespoir (parce que cela n'avait aucun sens pour moi que cela soit vrai) et putain, ça a marché! Merci!
TMc

3
Pareil ici - n'importe quel gourou angularJs peut-il expliquer pourquoi cela fonctionne? C'est comme si la vue avait sa propre portée imbriquée et parfois vous êtes bloqué en mettant à jour uniquement la vue-portée, pas la portée du contrôleur
Tom Carver

1
Fonctionne bien pour moi! Je me demande pourquoi cela ne fonctionne pas de la même manière sur la lunette nue.
Stephen

1
Cela a peu à voir avec angular, et beaucoup à voir avec le fonctionnement de javascript. Lorsque vous affectez la variable d'étendue à un objet, vous l'affectez par référence, par opposition à par valeur, comme c'est le cas lorsqu'une var est définie comme égale à une valeur primitive. J'en ai parlé dans mon post ici. stackoverflow.com/questions/14527377/… . J'ai fait référence à ce plunk que j'ai fait dans ce post pour illustrer plnkr.co/edit/WkCT6aYao3qCmzJ8t5xg?p=preview .
Nick Brady

4

Essaye ça

var selectedDueDateField = document.getElementById("selectedDueDate");
var element = angular.element(selectedDueDateField);
element.val('new value here');
element.triggerHandler('input');

Il est utilisable lorsque vous n'avez pas accès au contrôleur $ scope de l'angular. Lorsque vous écrivez un script avec un Tampermonkey, par exemple.
wassertim

3

Exécutez simplement la ligne suivante à la fin de votre fonction:

$ scope. $ apply ()


2

Quoi qu'il arrive en dehors de la portée d'Angular, Angular ne le saura jamais.

Le cycle de résumé met les modifications du modèle -> contrôleur, puis du contrôleur -> modèle.

Si vous avez besoin de voir le dernier modèle, vous devez déclencher le cycle de résumé

Mais il y a une chance qu'un cycle de digestion soit en cours, nous devons donc vérifier et lancer le cycle.

De préférence, effectuez toujours une application en toute sécurité.

       $scope.safeApply = function(fn) {
            if (this.$root) {
                var phase = this.$root.$$phase;
                if (phase == '$apply' || phase == '$digest') {
                    if (fn && (typeof (fn) === 'function')) {
                        fn();
                    }
                } else {
                    this.$apply(fn);
                }
            }
        };


      $scope.safeApply(function(){
          // your function here.
      });

1

AngularJS transmet la chaîne, les nombres et les booléens par valeur pendant qu'il transmet les tableaux et les objets par référence. Vous pouvez donc créer un objet vide et faire de votre date une propriété de cet objet. De cette façon, angular détectera les changements de modèle.

Dans le contrôleur

app.module('yourModule').controller('yourController',function($scope){
$scope.vm={selectedDate:''}
});

En html

<div ng-controller="yourController">
<input id="selectedDueDate" type="text" ng-model="vm.selectedDate" />
</div>

1

Vous devez déclencher l' événement de modification de l'élément d'entrée car ng-model écoute les événements d'entrée et la portée sera mise à jour. Cependant, le déclencheur jQuery régulier ne fonctionnait pas pour moi. Mais voici ce qui fonctionne comme un charme

$("#myInput")[0].dispatchEvent(new Event("input", { bubbles: true })); //Works

Le suivi n'a pas fonctionné

$("#myInput").trigger("change"); // Did't work for me

Vous pouvez en savoir plus sur la création et la distribution d'événements synthétiques .


0

J'ai écrit ce petit plugin pour jQuery qui fera tous les appels pour .val(value)mettre à jour l'élément angulaire s'il est présent:

(function($, ng) {
  'use strict';

  var $val = $.fn.val; // save original jQuery function

  // override jQuery function
  $.fn.val = function (value) {
    // if getter, just return original
    if (!arguments.length) {
      return $val.call(this);
    }

    // get result of original function
    var result = $val.call(this, value);

    // trigger angular input (this[0] is the DOM object)
    ng.element(this[0]).triggerHandler('input');

    // return the original result
    return result; 
  }
})(window.jQuery, window.angular);

Insérez simplement ce script après jQuery et angular.js et les val(value)mises à jour devraient maintenant fonctionner correctement .


Version réduite:

!function(n,t){"use strict";var r=n.fn.val;n.fn.val=function(n){if(!arguments.length)return r.call(this);var e=r.call(this,n);return t.element(this[0]).triggerHandler("input"),e}}(window.jQuery,window.angular);

Exemple:


Cette réponse a été copiée textuellement de ma réponse à une autre question similaire .


0

Utilisez simplement:

$('#selectedDueDate').val(dateText).trigger('input');

au lieu de:

$('#selectedDueDate').val(dateText);
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.