Javascript 如何防止$setViewValue触发$watch
我已经指示wrapps是一个jQuery插件:Javascript 如何防止$setViewValue触发$watch,javascript,jquery,angularjs,Javascript,Jquery,Angularjs,我已经指示wrapps是一个jQuery插件: angular.module('ngJQueryPlugin', []) .controller('MainCtrl', function ($scope) { $scope.input = {'hexa': 0}; }) .directive('jQueryPlugin', function ($compile, $parse) { return { restrict: 'E', r
angular.module('ngJQueryPlugin', [])
.controller('MainCtrl', function ($scope) {
$scope.input = {'hexa': 0};
})
.directive('jQueryPlugin', function ($compile, $parse) {
return {
restrict: 'E',
require: '?ngModel',
link: function ($scope, $element, $attributes, ngModel) {
if (ngModel === null) return;
$scope.$watch($attributes.ngModel, function (hexaFromModel) {
$element.data('jQueryPlugin').updateJQueryPluginUI(hexaFromModel);
}, true);
$element.jQueryPluginConstructor({}, function (hexaFromjQueryPlugin) {
$scope.$apply(function () {
ngModel.$setViewValue({ hexa: hexaFromjQueryPlugin});
ngModel.$render();
});
});
}
};
});
指令实例化:
<jquery-plugin ng-model="input" />
<input type="text" ng-model="input.hexa"/>
指令中的scope watch跟踪每个模型更新并通知jQueryPlugin
jQueryPlugin构造函数中的第二个参数是回调函数,每次jQueryPlugin UI更新十六进制颜色时都会调用该函数。然后在作用域上设置新值
我的问题是$setViewValue导致调用$watch$查看jQueryPlugin的更新,这是无用的,因为我们最初从jQueryPlugin回调得到通知
我想到的一个解决方案是在作用域上使用标志来知道更新最初是在jQueryPlugin中启动的还是在作用域中启动的。这将阻止递归调用
还有别的办法吗?例如,即使模型已更新,也阻止$setViewValue触发$watch
更新:
我刚刚创造了一个plunker。
如果我在Angular的第一个输入中输入文本,jQuery插件将收到通知:正常行为。 如果我在来自jQuery组件的第二个输入中输入文本,则不应返回通知
$scope.$watch($attributes.ngModel, function (hexaFromModel, oldValue) {
if (hexaFromModel === oldValue) {
return;
}
$element.data('jQueryPlugin').updateJQueryPluginUI(hexaFromModel);
});
请注意,这不会停止调用手表,但如果没有更改,则会停止运行手表逻辑。检测更改是否来自ngModel的一种方法。$setViewValue是与ngModel。$modelValue进行比较。你可以这样做:
$scope.$watch($attributes.ngModel, function (hexaFromModel) {
if (angular.equals(hexaFromModel, ngModel.$modelValue)) {
return;
}
$element.data('jQueryPlugin').updateJQueryPluginUI(hexaFromModel);
}, true);
ngModel.$formatters.push(function (value) {
$element.data('jQueryPlugin').updateJQueryPluginUI(value);
});
或者,您也可以使用$formatters,而不是像这样查看$attributes.ngModel:
$scope.$watch($attributes.ngModel, function (hexaFromModel) {
if (angular.equals(hexaFromModel, ngModel.$modelValue)) {
return;
}
$element.data('jQueryPlugin').updateJQueryPluginUI(hexaFromModel);
}, true);
ngModel.$formatters.push(function (value) {
$element.data('jQueryPlugin').updateJQueryPluginUI(value);
});
希望这有帮助
编辑:事实证明hexaFromModel实际上是一个对象,而不是一个简单的字符串,这使得上述解决方案毫无用处
在这种情况下,如果更新来自插件,我想不出比设置一个标志绕过watch侦听器更好的解决方案了。像这样:
var updatingByPlugin = false;
$scope.$watch($attributes.ngModel, function(newValue, oldValue) {
if (updatingByPlugin) {
return;
}
jQueryPlugin.update(newValue);
}, true);
jQueryPlugin = new jQueryPluginConstructor($element, {}, function(hexFromjQueryPlugin) {
console.log('notified');
updatingByPlugin = true;
$scope.$apply(function () {
ngModel.$setViewValue({
hex: hexFromjQueryPlugin
});
});
updatingByPlugin = false;
});
示例plunker:您应该编写$render函数,而不是调用它:
angular.module('ngJQueryPlugin', [])
.controller('MainCtrl', function ($scope) {
$scope.input = {'hexa': 0};
})
.directive('jQueryPlugin', function ($compile, $parse) {
return {
restrict: 'E',
require: '?ngModel',
link: function ($scope, $element, $attributes, ngModel) {
if (ngModel === null) return;
ngModel.$render = function () {
if(ngModel.$viewValue)
$element.data('jQueryPlugin').updateJQueryPluginUI(ngModel.$viewValue);
}
$element.jQueryPluginConstructor({}, function (hexaFromjQueryPlugin) {
$scope.$apply(function () {
ngModel.$setViewValue({ hexa: hexaFromjQueryPlugin});
});
});
}
};
});
看来您不需要双向绑定。那你为什么要用ng模型呢?我确实想要一个双向绑定。这就是我的目标。问题在于:jQueryPlugin更新=>回调=>$setViewValue=>$watch。我不知道$watch将被调用,在这种情况下,$setViewValue已经执行了绑定。但是我需要在这个场景中调用它:Scope model update=>watch=>jQueryPlugin update;不修改范围。你会如何直接设置它?我现在明白你的意思了,请看下面的答案。如果我们使用angular.equals来比较对象相等,而不是通过$scope来比较引用相等,那么这是不起作用的。$watch$attributes.ngModel,函数hexaFromModel{if hexaFromModel==oldValue//always false},true;请参阅初始问题的更新。我已经创建了一个plunkr。如果我在Angular的第一个输入中输入文本,jQuery插件将收到通知:正常行为。如果我在jQuery组件的第二个输入中输入文本,则不应返回通知。请看我编辑的答案。非常感谢runTarm的帮助和帮助。实际上,我已经想到了第一个问题中描述的标志解决方案。我希望angularJs提供一些内部机制,防止$setViewValue回调$watch