AngularJS: powiązanie modelu ng nie aktualizuje się po zmianie za pomocą jQuery


98

To jest mój HTML:

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

Kiedy piszę w polu, model jest aktualizowany za pomocą mechanizmu dwukierunkowego wiązania. Słodkie.

Jednak kiedy robię to przez JQuery ...

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

Nie aktualizuje modelu. Czemu?


1
Dlaczego miałbyś zacząć od drugiego? Używasz frameworka, a następnie decydujesz się go ominąć i ustawić wartość poprzez manipulację DOM. Najlepsza rada, jakiej można udzielić początkującym użytkownikom z angularem: zapomnij o istnieniu jquery. w 90% przypadków kątowy będzie wystarczający, a pozostałe 10% można osiągnąć poprzez jqlite wewnątrz linku dyrektywy (element jest w rzeczywistości opakowanym elementem jqlite gotowym do manipulacji).
Nieważne

1
bardzo ważne pytanie
Syed Md. Kamruzzaman

są bardzo dobre powody, dla których możesz chcieć zmienić modele kątowe za pomocą manipulacji dom, być może jesteś programistą pracującym z narzędziem do testów A / B i chcesz wypełniać formularze za kulisami lub pracujesz nad skryptem greasemonkey do autouzupełniania formularzy.
Shadaez,

Odpowiedzi:


134

Angular nie wie o tej zmianie. W tym celu należy zadzwonić $scope.$digest()lub dokonać zmiany w $scope.$apply():

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

Zobacz to, aby lepiej zrozumieć brudne sprawdzanie

UPDATE : Oto przykład


Zrobiłem to tak, jak mówisz: fiddle.jshell.net/AladdinMhaimeed/agvTz/8, ale to nie działa
Aladdin Mhemed

1
Zobacz ten fiddle.jshell.net/agvTz/38 Powinieneś wywołać function$ scope. $ Apply, przekazując funkcję jako argument. A $ apply powinno zostać wywołane, kiedy dokonasz zmian w $ scope, a więc wewnątrz onSelect. Ach, spodziewam się, że umieścisz manipulację DOM wewnątrz kontrolera tylko dla przykładu, w prawdziwej aplikacji to jest złe;)
Renan Tomal Fernandes

więc co powinienem zrobić, aby zrobić dobrą praktykę kątową? oddzielna manipulacja DOM do dyrektywy?
Aladdin Mhemed

2
Tak, tutaj przykład fiddle.jshell.net/agvTz/39 dyrektywy można używać tyle razy, ile chcesz, z prostym datepicker="scopeKeyThatYouWant"wprowadzeniem
Renan Tomal Fernandes

Po prostu wywołaj .. $ scope. $ Digest (), aby rozstrzygnąć. czy to zwolni?
Thant Zin

29

Po prostu użyj;

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

Użyłem trigger('input')w tym onSelectprzypadku z DatePicker . Prawidłowo aktualizuje wartość.
iman

To załatwiło sprawę dla mnie. Aktualizowałem wartość powiązanego wejścia z testu dyrektywy i zawijanie .val('something'wywołania w $apply(lub wywołanie $digestpóźniej) nie działało.
Tom Seldon

2
Po godzinach poszukiwań ten zadziałał! Dziękuję Ci!
Dan

Alhamdulillah, doskonale, działa dla mnie. dzięki zaoszczędzeniu czasu
Syed Md. Kamruzzaman

Prawdziwe. Używałem, $('#selectedDueDate').val(dateText).trigger('change');co nie działało dla mnie. .trigger('input');działa jak magia
jafarbtech

17

Odkryłem, że jeśli nie umieścisz zmiennej bezpośrednio w zakresie, aktualizuje się bardziej niezawodnie.

Spróbuj użyć jakiegoś „dateObj.selectedDate” iw kontrolerze dodaj selectedDate do obiektu dateObj w następujący sposób:

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

To zadziałało dla mnie.


4
U mnie też to zadziałało. Zmarnowałem ponad 2 godziny, próbując dowiedzieć się, dlaczego dwukierunkowe wiązanie nie działa. Działało dobrze na stronie, ale zakres w kontrolerze nie był aktualizowany. Potem spróbowałem twojego podejścia z desperacji (ponieważ nie miało dla mnie sensu, żeby to było prawdą) i do cholery, zadziałało! Dzięki!
TMc

3
To samo tutaj - czy jakikolwiek guru angularJs może wyjaśnić, dlaczego to działa? To tak, jakby widok miał swój własny zagnieżdżony zakres i czasami utkniesz, aktualizując tylko zakres widoku, a nie zakres kontrolera
Tom Carver

1
U mnie działa dobrze! Zastanawiam się, dlaczego u licha to nie działa tak samo na gołym teleskopie.
Stephen

1
Ma to niewiele wspólnego z angularem, a dużo z tym, jak działa javascript. Kiedy przypisujesz zmienną zasięgu do obiektu, przypisujesz ją przez odwołanie, w przeciwieństwie do wartości, jak to się robi, gdy zmienna jest ustawiona na wartość pierwotną. Mówiłem o tym w moim poście tutaj. stackoverflow.com/questions/14527377/… . Odwołałem się do tej wstawki, którą stworzyłem w tym poście, aby zilustrować plnkr.co/edit/WkCT6aYao3qCmzJ8t5xg?p=preview .
Nick Brady

4

Spróbuj tego

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

Jest użyteczny, gdy nie masz dostępu do kontrolera kątowego $ scope. Na przykład, kiedy piszesz skrypt z Tampermonkey.
wassertim


2

Cokolwiek dzieje się poza zakresem Angulara, Angular nigdy się o tym nie dowie.

Cykl Digest wstawia zmiany z modelu -> kontroler a następnie ze sterownika -> model.

Jeśli chcesz zobaczyć najnowszy model, musisz uruchomić cykl podsumowania

Ale istnieje szansa, że ​​trwa cykl podsumowania, więc musimy sprawdzić i zainicjować cykl.

Najlepiej zawsze wykonywać bezpieczne nakładanie.

       $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 przekazuje łańcuchy, liczby i wartości logiczne według wartości, podczas gdy przekazuje tablice i obiekty przez referencje. Możesz więc utworzyć pusty obiekt i ustawić datę jako właściwość tego obiektu. W ten sposób angular wykryje zmiany modelu.

W kontrolerze

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

W html

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

1

Musisz wyzwolić zdarzenie zmiany elementu wejściowego, ponieważ ng-model nasłuchuje zdarzeń wejściowych, a zakres zostanie zaktualizowany. Jednak zwykły wyzwalacz jQuery nie działał dla mnie. Ale oto, co działa jak urok

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

Śledzenie nie działało

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

Możesz przeczytać więcej o tworzeniu i wysyłaniu zdarzeń syntetycznych .


0

Napisałem tę małą wtyczkę dla jQuery, która będzie wykonywać wszystkie wywołania .val(value)aktualizacji elementu kątowego, jeśli jest obecny:

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

Po prostu włóż ten skrypt po tym, jak jQuery i angular.js, a val(value)aktualizacje powinny teraz działać dobrze.


Wersja zminimalizowana:

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

Przykład:


Ta odpowiedź została dosłownie skopiowana z mojej odpowiedzi na inne podobne pytanie .


0

Po prostu użyj:

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

zamiast:

$('#selectedDueDate').val(dateText);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.