Angular 带有格式化程序和解析器的指令。不确定为什么会这样

Angular directive with formatters and parsers. Not sure why this is working

我需要 select 以公斤和磅为单​​位的重量。但是我的 modelValue 应该始终包含以千克为单位的值。

所以我创建了以下 HTML。

<div>
   <label>{{'aircraftModal.mtom' | translate}}:</label>
   <label><input type="radio" name="weightRadio" ng-model="AircraftCtrl.weightRepresentation" ng-value="'kg'" >Kilogram</label>
   <label><input type="radio" name="weightRadio" ng-model="AircraftCtrl.weightRepresentation" ng-value="'pound'">Pound</label>
   <input type="number" step="0.01" ng-model="AircraftCtrl.mtom" weight weight-representation="AircraftCtrl.weightRepresentation">
</div>

然后我创建了一个带有属性 weigt-representation 的指令 weight。该指令将 parse/format 我在 view/controller.

中的值
angular.module('app.directives').directive('weight', function () {
return {
    require: 'ngModel',
    restrict: 'A',
    scope: {
        weightRepresentation: "=weightRepresentation"
    },
    link: function(scope, elem, attrs, ngModelController) {
        var conversionPoundsToKilogram = 0.45359237;

         ngModelController.$formatters.unshift(function(valueFromModel) {
             if(!valueFromModel) return valueFromModel;
             if(scope.weightRepresentation === 'pound'){
                valueFromModel = valueFromModel / conversionPoundsToKilogram;
             }
             return parseFloat(valueFromModel).toFixed(2);
         });

         ngModelController.$parsers.push(function(valueFromInput) {
             if(!valueFromInput) return valueFromInput;
             if(scope.weightRepresentation === 'pound'){
                valueFromInput = valueFromInput * conversionPoundsToKilogram;
             }
             return valueFromInput;
         });

         scope.$watch('weightRepresentation', function(newWeight, oldWeight){
             if(ngModelController.$modelValue){
                 console.log("before " + ngModelController.$modelValue);
                 ngModelController.$modelValue = ngModelController.$modelValue - 1;
                 console.log("After" + ngModelController.$modelValue);
             }
         });
    }
};
});

问题是每当我更改单选按钮时,我都需要 re-format the viewValue。所以我应该只重新 运行 格式化程序。我认为格式化程序只有在 modelValue 更改时才会执行。出于这个原因,我添加了 ngModelController.$modelValue = ngModelController.$modelValue - 1;。确实调用了格式化程序,但是 valueFromModel 不包含编辑后的值,而是负 1 之前的值。

老天,它完全符合我的需要,但我不明白为什么它会起作用

此外,modelValue 可以有任意数量的小数位,viewValue 应该固定为 2 个小数位

我的问题: 1. 为什么行为会发生? 2. 这是在不改变实际 modelValue 的情况下重新 运行 格式化程序的正确方法,还是这只是一个肮脏的 hack?

这是一个肮脏的 hack,使用 $setViewValue 来更改 viewValue,因为您实现了 $parsers,当 viewValue 被更改时它会工作正常。

关于您的工作:ngModelController 在他的 ngModel 上有一个手表,因此如果您更改它,它可以刷新视图。

编辑:添加作者接受的我最后一条评论中的示例代码:如果最后一次更改只是更改单位,则使用标志不更改 ngModel:

var unitChanged = false;

scope.$watch('weightRepresentation', function(newWeight, oldWeight){
         if(ngModelController.$modelValue){
             unitChanged = true;
             // compute new viewValue and update it using $setViewValue
             [...]
         }
     });

ngModelController.$parsers.push(function(valueFromInput) {
         if(!valueFromInput) return valueFromInput;
         if(unitChanged){
               unitChanged = false;
               return ngModelControler.$modelValue;
         }
         [...]// normal code
     });

请注意,这 3 个 javascript 指令的顺序不算数,我只是这样排序以提高可读性。