AngularJS问题:ngModel禁用时updateDecimals指令未触发
解决AngularJS指令在ngModel禁用时不执行的问题
咱们先拆解下原代码里的核心问题:
- 把
ngModel.$render的定义嵌套在$watch回调里,这会导致每次digest循环都重复赋值$render,不仅浪费性能,而且当控件禁用时,Angular不会主动触发这个回调。 - 在$render里调用
ngModel.$setViewValue会触发新一轮的digest循环,很容易陷入无限循环的坑。 - 没有主动监听小数位数的变化,当
updateDecimals绑定的值改变时,无法自动重新格式化显示。
修复后的指令代码
app.directive('updateDecimals', function($parse, $filter) { return { restrict: 'A', require: '?ngModel', link: function(scope, ele, attrs, ngModel) { if (!ngModel) return; // 没有ngModel的话直接退出 // 抽离格式化逻辑,方便复用和维护 var formatValue = function(value) { var decimalDigits = $parse(attrs.updateDecimals)(scope); if (angular.isUndefined(value) || value === null) return ''; var numValue = parseFloat(value, 10); return isNaN(numValue) ? value : $filter('number')(numValue, decimalDigits); }; // 用ngModel的$formatters处理model到view的转换——这是Angular的标准方式,禁用状态下也会执行 ngModel.$formatters.push(function(modelValue) { return formatValue(modelValue); }); // 监听小数位数的变化,一旦改变就强制更新视图 scope.$watch(attrs.updateDecimals, function() { ngModel.$render(); // 哪怕控件禁用,手动触发渲染也能生效 }); // 重写$render方法,直接更新元素值,避免触发不必要的digest循环 ngModel.$render = function() { ele.val(ngModel.$viewValue); }; } }; });
关键修复点说明
- 改用$formatters管道:这是AngularJS处理「模型值转视图显示值」的标准方案,无论控件是否禁用,只要模型值变化,$formatters都会执行,彻底解决禁用状态下不触发的问题。
- 监听小数位数变化:当
updateDecimals绑定的数值改变时,手动调用ngModel.$render(),确保视图立即同步更新。 - 避免循环风险:直接操作元素的value值,不再调用
$setViewValue,从根源上防止无限循环。
额外优化建议
如果需要支持用户输入(视图值转模型值),可以添加$parsers管道,把用户输入的格式化字符串转换为纯数值存储到模型里。
内容的提问来源于stack exchange,提问作者Raghul
相关产品推荐
相关产品推荐

