Knockout JS中的数字输入框

时间:2016-09-08 15:07:18

标签: javascript knockout.js

我试图创建一个只接受数字的数字输入框。

我的初始值方法是替换值并将其重新设置为自身。

订阅方法



function vm(){
  var self = this;
  self.num = ko.observable();
  self.num.subscribe(function(newValue){
    var numReg = /^[0-9]$/;
    var nonNumChar = /[^0-9]/g;
    if(!numReg.test(newValue)){
      self.num(newValue.toString().replace(nonNumChar, ''));
    }
  })
}

ko.applyBindings(new vm())

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input type="text" data-bind="textInput: num" />
&#13;
&#13;
&#13;

现在这种方法有效,但会添加另一个订阅事件周期,所以我尝试使用自定义绑定,以便我只能返回更新的值。新的,我尝试了一些但不知道该怎么做。以下是我的尝试,但它不起作用。它甚至没有更新observable。

自定义绑定尝试

&#13;
&#13;
ko.bindingHandlers.numeric_value = {
  update: function(element, valueAccessor, allBindingsAccessor) {
    console.log(element, valueAccessor, allBindingsAccessor())
    ko.bindingHandlers.value.update(element, function() {
      var value = ko.utils.unwrapObservable(valueAccessor());
      return value.replace(/[^0-9]/g, '')
    });
  },
};

function vm() {
  this.num = ko.observable(0);
  this.num.subscribe(function(n) {
    console.log(n);
  })
}

ko.applyBindings(new vm())
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div>
  <input type="text" data-bind="number_value: num, valueUpdate:'keyup'">
  <span data-bind="text: num"></span>
</div>
&#13;
&#13;
&#13;

所以我的问题是,我们可以使用自定义绑定来实现这一点吗?它是比订阅它更好的方法吗?

编辑1:

根据@user3297291的回答,ko.extenders看起来更像是我的订阅方式的通用方式。我正在寻找一种方法(如果可能的话,在Knockout中),它会在将值设置为可观察之前清除它。

我参考了以下文章:

注意:在第一个示例中,他们使用jQuery来设置值。我想避免它,只使用淘汰赛

4 个答案:

答案 0 :(得分:6)

我赞成使用extender作为user3297291的aswer。

扩展器是一种灵活的格式化或验证可观察的方式,并且更具可重用性。

这是我对数字扩展器的实现

//Extender

ko.extenders.numeric = function(target, options) {
  //create a writable computed observable to intercept writes to our observable
  var result = ko.pureComputed({
    read: target, //always return the original observables value
    write: function(newValue) {
      var newValueAsNum = options.decimals ? parseFloat(newValue) : parseInt(newValue);
      var valueToWrite = isNaN(newValueAsNum) ? options.defaultValue : newValueAsNum;
      target(valueToWrite);
    }
  }).extend({
    notify: 'always'
  });

  //initialize with current value to make sure it is rounded appropriately
  result(target());

  //return the new computed observable
  return result;
};

//View Model

