Поле ввода номера в 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" />

Ответ 1

Я использую 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>

Ответ 2

Я думаю, вы можете разделить проблему на две части:

  • Убедитесь, что пользователь может вводить только номера, или
  • Убедитесь, что ваше значение viewmodel - это число, а не строка.

Если вам нужна только часть 1, я бы посоветовал использовать функции HTML по умолчанию (5):

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

Если вы хотите, чтобы пользователь не мог ввести ничего, кроме числа, и хотите использовать значение в вашей модели просмотра, я бы использовал расширитель. Расширяя наблюдаемое, вы можете изменить его значение до того, как будут запущены какие-либо подписки.

Документы с нокаутом предоставляют отличный пример на странице :

Обратите внимание, что при использовании расширителя вам больше не нужно беспокоиться об атрибуте pattern или type; нокаут мгновенно изменяет значение сразу после его установки.

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);
<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>

Ответ 3

Использование Проверка нокаута | 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());

Ответ 4

Ниже приведена привязка mimic от knockout textInput, но с пользовательским анализом. Заметьте, я знаю, это добавило несколько дополнительных строк дублированного кода, но я думаю, что это стоит.

Я думал о создании своего пользовательского кода, но изобретать колесо будет много проблем, поэтому он будет оценивать усилия команд Knockout и копировать их.

Только числовой - JSFiddle.

Только персонажи - JSFiddle

Я обновил код в следующих

  • updateModel. Чтобы получить только проанализированное значение из элемента. Это предотвратит обновление некорректного значения.
  • updateView. Чтобы проверить, введен ли пользователь неправильное значение. Если да, замените предыдущее значение, добавьте предыдущее значение как текущее значение и продолжите.

Юзабилити

Я попытался увеличить объем этой привязки за пределами этого вопроса. Я добавил специальные атрибуты данных (data-pattern и data-flag), чтобы создать регулярное выражение, которое будет анализироваться соответствующим образом.