Mettre à jour le modèle angulaire après avoir défini la valeur d'entrée avec jQuery

160

J'ai ce scénario simple:

Élément d'entrée dont la valeur est modifiée par la méthode val () de jQuery.

J'essaie de mettre à jour le modèle angulaire avec la valeur définie par jQuery. J'ai essayé d'écrire une directive simple, mais ça ne fait pas ce que je veux.

Voici la directive:

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

c'est la partie jQuery:

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

et html:

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

<button>clickme</button>

Voici le violon avec mon essai:
http://jsfiddle.net/U3pVM/743/

Quelqu'un peut-il s'il vous plaît me diriger dans la bonne direction?

Michal J.
la source
2
Mélanger AngularJS et jQuery, en dehors des directives, est souvent une mauvaise idée. Est-ce que jQuery est obligatoire pour ce que vous voulez faire?
Blackhole
Pourquoi ne définissez-vous pas l'événement de clic angulaire sur une fonction du contrôleur pour modifier la valeur du modèle?
Jimmy Kane le
@Blackhole, j'utilise le plugin de téléchargement jquery qui fait son travail et met le chemin du fichier téléchargé dans l'entrée cachée. Tout ce dont j'ai besoin est d'avoir accès à ce chemin en modèle angulaire. J'ai donc pensé que je le définirais comme une valeur d'entrée masquée et, après sa modification, mettre à jour le modèle angulaire.
Michal J.
Je ne vois pas l'appel Jquery dans votre violon ... Pouvez-vous vérifier et indiquer où vous changez de valeur avec JQ?
Valentyn Shybanov le
Votre violon est lié à un exemple d'application Todo, qui n'est pas lié à cette question.
Stewie le

Réponses:

322

ngModel écoute l'événement "input", donc pour "corriger" votre code, vous devez déclencher cet événement après avoir défini la valeur:

$('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
});

Pour l'explication de ce comportement particulier, consultez cette réponse que j'ai donnée il y a quelque temps: " Comment AngularJS intercepte-t-il en interne des événements tels que 'onclick', 'onchange'? "


Mais malheureusement, ce n'est pas le seul problème que vous rencontrez. Comme indiqué dans d'autres commentaires, votre approche centrée sur jQuery est tout simplement fausse. Pour plus d'informations, jetez un œil à cet article: Comment est-ce que je "pense dans AngularJS" si j'ai un arrière-plan jQuery? ).

Stewie
la source
5
Cool! À une exception près: pourquoi cela ne fonctionne pas sur les entrées cachées? Lorsque je change mon type d'entrée en texte, cela fonctionne comme prévu.
Karol
8
Cela a résolu un problème majeur lors de l'utilisation des plugins de calendrier jquery. En piratant le plugin pour déclencher ('input') après input.val (), l'événement ng-change se déclenche après que l'utilisateur sélectionne la date du calendrier. Merci!
Drone Brain
3
Fonctionne parfaitement sur les entrées cachées: element.triggerHandler ("change");
falko
2
Cela ne fonctionne pas pour moi. $ ("# Distance"). Val (données); $ ("# Distance"). Trigger ('input');
Stas Svishov
10
Avec Angular 1.4.3, l' inputévénement ne fonctionne pas sur IE, y compris IE11. inputL'événement ne fonctionne que quand $sniff.hasEvent('input')est vrai mais $sniff.hasEvent('input')est faux même sur IE11! Nous pouvons utiliser l' changeévénement à la place.
Shuhei Kagawa
61

J'espère que cela est utile pour quelqu'un.

Je n'ai pas pu récupérer l' jQuery('#myInputElement').trigger('input')événement dans mon application angulaire.

J'ai cependant pu angular.element(jQuery('#myInputElement')).triggerHandler('input')être pris en charge.

Ginman
la source
2
Veuillez expliquer la solution, ne vous contentez pas d'écrire du code. je deviens angulaire n'est pas $ n'est pas une fonction
Thakhani Tharage
1
$ représente jQuery ici. Il est marqué dans la question.
ginman
Cela a fonctionné pour moi aussi alors que $ ('myInputElement'). Trigger ('input') était irrégulier. Cela semblait fonctionner au hasard parfois mais pas d'autres et je n'ai jamais pu établir de modèle. Cette solution fonctionne à chaque fois.
BryanP
2
Le mien est travaillé avec le préfixe #:angular.element($('#myInputElement')).triggerHandler('input')
Ebru Yener
Ce triggerHandler a fonctionné pour moi aussi, je traitais de textarea pas d'entrée. Merci
Mounir
25

La réponse acceptée qui déclenchait un inputévénement avec jQuery ne fonctionnait pas pour moi. La création d'un événement et l'envoi avec JavaScript natif ont fait l'affaire.

