Actualice el modelo angular después de establecer el valor de entrada con jQuery


160

Tengo este escenario simple:

Elemento de entrada cuyo valor cambia mediante el método val () de jQuery.

Estoy tratando de actualizar el modelo angular con el valor que estableció jQuery. Traté de escribir una directiva simple, pero no está haciendo lo que quiero.

Aquí está la directiva:

var myApp = angular.module('myApp', []);
myApp.directive('testChange', function() {
    return function(scope, element, attrs) {        
        element.bind('change', function() {
            console.log('value changed');
        })
    }
})

Esta es la parte de jQuery:

$(function(){
    $('button').click(function(){
        $('input').val('xxx');
    })
})

y html:

<div ng-app="myApp">
    <div ng-controller="MyCtrl">
        <input test-change ng-model="foo" />
        <span>{{foo}}</span>
    </div>
</div>

<button>clickme</button>

Aquí está el violín con mi intento:
http://jsfiddle.net/U3pVM/743/

¿Puede alguien indicarme el camino correcto?


2
Mezclar AngularJS y jQuery, fuera de las directivas, a menudo es una mala idea. ¿Es obligatorio jQuery para lo que quieres hacer?
Blackhole

¿Por qué no configura el evento de clic con angular a una función en el controlador para cambiar el valor del modelo?
Jimmy Kane

@Blackhole, estoy usando el complemento de carga jquery que hace su trabajo y coloca la ruta al archivo cargado en la entrada oculta. Todo lo que necesito es tener acceso a esa ruta en el modelo angular. Así que pensé que lo establecería como un valor de entrada oculta y, después de cambiarlo, actualice el modelo angular.
Michal J.

No veo la llamada de Jquery en su violín ... ¿Puede verificar y señalar dónde cambia el valor con JQ?
Valentyn Shybanov

Su violín se vincula a una aplicación de muestra de Todo, que no se relaciona con esta pregunta.
Stewie

Respuestas:


322

ngModel escucha el evento "input", por lo que para "arreglar" su código necesitaría activar ese evento después de establecer el valor:

$('button').click(function(){
    var input = $('input');
    input.val('xxx');
    input.trigger('input'); // Use for Chrome/Firefox/Edge
    input.trigger('change'); // Use for Chrome/Firefox/Edge + IE11
});

Para la explicación de este comportamiento en particular, consulte esta respuesta que le di hace un tiempo: " ¿Cómo AngularJS captura internamente eventos como 'onclick', 'onchange'? "


Pero desafortunadamente, este no es el único problema que tiene. Como se señaló con otros comentarios de publicaciones, su enfoque centrado en jQuery es simplemente incorrecto. Para obtener más información, eche un vistazo a esta publicación: ¿Cómo “pienso en AngularJS” si tengo antecedentes de jQuery? )


55
¡Frio! Con una excepción: ¿por qué no funciona en entradas ocultas? Cuando cambio mi tipo de entrada a texto, funciona como se esperaba.
Karol

8
Esto resolvió un problema importante al usar los complementos de calendario jquery. Al piratear el complemento para activar ('input') después de input.val (), el evento ng-change se activa después de que el usuario seleccione la fecha del calendario. ¡Gracias!
Drone Brain

3
Funciona perfecto en entradas ocultas: element.triggerHandler ("cambio");
falko

2
Esto no funciona para mi. $ ("# Distancia"). Val (datos); $ ("# Distancia"). Trigger ('input');
Stas Svishov


61

Espero que esto sea útil para alguien.

No pude obtener el jQuery('#myInputElement').trigger('input')evento para recoger mi aplicación angular.

Sin embargo, pude llegar angular.element(jQuery('#myInputElement')).triggerHandler('input')a ser recogido.


2
explique la solución, no solo escriba código. me sale angular no es $ no es una función
Thakhani Tharage

1
$ representa jQuery aquí. Está etiquetado en la pregunta.
Ginman

Esto también funcionó para mí mientras $ ('myInputElement'). Trigger ('input') era escamoso. A veces parecía funcionar al azar, pero no en otros, y nunca pude establecer un patrón. Esta solución funciona siempre.
BryanP

2
El mío está trabajado con el prefijo #:angular.element($('#myInputElement')).triggerHandler('input')
Ebru Yener

Este triggerHandler también funcionó para mí, estaba tratando con textarea, no input. Gracias
Mounir

25

La respuesta aceptada que desencadenaba un inputevento con jQuery no funcionó para mí. Crear un evento y enviarlo con JavaScript nativo fue el truco.

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

Esta fue la solución para mí también. Usando Angular 1.1.5, jQuery 2.2.4 y Jasmine 2.5.3. Muy, MUY extraño que no funcionen otros mecanismos de activación.
Lars-Erik

Estaba buscando el método JS nativo para activar la validación de formularios al configurar una entrada en un script y esto funcionó para mí, gracias.
dispersarse

Funciona incluso con Angular 5, ¡genial!
Igor

Gracias. Especialmente útil en webview.evaluatejavascript
Berry Wing

22

