AngularJS: el enlace del modelo ng no se actualiza cuando se cambia con jQuery

98

Este es mi HTML:

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

Cuando escribo en el cuadro, el modelo se actualiza mediante el mecanismo de enlace bidireccional. Dulce.

Sin embargo, cuando hago esto a través de JQuery ...

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

No actualiza el modelo. ¿Por qué?

Greg
fuente
1
¿Por qué harías el segundo para empezar? Está utilizando un marco y luego decide omitirlo y establecer el valor a través de la manipulación DOM. El mejor consejo que se puede dar con angular a los principiantes: olvide que existe jquery. en el 90% de los casos, el angular será suficiente y el 10% restante se puede lograr a través de jqlite dentro del enlace de una directiva (el elemento es en realidad un elemento envuelto en jqlite listo para ser manipulado).
Anulado el
1
pregunta muy importante
Syed Md. Kamruzzaman
hay muy buenas razones por las que es posible que desee cambiar los modelos angulares con manipulación de dom, tal vez sea un desarrollador que trabaja con una herramienta de prueba A / B y desee completar formularios detrás de escena, o esté trabajando en un script de greasemonkey para autocompletar formularios.
Shadaez

Respuestas:

134

Angular no conoce ese cambio. Para esto, debe llamar $scope.$digest()o realizar el cambio dentro de $scope.$apply():

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

Vea esto para comprender mejor la verificación sucia

ACTUALIZACIÓN : aquí hay un ejemplo

Renan Tomal Fernandes
fuente
Hice esto como dices: fiddle.jshell.net/AladdinMhaimeed/agvTz/8 pero no funciona
Aladdin Mhemed
1
Vea esto fiddle.jshell.net/agvTz/38 Debe llamar al function$ scope. $ Apply pasando una función como argumento. Y $ apply debe llamarse cuando realice los cambios en $ scope, por lo tanto, dentro de onSelect. Ah, espero que pongas la manipulación DOM dentro del controlador solo por ejemplo, en una aplicación real esto está mal;)
Renan Tomal Fernandes
entonces, ¿qué debo hacer para hacer una buena práctica angular? separar la manipulación DOM en una directiva?
Aladdin Mhemed
2
Sí, aquí un ejemplo de fiddle.jshell.net/agvTz/39, la directiva se puede usar tantas veces como desee con un simple datepicker="scopeKeyThatYouWant"en la entrada
Renan Tomal Fernandes
Simplemente llame a .. $ scope. $ Digest () para liquidar. se ralentizará?
Thant Zin
29

Solo usa;

$('#selectedDueDate').val(dateText).trigger('input');
Jan Kuri
fuente
He utilizado trigger('input')con un selector de fechas en su onSelectcaso. Actualiza el valor correctamente.
iman
Esto funcionó para mí. Estaba actualizando el valor de una entrada vinculada de una prueba de directiva y envolver la .val('something'llamada en un $apply(o llamar $digestdespués) no funcionó.
Tom Seldon
2
Después de horas de búsqueda, ¡este fue el que funcionó! ¡Gracias!
Dan
Alhamdulillah, perfecto, está funcionando para mí. gracias por salvar mis horas
Syed Md. Kamruzzaman
Cierto. Estaba usando lo $('#selectedDueDate').val(dateText).trigger('change');que no me funcionaba. .trigger('input');funciona como magia
jafarbtech
17

Descubrí que si no coloca la variable directamente contra el alcance, se actualiza de manera más confiable.

Intente usar algún "dateObj.selectedDate" y en el controlador agregue el selectedDate a un objeto dateObj de la siguiente manera:

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

Esto funcionó para mí.

Ben Taliadoros
fuente
4
Esto funcionó para mí también. Perdí más de 2 horas tratando de averiguar por qué el enlace bidireccional no funcionaba. Funcionó bien en la página, pero el alcance en el controlador no se estaba actualizando. Luego probé tu enfoque por desesperación (porque no tenía sentido para mí que esto fuera cierto) y maldita sea, ¡funcionó! ¡Gracias!
TMc
3
Lo mismo aquí: ¿puede algún gurú de angularJs explicar por qué funciona esto? Es como si la vista tuviera su propio alcance anidado y, a veces, te quedas atascado actualizando solo el alcance de la vista, no el alcance del controlador
Tom Carver
1
¡Funciona bien para mí! Me pregunto por qué diablos no funciona igual en el alcance desnudo.
Stephen
1
Tiene poco que ver con angular y mucho que ver con cómo funciona javascript. Cuando asigna la variable de alcance a un objeto, lo está asignando por referencia, en lugar de por valor como se hace cuando una var se establece igual a un valor primitivo. Hablé de eso en mi publicación aquí. stackoverflow.com/questions/14527377/… . Hice referencia a este plunk que hice en esa publicación para ilustrar plnkr.co/edit/WkCT6aYao3qCmzJ8t5xg?p=preview .
Nick Brady
4

Prueba esto

var selectedDueDateField = document.getElementById("selectedDueDate");
var element = angular.element(selectedDueDateField);
element.val('new value here');
element.triggerHandler('input');
Rayo
fuente
Se puede usar cuando no tiene acceso al controlador $ scope del angular. Cuando escribe un script con Tampermonkey, por ejemplo.
wassertim
3

Simplemente ejecute la siguiente línea al final de su función:

$ alcance. $ aplicar ()

Rohan M Nabar
fuente
2

Pase lo que pase fuera del alcance de Angular, Angular nunca lo sabrá.

El ciclo de resumen coloca los cambios del modelo -> controlador y luego del controlador -> modelo.

Si necesita ver el último modelo, debe activar el ciclo de resumen

Pero existe la posibilidad de que haya un ciclo de resumen en curso, por lo que debemos verificar e iniciar el ciclo.

Preferiblemente, realice siempre una aplicación segura.

       $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.
      });
Rajendra kumar Vankadari
fuente
1

AngularJS pasa cadenas, números y booleanos por valor mientras pasa matrices y objetos por referencia. Entonces puedes crear un objeto vacío y hacer que tu fecha sea una propiedad de ese objeto. De esa manera angular detectará cambios en el modelo.

En controlador

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>
Himanshu Mittal
fuente
1

Debe activar el evento de cambio del elemento de entrada porque ng-model escucha los eventos de entrada y el alcance se actualizará. Sin embargo, el disparador jQuery regular no funcionó para mí. Pero esto es lo que funciona de maravilla

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

Seguir no funcionó

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

Puede leer más sobre cómo crear y enviar eventos sintéticos .

Hari Das
fuente
0

Escribí este pequeño complemento para jQuery que hará todas las llamadas para .val(value)actualizar el elemento angular si está 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);

Simplemente inserte este script después de jQuery y angular.js y las val(value)actualizaciones ahora deberían funcionar bien.


Versión minificada:

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

Ejemplo:


Esta respuesta fue copiada literalmente de mi respuesta a otra pregunta similar .

dav_i
fuente
0

Solo usa:

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

en vez de:

$('#selectedDueDate').val(dateText);
Wekerle Tibor
fuente