Update Angular model after setting input value with jQuery
Asked Answered
C

12

164

I have this simple scenario:

Input element which value is changed by jQuery's val() method.

I am trying to update the angular model with the value that jQuery set. I tried to write a simple directive, but it's not doing what I want.

Here's the directive:

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

this is the jQuery part:

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

and html:

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

<button>clickme</button>

Here is the fiddle with my try:
http://jsfiddle.net/U3pVM/743/

Can someone please point me in the right direction?

Crampon answered 14/6, 2013 at 13:39 Comment(7)
Mix AngularJS and jQuery, outside the directives, is often a bad idea. Is jQuery mandatory for what you want to do ?Castiglione
Why don't you set the click event with angular to a function in the controller to change the value of the model?Eringo
@Blackhole, I am using jquery upload plugin that do its work and puts the path to uploaded file into the hidden input. All I need is to have access to that path in angular model. So I thought I'll set it as a value of hidden input and after it is changed update the angular model.Crampon
I do not see Jquery call in your fiddle... Can you check and point where you change value with JQ?Dustproof
Your fiddle links to a Todo sample app, which doesn't relate to this question.Tosha
sorry, I updated the fiddle address.Crampon
Try adding something like this inside your click handler (along with the "value changed" console log): scope.foo = element.val(); scope.$apply(); What you want to do is to propagate the change back into your Angular model. Because this is initiated outside of an Angular callback, the "$apply()" call is what will tell Angular to recognize the change and update its models.Brinna
T
330

ngModel listens for "input" event, so to "fix" your code you'd need to trigger that event after setting the value:

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

For the explanation of this particular behaviour check out this answer that I gave a while ago: "How does AngularJS internally catch events like 'onclick', 'onchange'?"


But unfortunately, this is not the only problem you have. As pointed out with other post comments, your jQuery-centric approach is plain wrong. For more info take a look at this post: How do I “think in AngularJS” if I have a jQuery background?).

Tosha answered 14/6, 2013 at 14:22 Comment(13)
Cool! With one exception: why it doesn't work on hidden inputs? When I switch my input type to text, it works as expected.Satisfied
This solved a major issue when using jquery calendar plugins. By hacking the plugin to trigger('input') after input.val() the ng-change event fires after user selects calendar date. Thanks!Fundament
Works perfect on hidden input: element.triggerHandler("change");Vizcacha
this does not work for me. $("#Distance").val(data); $("#Distance").trigger('input');Mcgregor
@StasSvishov Provide a plunker example, perhaps I can help.Tosha
If using for a checkbox (like <input type="checkbox" ng-model="SomeServerGeneratedID">), you can say $scope["SomeServerGeneratedID"] = false (or true) to control it.Horsefly
Sometimes you just need to trigger 1 function from a jquery library, there is no error in this approach I think, no need to do fancy stuff with definitions & etc.Higher
With Angular 1.4.3, input event doesn't work on IE including IE11. input event works only when $sniff.hasEvent('input') is true but $sniff.hasEvent('input') is false even on IE11! We can use change event instead.Knapsack
Wow quite neat , works for my autocomplete , which had lot of other issues when using directiveWordbook
Really a great solution. Thanks a lot @Stewie. You made my day. :)Milliary
@Satisfied : Alternative you can use style="display:none" instead of type="hidden".Supranatural
thaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaank you. you are a herooooooooooExoskeleton
@ShuheiKagawa THANK YOU SO MUCHDevisable
B
61

Hope this is useful for someone.

I was unable to get the jQuery('#myInputElement').trigger('input') event to be picked up my angular app.

I was however, able to get angular.element(jQuery('#myInputElement')).triggerHandler('input') to be picked up.

Bijugate answered 24/5, 2014 at 23:52 Comment(10)
please explain the solution dont just write code. i get angular is not $ is not a functionJobbery
$ represents jQuery here. It is tagged in the question.Bijugate
This worked for me too while $('myInputElement').trigger('input') was flaky. It seemed to randomly work sometimes but not others and I never could establish a pattern. This solution works every time.Ceremony
Mine is worked with # prefix: angular.element($('#myInputElement')).triggerHandler('input')Scone
This triggerHandler worked for me also, I was dealing with textarea not input. ThanksHamman
That's the only solution that worked for me within a bunch o accepted solutions! thanksNereid
The model is changed, but I can catch event by using ng-change. What is the problem? Here is my code: <input class="textColor" id="color_{{itemId}}" type="text" name='color_{{itemId}}' ng-model="color" ng-change="optToPreviewText()"/>Meuse
This helped me very much to make bootstrap-datepicker work with Angular. I described it here: https://mcmap.net/q/151599/-bootstrap-datepicker-does-not-update-angularjs-model-when-picking-dateRestrain
This worked for me. angular.element($('#server_name_dld')).triggerHandler('input');Wadley
I had to change the event name from 'input' to change: angular.element(jQuery('#myElementId')).triggerHandler('change')Informant
E
30

The accepted answer which was triggering input event with jQuery didn't work for me. Creating an event and dispatching with native JavaScript did the trick.