No creo que se requiera jQuery aquí.

Puede usar $ watch y ng-click en su lugar

<div ng-app="myApp">
  <div ng-controller="MyCtrl">
    <input test-change ng-model="foo" />
    <span>{{foo}}</span>

    <button ng-click=" foo= 'xxx' ">click me</button>
    <!-- this changes foo value, you can also call a function from your controller -->
  </div>
</div>

En tu controlador:

$scope.$watch('foo', function(newValue, oldValue) {
  console.log(newValue);
  console.log(oldValue);
});

1
Gracias Utopik, tienes razón, no debería mezclar jquery con angular cuando hay una manera de hacer el trabajo usando angular.
Michal J.

17

Debe usar el siguiente código para actualizar el alcance del modelo de entrada específico de la siguiente manera

$('button').on('click', function(){
    var newVal = $(this).data('val');
    $('select').val(newVal).change();

    var scope = angular.element($("select")).scope();
    scope.$apply(function(){
        scope.selectValue = newVal;
    });
});

1
var scope = angular.element($("select")).scope(); scope.$apply(function(){ scope.selectValue = newVal; }); esa porción me ayudó mucho. Mi problema fue actualizar un modelo angular después de una llamada jquery ajax en la función de éxito. Era lento con $ http porque había un detector de eventos de cambio duplicado para el elemento que estaba haciendo otra cosa, así que lo fusioné en jQuery.
Emmanuel Mahuni

1
El uso de element.scope () es una mala idea, ya que solo funciona con el registro habilitado. Si se encuentra en un sitio de producción, debe deshabilitar el inicio de sesión en su configuración $compileProvider.debugInfoEnabled(false);o $logProvider.debugEnabled(false);ver code.angularjs.org/1.4.0/docs/guide/production para obtener más información.
RWAM

Probé este método para la variable oculta y funcionó. var li_cuboid_id = $ ('# li_cuboid_id'); li_cuboid_id.val (json.cuboidId) .change (); var scope = angular.element ($ ("# li_cuboid_id")). scope (); scope. $ apply (function () {scope.cuboidId = json.cuboidId;}); la variable oculta se define como: <input id = "li_cuboid_id" type = hidden ng-model = "cuboidId">
Rahul Varadkar

7

Hice modificaciones solo en la inicialización del controlador agregando oyente en el botón de acción:

$(document).on('click', '#action-button', function () {
        $timeout(function () {
             angular.element($('#input')).triggerHandler('input');
        });
});

Otras soluciones no funcionaron en mi caso.


4

Sé que es un poco tarde para responder aquí, pero tal vez pueda ahorrar un día.

He estado lidiando con el mismo problema. Un modelo no se completará una vez que actualice el valor de entrada de jQuery. Intenté usar eventos desencadenantes pero ningún resultado.

Esto es lo que hice que puede salvar tu día.

Declare una variable dentro de su etiqueta de script en HTML.

Me gusta:

<script>
 var inputValue="";

// update that variable using your jQuery function with appropriate value, you want...

</script>

Una vez que hiciste eso usando el siguiente servicio de angular.

$ ventana

Ahora debajo de la función getData llamada desde el mismo alcance del controlador le dará el valor que desea.

var myApp = angular.module('myApp', []);

app.controller('imageManagerCtrl',['$scope','$window',function($scope,$window) {

$scope.getData = function () {
    console.log("Window value " + $window.inputValue);
}}]);

3

He escrito 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 ingrese este script después de jQuery y angular.js y val(value) actualizaciones ahora deberían funcionar bien.


Versión minimizada:

!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:


Perdón por desenterrar esto, pero ¿alguna idea de cómo esto podría / podría funcionar con la casilla de verificación o las selecciones ya que usan .prop en lugar de .val? Esto funciona perfectamente para los cambios de entrada .val como mínimo, así que gracias!
Austin

@ Austin, me temo que no lo sé. Esta es una pregunta de 5 años sobre dos tecnologías que se consideran obsoletas, por lo que no he tenido el conocimiento en mi cabeza. Sin embargo, la mejor de las suertes obtiene la solución que necesita.
dav_i

2

Si está utilizando IE, debe usar: input.trigger ("cambio");


2

añadir .change() después de establecer el valor.

ejemplo:('id').val.('value').change();

tampoco olvides agregar onchangeo ng-changeetiquetar en html


0

Hice esto para poder actualizar el valor de ngModel desde el exterior con Vanilla / jQuery:

function getScope(fieldElement) {
    var $scope = angular.element(fieldElement).scope();
    var nameScope;
    var name = fieldElement.getAttribute('name');
    if($scope) {
        if($scope.form) {
            nameScope = $scope.form[name];
        } else if($scope[name]) {
            nameScope = $scope[name];
        }
    }
    return nameScope;
}

function setScopeValue(fieldElement, newValue) {
    var $scope = getScope(fieldElement);
    if($scope) {
        $scope.$setViewValue(newValue);
        $scope.$validate();
        $scope.$render();
    }
}

setScopeValue(document.getElementById("fieldId"), "new value");
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.