AngularJS: ng-model 绑定在使用 jQuery 更改时不会更新

这是我的 HTML:

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

当我在框中输入时,模型会通过双向绑定机制进行更新。

然而,当我通过 JQuery 执行此操作时, ..。

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

它没有更新模型,为什么?

187957 次浏览

Angular doesn't know about that change. For this you should call $scope.$digest() or make the change inside of $scope.$apply():

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

See this to better understand dirty-checking

UPDATE: Here is an example

I have found that if you don't put the variable directly against the scope it updates more reliably.

Try using some "dateObj.selectedDate" and in the controller add the selectedDate to a dateObj object as so:

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

This worked for me.

Just use;

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

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>


This answer was copied verbatim from my answer to another similar question.

AngularJS pass string, numbers and booleans by value while it passes arrays and objects by reference. So you can create an empty object and make your date a property of that object. In that way angular will detect model changes.

In controller

app.module('yourModule').controller('yourController',function($scope){
$scope.vm={selectedDate:''}
});

In html

<div ng-controller="yourController">
<input id="selectedDueDate" type="text" ng-model="vm.selectedDate" />
</div>

Try this

var selectedDueDateField = document.getElementById("selectedDueDate");
var element = angular.element(selectedDueDateField);
element.val('new value here');
element.triggerHandler('input');

Whatever happens outside the Scope of Angular, Angular will never know that.

Digest cycle put the changes from the model -> controller and then from controller -> model.

If you need to see the latest Model, you need to trigger the digest cycle

But there is a chance of a digest cycle in progress, so we need to check and init the cycle.

Preferably, always perform a safe apply.

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

Just run the following line at the end of your function:

$scope.$apply()

You have to trigger the change event of the input element because ng-model listens to input events and the scope will be updated. However, the regular jQuery trigger didn't work for me. But here is what works like a charm

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

Following didn't work

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

You can read more about creating and dispatching synthetic events.

Just use:

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

instead of:

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