$("input")[0].dispatchEvent(new Event("input", { bubbles: true }));
Electrotonus answered 11/1, 2017 at 14:11 Comment(4)
This was the solution for me too. Using Angular 1.1.5, jQuery 2.2.4 and Jasmine 2.5.3. Very, VERY weird that no other trigger mechanisms work.Lightman
I was looking for the native JS method for triggering form validation when setting an input in a script and this worked for me, thanks.Dustidustie
Works even with Angular 5, great!Bohner
Thank you. Especially helpful in webview.evaluatejavascriptShiau
N
22

I don't think jQuery is required here.

You can use $watch and ng-click instead

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

In your controller :

$scope.$watch('foo', function(newValue, oldValue) {
  console.log(newValue);
  console.log(oldValue);
});
Nottingham answered 14/6, 2013 at 14:44 Comment(1)
Thank you Utopik, you're right, I shouldn't mix jquery with angular when there is a way how to do the job using angular.Crampon
S
18

You have to use the following code in order to update the scope of the specific input model as follows

$('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;
    });
});
Sentence answered 5/3, 2014 at 15:54 Comment(3)
var scope = angular.element($("select")).scope(); scope.$apply(function(){ scope.selectValue = newVal; }); that portion helped me a lot. My issue was updating an angular model after a jquery ajax call in the success function. It was slow with $http because there was duplicate change event listener for the element that was doing something else so i merged it into jQuery.Tupi
The use of element.scope() is a bad idea, because it works only with logging enabled. If you on a production site you should disable logging on your config with $compileProvider.debugInfoEnabled(false); or $logProvider.debugEnabled(false); See code.angularjs.org/1.4.0/docs/guide/production for more info.Footplate
I tried this method for hidden variable and it worked. 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; }); the hidden variable is defined as: <input id="li_cuboid_id" type=hidden ng-model="cuboidId">Wadley
S
7

I made modifications on only controller initialization by adding listener on action button:

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

Other solutions did not work in my case.

Scone answered 26/8, 2015 at 10:35 Comment(0)
P
4

I know it's a bit late to answer here but maybe I may save some once's day.

I have been dealing with the same problem. A model will not populate once you update the value of input from jQuery. I tried using trigger events but no result.

Here is what I did that may save your day.

Declare a variable within your script tag in HTML.

Like:

<script>
 var inputValue="";

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

</script>

Once you did that by using below service of angular.

$window

Now below getData function called from the same controller scope will give you the value you want.

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

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

$scope.getData = function () {
    console.log("Window value " + $window.inputValue);
}}]);
Pyxis answered 2/3, 2017 at 8:24 Comment(0)
O
3

I've written this little plugin for jQuery which will make all calls to .val(value) update the angular element if present:

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

Just pop this script in after jQuery and angular.js and val(value) updates should now play nice.


Minified version:

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

Example:

// the function
(function($, ng) {
  'use strict';
  
  var $val = $.fn.val;
  
  $.fn.val = function (value) {
    if (!arguments.length) {
      return $val.call(this);
    }
    
    var result = $val.call(this, value);
    
    ng.element(this[0]).triggerHandler('input');
    
    return result;
    
  }
})(window.jQuery, window.angular);

(function(ng){ 
  ng.module('example', [])
    .controller('ExampleController', function($scope) {
      $scope.output = "output";
      
      $scope.change = function() {
        $scope.output = "" + $scope.input;
      }
    });
})(window.angular);

(function($){  
  $(function() {
    var button = $('#button');
  
    if (button.length)
      console.log('hello, button');
    
    button.click(function() {
      var input = $('#input');
      
      var value = parseInt(input.val());
      value = isNaN(value) ? 0 : value;
      
      input.val(value + 1);
    });
  });
})(window.jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div ng-app="example" ng-controller="ExampleController">
  <input type="number" id="input" ng-model="input" ng-change="change()" />
  <span>{{output}}</span>
  <button id="button">+</button>
</div>
Overanxious answered 12/3, 2015 at 17:45 Comment(2)
Sorry to grave dig this up, but any idea how this would/could work with checkbox or selects since those use .prop instead of .val? This is working perfectly for input .val changes at the very least so thank you!Edith
@Edith I'm afraid I don't know. This is a 5 year old question about two technologies that are widely considered outdated, as such I haven't kept the knowledge in my head. Best of luck getting the solution you need though.Overanxious
H
2

If you are using IE, you have to use: input.trigger("change");

Hydranth answered 29/6, 2016 at 0:54 Comment(0)
B
2

add .change() after setting the value.

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

also don't forget to add onchange or ng-change tag in html

Braise answered 20/10, 2016 at 7:4 Comment(0)
B
0

I did this to be able to update the value of ngModel from the outside with 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");
Brandes answered 11/7, 2019 at 23:9 Comment(0)
M
0

Not what OP asked, but for any soul that might be as well writing an userscript that goes through input fields and fills the required details. Nothing (fully) worked for me, but finally managed to get it done this way:

var el = $('#sp_formfield_fw_ip');
el.val("some value");
angular.element(el).triggerHandler('focus');
angular.element(el).triggerHandler('input');
angular.element(el).triggerHandler('change');
angular.element(el).triggerHandler('blur');

Open developer tools, and inspect input field for added events. There I found all of them (in my case): focus, input, change and blur.

Math answered 1/9, 2020 at 7:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.