在焦点剔除自定义绑定上显示输入的基础值

show underlying value of input on focus knockout custom binding

我已经创建了(请原谅乱七八糟的东西 - 目前只是把它拼凑起来!)下面的 knockout 绑定处理程序

所需的功能是,如果一个字段是数字,那么在显示时它将被格式化为 'X' 个小数位(默认为 2),但基础值是输入的任何值。

一切正常,但我想添加这样的功能,即当输入聚焦时它会显示实际值,但我只是不知道该怎么做。

  1. 用户在输入中输入 1.1121
  2. 当他们离开输入时,它的格式为 1.11
  3. 如果他们返回输入(焦点),则会显示 1.1121 以进行编辑

第 3 点我不知道如何实现,因为目前它显示 1.11 然后覆盖模糊?

谁能给我指出正确的方向 - 基本上我应该在哪里访问焦点的基础值并将显示的文本替换为基础值?

为简洁起见,我删除了一些其他 'decoration' 包装输入的代码,因为它们不相关。

提前致谢。

    ko.bindingHandlers.specialInput = {
    init: function (element, valueAccessor, allBindingsAccessor) {

        var value = valueAccessor();

        var decimals = allBindingsAccessor().decimals || 2;
        var formatString = "";

        var interceptor = ko.computed({
            read: function () {
                if(isNumeric(ko.unwrap(value))){

                    //to do if time - replace this with a function that will accept any number of decimals
                    if(decimals == 0){
            formatString = "0,0";
        }
                    if(decimals == 1){
            formatString = "0,0.0";
        }
                    if(decimals == 2){
            formatString = "0,0.00";
        }
                    if(decimals == 3){
            formatString = "0,0.000";
        }
                    if(decimals == 4){
            formatString = "0,0.0000";
        }
                    if(decimals == 5){
            formatString = "0,0.00000";
        }


                return numeral(ko.unwrap(value)).format(formatString);
                }else{
                    return ko.unwrap(value);
                }
            },
            write: function (newValue) {
                if ($.trim(newValue) == '')
                    value("");
                else
                   if(isNumeric(newValue)){ 
                    value(numeral().unformat(newValue));
                value.valueHasMutated();
                   }else{
                    value(newValue);
                    value.valueHasMutated();
                   }
            }
        }).extend({notify: 'always'});




        if (element.tagName.toLowerCase() == 'input') {
            ko.applyBindingsToNode(element, {
                value: interceptor
            });
        } else {
            ko.applyBindingsToNode(element, {
                text: interceptor
            });
        }


    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {

        var value = ko.unwrap(valueAccessor());

        return ko.bindingHandlers.value.update(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);

    }
};

function isNumeric(n) {
    return !isNaN(parseFloat(n)) && isFinite(n);
}

我不会用 interceptor 计算来实现这个。

我更愿意执行以下操作:

init 中为 focusblur 注册事件处理程序在 init:

  • focus 处理程序上,您必须在输入中显示基础值
  • blur 处理程序上,您必须将数字存储在文本框中,并在输入中显示四舍五入的值

update 中,您必须存储真实值,并将其显示在文本框中。当您更新值时,文本框很可能不会获得焦点,因此您应该安全地显示四舍五入的值。但是,如果你认为在某些情况下它可以被聚焦,你可以做这样的事情来测试它来决定如何显示值:Using jQuery to test if an input has focus

伪代码

ko.bindingHandlers.yourBindingName = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
  var $element = $(element);

  $element.on('focus', function() {
    // Show raw value:
    $element.val(/* raw value */);
  });

  $element.on('blur', function() {
    // Update the observable with the value in the input
    ko.unwrap(valueAccessor())( /* get raw value from input */);
    // Show the rounded value
    $element.val(/* rounded value */);
  });

  // Update will be called after init when the binding is first applied,
  // so you don't have to worry about what it's initially shown
},

update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    // When the value changes, show it in the input
    $.element.val(/* if focused show raw, if not, show roundede*/);
}

};

如果您确定只将它与 input 和可写可观察对象一起使用,则此代码是安全的。如果您对此有疑问,您应该添加许多检查,例如检查元素的种类以使用 jQuery .val().text(),检查绑定表达式 is a writeable observable 是否更新其值等等。

注意:有些事情被多次监督:当元素被销毁时,我们不再需要的对象的处置(例如 'if'、'whit' 或 'template').在这种情况下,我认为你不需要这样做,但是,请看这个:Custom disposal logic 如果你认为你需要销毁一些东西。

在绑定处理程序中使用 jQuery 没有任何问题,但您也可以使用现有的绑定处理程序非常简单地完成此操作。这是一种可以整合到组件中以供重复使用的东西(尽管我在我的示例中没有这样做)。

您只需要为您的值提供一个后备存储,以及计算的 returns 后备存储或格式化的后备存储,具体取决于输入是否具有焦点(使用 hasFocus 绑定) .

vm = (function() {
  var editing = ko.observable(false),
    backingStore = 0,
    value = ko.computed({
      read: function() {
        return editing() ? backingStore : (+backingStore).toFixed(2);
      },
      write: function(newValue) {
        backingStore = newValue;
      }
    });

  return {
    editing: editing,
    value: value
  }

}());

ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input data-bind="hasFocus: editing, value: value" />
<input />