25

我有一些自定义验证代码,其中包括一个 $formatter。(为了正确起见,我以便士存储货币,但以磅.便士显示。)

如果用户在输入中键入“10”(这是一个有效值),则在他们移动到下一个字段后输入仍然显示“10”。

我希望它显示 10.00 以保持一致性。

如果模型将值更改为 1000,那么格式化程序将使字段显示为“10.00”。

我希望格式化程序在 field.blur() 上运行(只要输入有效)。

我的问题是,如果我将模型值从 10 更改为 10,则没有任何变化是可以理解的,因此不会重新渲染该字段。

代码:

var CURRENCY_REGEXP = /^\-?\d+(\.?\d?\d?)?$/;
app.directive('currency', function() {
  return {
    require: 'ngModel',
    link: function(scope, elm, attrs, ctrl) {
      ctrl.$parsers.unshift(function(viewValue) {
        if (CURRENCY_REGEXP.test(viewValue)) {
          // it is valid
          ctrl.$setValidity('currency', true);
          console.log("valid");
          return viewValue * 100;
        } else if (viewValue === '') {
          return 0;
        } else {
          // it is invalid, return undefined (no model update)
          ctrl.$setValidity('currency', false);
          console.log("invalid");
          return undefined;
        }
      });
      ctrl.$formatters.push(function(modelValue) {
         if (modelValue === 0) { // we're using integer pence, so this is safe
             return '';
         }
         return (modelValue / 100).toFixed(2); 
      });
    }
  };
});

PS 这与 Angular 的内置“货币”无关。


更新:根据安迪的回答,我添加了一个“renderOnBlur”指令。它被调用,但调用渲染方法不会重新渲染输入。即“10”保持为“10”,而不是根据需要更改为“10.00”。

(当这些字段中的模型值发生变化时,它们会以 2 位小数正确呈现。)

Andy 提到的页面http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController说你必须$render自己实现。这看起来很奇怪,因为当模型值更改时,输入已经正确呈现。

app.directive('renderOnBlur', function() {
    return {
        require: 'ngModel',
        restrict: 'A',
        link: function(scope, elm, attrs, ctrl) {
            elm.bind('blur', function() {
                console.log('rendering ctrl', ctrl);
                ctrl.$render();
            });
        }
    };  
});

PS我不知道是什么restrict: 'A',- 这是最糟糕的真正的货物崇拜节目。似乎有require: 'ngModel',必要填充ctrl参数。


受@Dan Doyen 的回答启发,我将其改写为:

app.directive('renderOnBlur', function() {
    return {
        require: 'ngModel',
        restrict: 'A',
        link: function(scope, elm, attrs, ctrl) {
            elm.bind('blur', function() {
                var viewValue = ctrl.$modelValue;
                for (var i in ctrl.$formatters) {
                    viewValue = ctrl.$formatters[i](viewValue);
                }
                ctrl.$viewValue = viewValue;
                ctrl.$render();
            });
        }
    };  
});

这样做的好处是对于任何 $formatter 都是通用的,而不是像 Dan 的回答那样重复格式化程序代码。

4

5 回答 5

15

但是,您的控制器的 $modelValue 正在正确更新,但是由于模糊事件发生在角度之外,因此您的 $viewValue 似乎不是。这个怎么样?

 elm.bind('blur', function() {
       ctrl.$viewValue = (ctrl.$modelValue / 100).toFixed(2);
       ctrl.$render();
 });
于 2012-07-11T17:47:19.940 回答
3

一点改进:如果值无效,请不要重新格式化(在我的情况下,无效文本刚刚被模糊清除,我认为这对可用性不利)。

此外,就像 Dark Falcon 所说:格式化程序应该向后迭代。

最后,不要使用 for-in 遍历数组,至少不要在没有检查 hasOwnProperty() 的情况下进行(对我来说,代码崩溃了,因为它将 Array.find() 视为格式化程序)。

// Reformat text on blur
elements.bind('blur', function() {
    if(!ngModel.$valid) {
        return;
    }
    var viewValue = ngModel.$modelValue;
    var formatters = ngModel.$formatters;
    for (var i = formatters.length - 1; i >= 0; --i) {
        viewValue = formatters[i](viewValue);
    }
    ngModel.$viewValue = viewValue;
    ngModel.$render();
});
于 2015-07-24T08:41:35.830 回答
2

另一种实现是触发 Angular 的格式化程序。Angular 1.5 实现监视 $modelValue 的变化,然后触发 $formatters。要手动执行此操作,可以执行此操作

function triggerFormattersAndRender(ngModel, scope) {
  /* Triggers angulars formatters, which watches for $modelValue changes */
  var originalModelValue = ngModel.$modelValue;
  if (originalModelValue === null) return;

  ngModel.$modelValue = null;
  scope.$digest();
  ngModel.$modelValue = originalModelValue;
  scope.$digest();
}

然后在指令中

function link(scope, elem, attrs, ngModel) {

    elem.bind('blur', function() {
        triggerFormattersAndRender(ngModel, scope);
    });

    // when we get focus, display full precision
    elem.bind('focus', function() {
      if (ngModel.$modelValue) {
        ngModel.$setViewValue(ngModel.$modelValue.toString());
        ngModel.$render();
      }
    })

}
于 2016-05-19T00:40:06.413 回答
0

尝试在模糊上使用 ctrl.$render。

elm.bind('blur', function() { ctrl.$render() });

在http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController中查看。

于 2012-07-08T15:19:34.113 回答
-1

10.00 === 10真的

a=10.00

console.log(a)10

.00 在 javascript 上没有任何意义,因此您的 10.00 正在变为 10

我建议将值设为字符串,以便您可以创建所需的格式

于 2012-07-08T17:33:30.077 回答