AngularJS: l'associazione ng-model non si aggiorna quando viene modificata con jQuery


98

Questo è il mio HTML:

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

Quando digito nella casella, il modello viene aggiornato tramite il meccanismo di rilegatura a 2 vie. Dolce.

Tuttavia, quando lo faccio tramite JQuery ...

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

Non aggiorna il modello. Perché?


1
Perché dovresti fare il secondo per cominciare? Stai utilizzando un framework e quindi decidi di bypassarlo e impostare il valore tramite la manipolazione DOM. Il miglior consiglio che si può dare con angular ai principianti: dimenticare che esiste jquery. nel 90% dei casi sarà sufficiente angolare e il restante 10% può essere ottenuto tramite jqlite all'interno di un link di direttiva (l'elemento è in realtà un elemento avvolto in jqlite pronto per essere manipolato).
Void

1
domanda molto importante
Syed Md. Kamruzzaman

ci sono ottime ragioni per cui potresti voler cambiare i modelli angolari con la manipolazione dom, forse sei uno sviluppatore che lavora con uno strumento di test A / B e vuoi compilare moduli dietro le quinte, o stai lavorando su uno script greasemonkey per compilare automaticamente i moduli.
Shadaez

Risposte:


134

Angular non sa di quel cambiamento. Per questo dovresti chiamare $scope.$digest()o apportare la modifica all'interno di $scope.$apply():

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

Vedere questo per comprendere meglio il controllo sporco

AGGIORNAMENTO : Ecco un esempio


Ho fatto questo come dici tu: fiddle.jshell.net/AladdinMhaimeed/agvTz/8 ma non funziona
Aladdin Mhemed

1
Vedi questo fiddle.jshell.net/agvTz/38 Dovresti chiamare function$ scope. $ Apply passando una funzione come argomento. E $ apply dovrebbe essere chiamato quando apporterai le modifiche su $ scope, quindi, all'interno di onSelect. Ah, mi aspetto che tu metta la manipolazione DOM all'interno del controller solo per esempio, in un'app reale è sbagliato;)
Renan Tomal Fernandes

allora cosa devo fare per fare una buona pratica angolare? manipolazione DOM separata in una direttiva?
Aladdin Mhemed

2
Sì, qui un esempio fiddle.jshell.net/agvTz/39 la direttiva può essere utilizzata tutte le volte che vuoi con un semplice datepicker="scopeKeyThatYouWant"nell'input
Renan Tomal Fernandes

Basta chiamare .. $ scope. $ Digest () per risolvere. rallenterà?
Thant Zin

29

Basta usare;

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

Ho usato trigger('input')con un datepicker nel suo onSelectevento. Aggiorna correttamente il valore.
iman

Questo ha funzionato per me. Stavo aggiornando il valore di un input associato da un test di direttiva e il wrapping della .val('something'chiamata in un $apply(o la chiamata in $digestseguito) non funzionava.
Tom Seldon

2
Dopo ore di ricerca, questo è stato quello che ha funzionato! Grazie!
Dan

Alhamdulillah, perfetto, per me sta funzionando. grazie per salvare le mie ore
Syed Md. Kamruzzaman

Vero. Stavo usando $('#selectedDueDate').val(dateText).trigger('change');che non funzionava per me. .trigger('input');funziona come per magia
jafarbtech

17

Ho scoperto che se non metti la variabile direttamente contro l'ambito, si aggiorna in modo più affidabile.

Prova a utilizzare un po 'di "dateObj.selectedDate" e nel controller aggiungi selectedDate a un oggetto dateObj in questo modo:

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

Questo ha funzionato per me.


4
Questo ha funzionato anche per me. Ho sprecato più di 2 ore cercando di capire perché l'associazione a due vie non funzionava. Ha funzionato bene sulla pagina, ma l'ambito nel controller non è stato aggiornato. Poi ho provato il tuo approccio per disperazione (perché per me non aveva senso che fosse vero) e dannatamente, ha funzionato! Grazie!
TMc

3
Lo stesso qui: un guru di angularJs può spiegare perché funziona? È come se la vista avesse il proprio ambito annidato ea volte ti blocchi ad aggiornare solo l'ambito della visualizzazione, non l'ambito del controller
Tom Carver

1
Funziona bene per me! Mi chiedo perché diavolo non funziona allo stesso modo sullo scopo.
Stephen

1
Ha poco a che fare con angular e molto a che fare con il funzionamento di javascript. Quando si assegna la variabile di ambito a un oggetto, la si assegna per riferimento, invece che per valore, come avviene quando una variabile è impostata uguale a un valore primitivo. Ne ho parlato nel mio post qui. stackoverflow.com/questions/14527377/… . Ho fatto riferimento a questo plunk che ho fatto in quel post per illustrare plnkr.co/edit/WkCT6aYao3qCmzJ8t5xg?p=preview .
Nick Brady

4

Prova questo

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

È utilizzabile quando non si ha accesso al controller $ scope di angular. Quando scrivi uno script con un Tampermonkey, per esempio.
wassertim

3

Basta eseguire la seguente riga alla fine della funzione:

$ scope. $ apply ()


2

Qualunque cosa accada al di fuori dell'ambito di Angular, Angular non lo saprà mai.

Il ciclo di digest inserisce le modifiche dal modello -> controller e poi da controller -> modello.

Se hai bisogno di vedere l'ultimo modello, devi attivare il ciclo di digest

Ma c'è la possibilità che un ciclo di digestione sia in corso, quindi dobbiamo controllare e avviare il ciclo.

Preferibilmente, eseguire sempre un'applicazione sicura.

       $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 passa stringhe, numeri e booleani per valore mentre passa array e oggetti per riferimento. Quindi puoi creare un oggetto vuoto e rendere la tua data una proprietà di quell'oggetto. In questo modo angolare rileverà i cambiamenti del modello.

Nel controller

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

È necessario attivare l' evento di modifica dell'elemento di input perché ng-model ascolta gli eventi di input e l'ambito verrà aggiornato. Tuttavia, il normale trigger jQuery non ha funzionato per me. Ma ecco cosa funziona come un fascino

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

Seguire non ha funzionato

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

Puoi leggere ulteriori informazioni sulla creazione e l'invio di eventi sintetici .


0

Ho scritto questo piccolo plugin per jQuery che farà tutte le chiamate per .val(value)aggiornare l'elemento angolare se presente:

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

Basta inserire questo script dopo jQuery e angular.js e gli val(value)aggiornamenti ora dovrebbero funzionare correttamente .


Versione ridotta:

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

Esempio:


Questa risposta è stata copiata alla lettera dalla mia risposta a un'altra domanda simile .


0

Usa solo:

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

invece di:

$('#selectedDueDate').val(dateText);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.