我已经制定了一个包含jQuery插件的指令:
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"/>
指令中的范围监视跟踪每个模型更新并通知jQueryPlugin。
jQueryPlugin构造函数中的第二个参数是每次jQueryPlugin UI更新十六进制颜色时调用的回调函数。然后它在范围上设置新值。
我的问题是$ setViewValue导致$ watch被调用。 $ watch更新了jQueryPlugin,这是没用的,因为我们最初从jQueryPlugin回调中得到通知。
我想到的解决方案是使用范围上的标志来了解更新最初是在jQueryPlugin或范围内启动的。这会阻止递归调用。
还有其他办法吗?例如,即使模型已更新,阻止$ setViewValue启动$ watch?
更新 我刚刚创造了一个plunker。 http://plnkr.co/edit/avumn9MJwdjmxHNtrmSF?p=preview 如果我在第一个输入(来自Angular)中输入文本,则会通知jQuery插件:正常行为。 如果我在jQuery组件的第二个输入中输入文本,则不应该通知它。
答案 0 :(得分:0)
$scope.$watch($attributes.ngModel, function (hexaFromModel, oldValue) {
if (hexaFromModel === oldValue) {
return;
}
$element.data('jQueryPlugin').updateJQueryPluginUI(hexaFromModel);
});
请注意,这不会阻止手表被调用,但如果没有更改,则会停止监视逻辑运行。
答案 1 :(得分:0)
检测来自ngModel.$setViewValue()
的更改的一种方法是与ngModel.$modelValue
进行比较。你可以这样做:
$scope.$watch($attributes.ngModel, function (hexaFromModel) {
if (angular.equals(hexaFromModel, ngModel.$modelValue)) {
return;
}
$element.data('jQueryPlugin').updateJQueryPluginUI(hexaFromModel);
}, true);
或者您可能也可以使用$formatters
而不是像这样观看$attributes.ngModel
:
ngModel.$formatters.push(function (value) {
$element.data('jQueryPlugin').updateJQueryPluginUI(value);
});
希望这有帮助。
编辑:事实证明hexaFromModel
实际上是一个对象,而不是一个简单的字符串,这使得上述解决方案毫无用处。
在这种情况下,如果更新来自插件,我想不出任何解决方案比设置标志绕过监听监听器更好。像这样:
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: http://plnkr.co/edit/HH1lw7Pk31tsyri9h0w7?p=preview
答案 2 :(得分:0)
你应该写一个$ 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});
});
});
}
};
});