Knockout — невозможно обновить наблюдаемое значение с помощью подписки (из-за проверки расширения)

Я попытался реализовать приведенные ниже предложения, но ни один из них не вызывал требуемой реакции каждый раз при обновлении значения. Это может быть связано с моей реализацией, поскольку я новичок в Knockout.

У меня есть наблюдаемое (количество), которое привязано к TextBoxFor.

@Html.TextBoxFor(m => m.Amount, new { type = "number", data_bind = "value: Amount" })

При вводе значения его необходимо проверить, чтобы убедиться, что оно не превышает 999999. В дополнение к этому, если значение меньше 50, его необходимо установить на 50. Эти проверки необходимо выполнять каждый раз, когда ' Сумма меняется.

var viewModel = {
    Amount: ko.observable().extend({numeric: 0, max: 999999})
};

Я пытался реализовать решения (в ответе на мой предыдущий пост), но не смог заставить их работать.

Лучшее, что я придумал, это создать вычисление следующим образом. Это проверяет входное значение и правильно обновляет его с первой попытки. Последующие изменения значения не вызывают изменения на экране, но, выполняя код, похоже, обновляют значение ViewModel.Amount.

@Html.TextBoxFor(m => m.Amount, new { type = "number", data_bind = "value: amountMin" })


quoteVehicleViewModel.VehicleMileage = ko.observable(0);


viewModel.amountMin = ko.computed({
    read: function () {
        return viewModel.Amount();  
    },
    write: function (value) {
        if (value < 50) {
            viewModel.Amount(50);
        }
    }
}).extend({ numeric: 0, max: 999999 });

** "viewModel.Amount()", введенное в консоли, показывает значение 1000, но значение на экране по-прежнему отображается как введенное значение.

Любая помощь с благодарностью получена

Джон


person JohnT    schedule 21.04.2013    source источник
comment
Большое спасибо за ваш ответ, Джозеф - я видел, как работает ваш JSFiddle. Я попытался реализовать в своем решении, и хотя я вижу, что функция coerceMin запускается при загрузке страницы, она не срабатывает, когда я изменяю значение в текстовом поле. Есть ли еще какой-нибудь ключ (из вашего JSFiddle), который требуется для этого, кроме кода, который вы добавили ниже?   -  person JohnT    schedule 22.04.2013
comment
Есть хитрая строка ko.validation.registerExtenders();, которую я не вставил в фактический фрагмент-расширитель в ответе. Вы включили эту строку в свой код?   -  person Joseph Gabriel    schedule 22.04.2013
comment
Привет. Да, я включил registerExtenders, и код coerceMin срабатывает при загрузке страницы, а не при вводе значений. Спасибо   -  person JohnT    schedule 23.04.2013


Ответы (2)


Насколько мне известно, нокаут-валидация не дает возможности получить доступ к наблюдаемому непосредственно из определения правила. Если бы это было возможно, вы могли бы указать значение прямо в пользовательском правиле проверки.

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

Вот пример расширителя принуждения для правила «min»:

  ko.extenders.coerceMin = function (target, enable) {
      //collect rule values
      var minRule = ko.utils.arrayFirst(target.rules(), function (val) {
          return val.rule === "min"
      });
      if (minRule === null) {
          throw Error("coerceMin extender must be used in conjunction with the 'min' knockout validation rule");
      }
      var minValue = minRule.params;
      //listen for changes and coerce when necessary
      var subscription = null;
      if (enable && !subscription) {
          subscription = target.subscribe(function (newValue) {
              if (!isNaN(newValue)) {
                  var newValueAsNum = parseFloat(+newValue);
                  var valueToWrite = newValueAsNum < minValue ? minValue : newValueAsNum;
                  //only write if it changed
                  if (valueToWrite !== newValue) {
                      target(valueToWrite);
                  }
              }
          });
      } else {
          if (subscription) {
              subscription.dispose();
          }
      }

      //return the original observable
      return target;
  };

Вы можете применить его в своей модели представления следующим образом:

var viewModel = {
    Amount: ko.observable()
              .extend({numeric: 0, min: 50, max: 999999})
              .extend({coerceMin: true});
};

Вот скрипт для демонстрации: http://jsfiddle.net/f2rTR/1/

person Joseph Gabriel    schedule 22.04.2013

Очень похожая проблема здесь: Настройте Knockout applyBindings, чтобы параметры выбора рассматривались как числа

Вы можете сделать вычисленное значение, несмотря на подписку. Это будет выглядеть так:

var viewModel = {
    _amount: ko.observable(100).extend({numeric: 0, max: 999999, reset: true}),
    Amount : ko.computed(function(){
       return _amount < 50 ? 50 : _amount;
   })
};


ko.applyBindings(viewModel);

Изменить

Я также посмотрел на удлинители, которые вы использовали здесь. Вы можете добавить к минимальному значению вашего расширителя и передать ему аргумент вашего наблюдаемого. Очевидно, вам нужно написать некоторый код в методе записи вашего расширителя.

что-то типа:

ko.extenders.numeric = function(target, min) {
...
   write:function(v){
      ...
      if(v<50) v=50;
      ...
      target(v)


   }
...
}
person SiMet    schedule 21.04.2013
comment
Привет SiMet, спасибо за ваш ответ. У меня есть еще пара вопросов.......... В вашем первом примере, используя вычисление вместо подписки, к какому значению вы привязываете свое значение? например, ‹тип ввода=текстовое поле привязка данных=значение: сумма /›. Я пробовал как _amount, так и Amount, но вычисление не сработало. Есть ли способ реализовать решение для расширителей, специфичное для наблюдаемого? Я новичок в KO, но похоже, что ko.extenders.numeric повлияет на все наблюдаемые? Спасибо - person JohnT; 22.04.2013