var vm = {
  Product: ko.observable(),
  Price: ko.observable().extend({
    numeric: {
      decimals: 2,
      defaultValue: undefined
    }
  }),
  Quantity: ko.observable().extend({
    numeric: {
      decimals: 0,
      defaultValue: 0
    }
  })
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

修改

我明白你的观点,正则表达式自定义绑定怎样才能使它更具可重用性?

像这样。

function regExReplace(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {

  var observable = valueAccessor();
  var textToReplace = allBindingsAccessor().textToReplace || '';
  var pattern = allBindingsAccessor().pattern || '';
  var flags = allBindingsAccessor().flags;
  var text = ko.utils.unwrapObservable(valueAccessor());
  if (!text) return;
  var textReplaced = text.replace(new RegExp(pattern, flags), textToReplace);

  observable(textReplaced);
}

ko.bindingHandlers.regExReplace = {
  init: regExReplace,
  update: regExReplace
}


ko.applyBindings({
  name: ko.observable(),
  num: ko.observable()
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>


<input type="text" data-bind="textInput : name, regExReplace:name, pattern:'(^[^a-zA-Z]*)|(\\W)',flags:'g'" placeholder="Enter a valid name" />
<span data-bind="text : name"></span>
<br/>
<input class=" form-control " type="text " data-bind="textInput : num, regExReplace:num, pattern: '[^0-9]',flags: 'g' " placeholder="Enter a number " />
<span data-bind="text : num"></span>

答案 1 :(得分:3)

我认为你可以把问题分成两部分:

  1. 确保用户只能输入数字或
  2. 确保您的viewmodel值是数字而不是字符串。
  3. 如果您只需要第1部分,我建议您使用默认的HTML(5)功能:

    <input type="number" step="1" />
    <input type="text" pattern="\d*" />
    

    如果您想确保用户不能输入任何数字以外的任何内容,并且想要在您的视图模型中使用该值,我也会使用扩展程序。通过扩展observable,您可以在触发任何订阅之前更改其值。

    淘汰赛文档在documentation page上提供了一个很好的例子:

    请注意,当您使用扩展程序时,您不再需要担心patterntype属性;敲门声一旦设定就会立即修改价值。

    &#13;
    &#13;
    ko.extenders.numeric = function(target, precision) {
      //create a writable computed observable to intercept writes to our observable
      var result = ko.pureComputed({
        read: target, //always return the original observables value
        write: function(newValue) {
          var current = target(),
            roundingMultiplier = Math.pow(10, precision),
            newValueAsNum = isNaN(newValue) ? 0 : +newValue,
            valueToWrite = Math.round(newValueAsNum * roundingMultiplier) / roundingMultiplier;
    
          //only write if it changed
          if (valueToWrite !== current) {
            target(valueToWrite);
          } else {
            //if the rounded value is the same, but a different value was written, force a notification for the current field
            if (newValue !== current) {
              target.notifySubscribers(valueToWrite);
            }
          }
        }
      }).extend({
        notify: 'always'
      });
    
      //initialize with current value to make sure it is rounded appropriately
      result(target());
    
      //return the new computed observable
      return result;
    };
    
    var vm = {
      changes: ko.observable(0),
      myNumber: ko.observable(0).extend({
        numeric: 1
      })
    };
    
    vm.myNumber.subscribe(function() {
      vm.changes(vm.changes() + 1);
    });
    
    ko.applyBindings(vm);
    &#13;
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
    
    <input type="text" data-bind="value: myNumber">
    
    <div>2 times <span data-bind="text: myNumber"></span> is <span data-bind="text: myNumber() * 2"></span>.</div>
    <div> Changes: <span data-bind="text: changes"></span></div>
    &#13;
    &#13;
    &#13;

答案 2 :(得分:0)

使用Knockout Validation | LIVE PEN

<input data-bind="value: Number">

ko.validation.init();

function VM() {
    var self = this;
    self.Number = ko.observable().extend({
        required: true,
        pattern: {
            message: 'Invalid number.',
            params: /\d$/
        }
    });
}

ko.applyBindings(window.v=new VM());

答案 3 :(得分:0)

以下是knockout的{​​{1}}绑定的模仿,但具有自定义解析功能。请注意,我知道,这增加了几行重复​​代码,但我猜它值得。

我想过创建我的自定义代码,但重新发明轮子会有很多问题,因此感谢Knockout团队努力并复制它。

Numeric Only - JSFiddle

Characters Only - JSFiddle

我已在以下

中更新了代码
  • updateModel :仅从元素中获取已解析的值。这样可以防止更新错误值。
  • updateView :检查用户是否输入了错误的值。如果是,则替换先前的值,否则将先前的值更新为当前值并继续。

<强>可用性

我试图在此问题之外增加此绑定的范围。我添加了一个特殊的数据属性(textInputdata-pattern)来创建正则表达式,因此会进行解析。