$("input")[0].dispatchEvent(new Event("input", { bubbles: true }));
Martins Balodis
la source
C'était aussi la solution pour moi. En utilisant Angular 1.1.5, jQuery 2.2.4 et Jasmine 2.5.3. Très, TRÈS bizarre qu'aucun autre mécanisme de déclenchement ne fonctionne.
Lars-Erik
Je cherchais la méthode native JS pour déclencher la validation de formulaire lors de la définition d'une entrée dans un script et cela a fonctionné pour moi, merci.
disperser
Fonctionne même avec Angular 5, super!
Igor
Je vous remercie. Particulièrement utile dans webview.evaluatejavascript
Berry Wing
22

Je ne pense pas que jQuery soit nécessaire ici.

Vous pouvez utiliser $ watch et ng-click à la place

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

Dans votre contrôleur:

$scope.$watch('foo', function(newValue, oldValue) {
  console.log(newValue);
  console.log(oldValue);
});
Utopik
la source
1
Merci Utopik, vous avez raison, je ne devrais pas mélanger jquery avec angular quand il y a un moyen de faire le travail en utilisant angular.
Michal J.
17

Vous devez utiliser le code suivant pour mettre à jour la portée du modèle d'entrée spécifique comme suit

$('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;
    });
});
jayM
la source
1
var scope = angular.element($("select")).scope(); scope.$apply(function(){ scope.selectValue = newVal; }); cette portion m'a beaucoup aidé. Mon problème était de mettre à jour un modèle angulaire après un appel jquery ajax dans la fonction success. C'était lent avec $ http car il y avait un écouteur d'événement de changement en double pour l'élément qui faisait autre chose, alors je l'ai fusionné dans jQuery.
Emmanuel Mahuni
1
L'utilisation de element.scope () est une mauvaise idée, car elle ne fonctionne qu'avec la journalisation activée. Si vous êtes sur un site de production, vous devez désactiver la journalisation de votre configuration avec $compileProvider.debugInfoEnabled(false);ou $logProvider.debugEnabled(false);Voir code.angularjs.org/1.4.0/docs/guide/production pour plus d'informations.
RWAM
J'ai essayé cette méthode pour la variable cachée et cela a fonctionné. 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 cachée est définie comme suit: <input id = "li_cuboid_id" type = hidden ng-model = "cuboidId">
Rahul Varadkar
7

J'ai apporté des modifications uniquement à l'initialisation du contrôleur en ajoutant un écouteur sur le bouton d'action:

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

D'autres solutions n'ont pas fonctionné dans mon cas.

Ebru Yener
la source
4

Je sais qu'il est un peu tard pour répondre ici, mais je pourrais peut-être en sauver une fois.

J'ai été confronté au même problème. Un modèle ne se remplira pas une fois que vous aurez mis à jour la valeur d'entrée de jQuery. J'ai essayé d'utiliser des événements déclencheurs mais aucun résultat.

Voici ce que j'ai fait pour vous sauver la vie.

Déclarez une variable dans votre balise de script en HTML.

Comme:

<script>
 var inputValue="";

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

</script>

Une fois que vous avez fait cela en utilisant le service ci-dessous d'angular.

$ fenêtre

Maintenant, ci-dessous, la fonction getData appelée à partir de la même portée de contrôleur vous donnera la valeur souhaitée.

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

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

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

J'ai écrit ce petit plugin pour jQuery qui fera tous les appels pour .val(value)mettre à jour l'élément angulaire s'il est présent:

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

Insérez simplement ce script après jQuery et angular.js et les val(value)mises à jour devraient maintenant fonctionner correctement .


Version réduite:

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

Exemple:

dav_i
la source
Désolé pour grave déterrer cela, mais une idée de la façon dont cela pourrait / pourrait fonctionner avec la case à cocher ou les sélections puisque ceux-ci utilisent .prop au lieu de .val? Cela fonctionne parfaitement pour les changements d'entrée .val à tout le moins, alors merci!
Austin le
@Austin J'ai peur de ne pas savoir. C'est une question vieille de 5 ans sur deux technologies qui sont largement considérées comme dépassées, en tant que telles, je n'ai pas gardé les connaissances en tête. Bonne chance pour trouver la solution dont vous avez besoin.
dav_i le
2

Si vous utilisez IE, vous devez utiliser: input.trigger ("change");

William
la source
2

ajouter .change()après avoir défini la valeur.

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

n'oubliez pas non plus d'ajouter onchangeou de ng-changetaguer en html

Azeez
la source
0

J'ai fait cela pour pouvoir mettre à jour la valeur de ngModel de l'extérieur avec 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");
anlijudavid
la source