AngularJS: Die ng-Modellbindung wird nicht aktualisiert, wenn sie mit jQuery geändert wird


98

Das ist mein HTML:

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

Wenn ich in die Box tippe, wird das Modell über den 2-Wege-Bindungsmechanismus aktualisiert. Süss.

Allerdings , wenn ich dies tun über JQuery ...

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

Das Modell wird nicht aktualisiert. Warum?


1
Warum sollten Sie den zweiten machen? Sie verwenden ein Framework und entscheiden sich dann, es zu umgehen und den Wert durch DOM-Manipulation festzulegen. Der beste Rat, den man Anfängern mit eckigen Worten geben kann: Vergiss, dass es eine Abfrage gibt. In 90% der Fälle reicht ein Winkel aus, und die restlichen 10% können durch jqlite innerhalb des Links einer Direktive erreicht werden (Element ist tatsächlich ein mit jqlite umwickeltes Element, das manipuliert werden kann).
Nichtig

1
sehr wichtige Frage
Syed Md. Kamruzzaman

Es gibt sehr gute Gründe, warum Sie Winkelmodelle mit Dom-Manipulation ändern möchten, vielleicht ein Entwickler sind, der mit einem A / B-Testwerkzeug arbeitet und Formulare hinter den Kulissen ausfüllen möchte, oder an einem Greasemonkey-Skript arbeiten Formulare automatisch ausfüllen.
Shadaez

Antworten:


134

Angular weiß nichts von dieser Änderung. Dazu sollten Sie Folgendes anrufen $scope.$digest()oder ändern $scope.$apply():

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

Sehen Sie sich das an, um die Schmutzprüfung besser zu verstehen

UPDATE : Hier ist ein Beispiel


Ich habe das so gemacht, wie du es sagst: fiddle.jshell.net/AladdinMhaimeed/agvTz/8, aber es funktioniert nicht
Aladdin Mhemed

1
Siehe dies fiddle.jshell.net/agvTz/38 Sie sollten den function$ scope aufrufen . $ Apply übergeben eine Funktion als Argument. Und $ apply sollte aufgerufen werden, wenn Sie die Änderungen am $ scope vornehmen, also innerhalb von onSelect. Ah, ich gehe davon aus, dass Sie die DOM-Manipulation nur für das Beispiel in den Controller
einfügen.

Was soll ich dann tun, um eine gute Winkelübung zu machen? DOM-Manipulation in eine Direktive umwandeln?
Aladdin Mhemed

2
Ja, hier ein Beispiel fiddle.jshell.net/agvTz/39 Die Direktive kann mit einer einfachen datepicker="scopeKeyThatYouWant"Eingabe so oft verwendet werden, wie Sie möchten
Renan Tomal Fernandes

Rufen Sie einfach .. $ scope. $ Digest () auf, um abzurechnen. wird es langsamer?
Thant Zin

29

Benutz einfach;

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

Ich habe trigger('input')mit einem Datepicker in seiner onSelectVeranstaltung verwendet. Der Wert wird korrekt aktualisiert.
Iman

