如何为格式化数字的文本框进行淘汰自定义绑定?

时间:2017-02-03 18:38:36

标签: knockout.js

我有一个observable,我绑定到input type="text" HTML元素。 observable保存浮点数。我希望文本框中的值显示为2位小数。因此1将是1.00,1.2将是1.20,依此类推。我创建了一个自定义绑定,我认为它适用于输出格式化的值,但不捕获用户输入:

ko.bindingHandlers.numericValue = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
    },
    update: function (element, valueAccessor, allBindingsAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor()),
            precision = ko.utils.unwrapObservable(allBindingsAccessor().precision) || ko.bindingHandlers.numericValue.defaultPrecision;

        var formattedValue = '';
        if (value) {
            if (typeof value === "string") {
                value = parseFloat(value);
            }
            formattedValue = value.toFixed(precision);
        } else {
            value = 0;
            formattedValue = value.toFixed(precision);
        }
        $(element).val(formattedValue);
    },
    defaultPrecision: 1
};

结合:

<input type="text" maxlength="6" class="bb width-100p" data-bind="numericValue: marketRate, precision: 2" />

在模型上可观察:

    self.marketRate = ko.observable(formatNumber(dc.marketRate, 2)).extend({
        required: { message: 'Required', onlyIf: function() { return self.isSelected(); }},
        min: { params: 0, onlyIf: function () { return self.isSelected(); } },
        max: { params: 999999.99, onlyIf: function() { return self.isSelected(); } },
        pattern: { message: 'Maximum 2 decimal places', params: /^\d*(\.\d{1,2})?$/, onlyIf: function () { return self.isSelected(); } }
    });

function formatNumber(value, places) {
        value = ko.utils.unwrapObservable(value);
        if (value) {
            if (typeof value === "string") {
                value = parseFloat(value);
            }
            return value.toFixed(places);
        }
        value = 0;
        return value.toFixed(places);
    }

我是否需要使用用户输入的值来更新observable?此代码永远不会更新observable。我的猜测是我需要调用Knockout值绑定代码。

2 个答案:

答案 0 :(得分:1)

据我了解,有几种选择。您可以在init函数中设置一个事件绑定,它将在值更改时直接处理更新observable。看起来像这样

init: function (element, valueAccessor, allBindings) {
    var valueObservable = valueAccessor();

    ko.utils.registerEventHandler(element, "change", function () {
      valueObservable($(element).val());
    });
}

另一种选择是通过将您的格式函数转换为可写的计算机来搭载现有的值绑定,然后在init绑定中使用以下命令将“value”绑定分配给计算的值。在这种情况下,您不需要更新功能。

ko.applyBindingAccessorsToNode(element, { value: formattingComputed }, bindingContext);

在您的特定示例中,可能如下所示:

ko.bindingHandlers.numericValue = {
  init: function (element, valueAccessor, allBindingsAccessor, data, bindingContext) {
    var formattingComputed = ko.computed({
        read: function(){
          var value = ko.utils.unwrapObservable(valueAccessor()),
              precision = ko.utils.unwrapObservable(allBindingsAccessor().precision) || ko.bindingHandlers.numericValue.defaultPrecision;

          var formattedValue = '';
          if (value) {
              if (typeof value === "string") {
                  value = parseFloat(value);
              }
              formattedValue = value.toFixed(precision);
          } else {
              value = 0;
              formattedValue = value.toFixed(precision);
          }
          formattedValue = value.toFixed(precision);
          return formattedValue;
        },
        write: function(value){
            ko.expressionRewriting.writeValueToProperty(valueAccessor(), allBindingsAccessor, 'value', value);
        }
    });
    ko.applyBindingAccessorsToNode(element, { value: function(){return formattingComputed;} }, bindingContext);
  }
}

jsFiddle

答案 1 :(得分:1)

根据官方文档,&#34;更新&#34;首次将绑定应用于元素时调用一次回调,并且每当访问的任何可观察对象/计算值发生更改时再次调用回调。

此外,您实际上不必同时提供init和更新回调 - 如果您需要,只需提供一个或另一个。

在下面的代码段中,我添加了一个&#34;值&#34;绑定到&#34; marketRate&#34; VM可观察,因此无论何时更改此可观察量,都会更新&#34;更新&#34;回调被触发。

&#13;
&#13;
ko.bindingHandlers.numericValue = {
    update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        var allBindings = ko.utils.unwrapObservable(allBindings()); 

        if (allBindings.value()) {
            // use informed precision or default value
            var precision = allBindings.numericValue.precision || 1;                    

            // prevent rounding
            var regex = new RegExp('^-?\\d+(?:\.\\d{0,' + precision + '})?');   

            // update observable
            allBindings.value(parseFloat(allBindings.value().match(regex)[0]).toFixed(precision));                     
        }
    }
};

function MyViewModel() {
    // applying some validation rules
    this.marketRate = ko.observable().extend({
        required: true,
        min: 5
     });
}

var vm = new MyViewModel();
ko.applyBindings(vm);
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout-validation/2.0.3/knockout.validation.min.js" type="text/javascript"></script>

<input type="text" data-bind="value: marketRate, numericValue: { precision: 2 }" />
&#13;
&#13;
&#13;

注意&#34; allBindings&#34;这个参数使我们能够访问应用于元素的其他绑定(例如值)以及自定义绑定本身的参数,在这种情况下只是属性&#34; precision&#34;。

要使用默认精度的自定义绑定,您可以使用 numericValue:{} numericValue:true