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?

7
  • 2
    Mix AngularJS and jQuery, outside the directives, is often a bad idea. Is jQuery mandatory for what you want to do ? Commented Jun 14, 2013 at 13:49
  • Why don't you set the click event with angular to a function in the controller to change the value of the model? Commented Jun 14, 2013 at 13:56
  • @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. Commented Jun 14, 2013 at 14:00
  • I do not see Jquery call in your fiddle... Can you check and point where you change value with JQ? Commented Jun 14, 2013 at 14:06
  • Your fiddle links to a Todo sample app, which doesn't relate to this question. Commented Jun 14, 2013 at 14:12

12 Answers 12

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?).

Sign up to request clarification or add additional context in comments.

13 Comments

Cool! With one exception: why it doesn't work on hidden inputs? When I switch my input type to text, it works as expected.
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!
Works perfect on hidden input: element.triggerHandler("change");
this does not work for me. $("#Distance").val(data); $("#Distance").trigger('input');
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.
|
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.

10 Comments

please explain the solution dont just write code. i get angular is not $ is not a function
$ represents jQuery here. It is tagged in the question.
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.
Mine is worked with # prefix: angular.element($('#myInputElement')).triggerHandler('input')
This triggerHandler worked for me also, I was dealing with textarea not input. Thanks
|
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 }));

4 Comments

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.
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.
Works even with Angular 5, great!
Thank you. Especially helpful in webview.evaluatejavascript
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);
});

1 Comment

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.
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;
    });
});

3 Comments

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

Comments

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);
}}]);

Comments

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>

2 Comments

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!
@Austin 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.
2

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

Comments

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

Comments

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

Comments

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.