Das hat den Trick für mich getan. Ich habe den Wert einer gebundenen Eingabe aus einem Direktiventest aktualisiert und das Einschließen des .val('something'Aufrufs in einen $apply(oder einen $digestanschließenden Aufruf ) hat nicht funktioniert.
Tom Seldon

2
Nach stundenlangem Suchen funktionierte dies! Danke dir!
Dan

Alhamdulillah, perfekt, es funktioniert für mich. Dank meine Stunden zu retten
Syed Md. Kamruzzaman

Wahr. Ich benutzte, $('#selectedDueDate').val(dateText).trigger('change');was für mich nicht funktionierte. .trigger('input');funktioniert wie Magie
Jafarbtech

17

Ich habe festgestellt, dass die Variable zuverlässiger aktualisiert wird, wenn Sie sie nicht direkt in den Bereich stellen.

Versuchen Sie es mit einem "dateObj.selectedDate" und fügen Sie im Controller das selectedDate wie folgt zu einem dateObj-Objekt hinzu:

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

Das hat bei mir funktioniert.


4
Das hat auch bei mir funktioniert. Ich hatte über 2 Stunden damit verbracht herauszufinden, warum die bidirektionale Bindung nicht funktionierte. Auf der Seite hat es einwandfrei funktioniert, aber der Bereich im Controller wurde nicht aktualisiert. Dann habe ich deinen Ansatz aus Verzweiflung ausprobiert (weil es für mich keinen Sinn machte, dass dies wahr sein sollte) und verdammt, es hat funktioniert! Vielen Dank!
TMc

3
Gleiches hier - kann ein eckiger Guru erklären, warum dies funktioniert? Es ist, als ob die Ansicht einen eigenen verschachtelten Bereich hat und manchmal bleibt man beim Aktualisieren nur des Ansichtsbereichs hängen, nicht des Controller-Bereichs
Tom Carver

1
Funktioniert gut für mich! Ich frage mich, warum zum Teufel es auf dem bloßen Zielfernrohr nicht gleich funktioniert.
Stephen

1
Es hat wenig mit Winkel zu tun und viel damit, wie Javascript funktioniert. Wenn Sie die Bereichsvariable einem Objekt zuweisen, weisen Sie sie als Referenz zu, im Gegensatz zu einem Wert, der ausgeführt wird, wenn eine Variable gleich einem primitiven Wert gesetzt wird. Ich habe in meinem Beitrag hier darüber gesprochen. stackoverflow.com/questions/14527377/… . Ich habe auf diesen Plunk verwiesen, den ich in diesem Beitrag gemacht habe, um plnkr.co/edit/WkCT6aYao3qCmzJ8t5xg?p=preview zu veranschaulichen .
Nick Brady

4

Versuche dies

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

Es ist verwendbar, wenn Sie keinen Zugriff auf den Controller-Bereich des Winkels haben. Wenn Sie beispielsweise ein Skript mit einem Tampermonkey schreiben.
Wassertim

3

Führen Sie einfach die folgende Zeile am Ende Ihrer Funktion aus:

$ scope. $ apply ()


2

Was auch immer außerhalb des Bereichs von Angular passiert, Angular wird das nie erfahren.

Im Digest-Zyklus werden die Änderungen vom Modell -> Controller und dann vom Controller -> Modell übernommen.

Wenn Sie das neueste Modell sehen möchten, müssen Sie den Digest-Zyklus auslösen

Es besteht jedoch die Möglichkeit, dass ein Verdauungszyklus ausgeführt wird. Daher müssen wir den Zyklus überprüfen und initiieren.

Führen Sie vorzugsweise immer eine sichere Anwendung durch.

       $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 übergibt Zeichenfolgen, Zahlen und Boolesche Werte als Wert, während Arrays und Objekte als Referenz übergeben werden. Sie können also ein leeres Objekt erstellen und Ihr Datum zu einer Eigenschaft dieses Objekts machen. Auf diese Weise erkennt Angular Modelländerungen.

In der Steuerung

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

In HTML

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

1

Sie müssen das Änderungsereignis des Eingabeelements auslösen, da ng-model Eingabeereignisse abhört und der Bereich aktualisiert wird. Der reguläre jQuery-Trigger funktionierte jedoch bei mir nicht. Aber hier ist, was wie ein Zauber funktioniert

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

Das Folgen hat nicht funktioniert

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

Weitere Informationen zum Erstellen und Versenden synthetischer Ereignisse finden Sie hier .


0

Ich habe dieses kleine Plugin für jQuery geschrieben, das alle Aufrufe zum .val(value)Aktualisieren des Winkelelements ausführt, falls vorhanden:

(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);

Fügen Sie dieses Skript einfach nach jQuery und angle.js ein, und val(value)Updates sollten jetzt gut funktionieren .


Minimierte Version:

!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);

Beispiel:


Diese Antwort wurde wörtlich aus meiner Antwort auf eine andere ähnliche Frage kopiert .


0

Benutz einfach:

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

anstatt:

$('#selectedDueDate').val(dateText);
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.