2017-02-03 1 views
0

У меня есть наблюдаемое, что я привязываюсь к элементу HTML input type="text". Наблюдаемое содержит числа с плавающей запятой. Я хочу, чтобы значение в текстовом поле отображалось до двух знаков после запятой. Таким образом, 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 
}; 

Binding:

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

Observable на модели:

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

Нужно ли что-нибудь обновить наблюдаемое значением, введенным пользователем? Этот код никогда не обновляет наблюдаемый. Я предполагаю, что мне нужно вызвать код привязки значения Knockout.

ответ

1

Как я понимаю, есть несколько вариантов. Вы можете настроить привязку событий в своей функции init, которая будет непосредственно обрабатывать обновление наблюдаемого при изменении значения. Это выглядит что-то вроде этого

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

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

Другого варианта заключается в контрейлерное на связывании, превратив функцию форматирования в перезаписываемый вычислено, а затем, используя следующую в инициализации привязки присвоить «значение» привязку к существующему значению который вычисляется. В этом случае вам не нужна функция обновления.

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

+0

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

+0

В последней строке попробуйте обернуть вычисление в функции.ko.applyBindingAccessorsToNode (элемент, {значение: функция() {return formattingComputed;}}, bindingContext); Я обновил свой ответ для соответствия. –

+0

Как выглядит ваша настройка проверки? Это использование плагина проверки нокаута? –

1

За официальными документы, то «обновление» обратный вызов вызывается один раз, когда привязка первый примененный к элемент и снова всякий раз, когда изменяются любые наблюдаемые/вычисленные вычисления.

Кроме того, на самом деле вам не нужно предоставлять обратные вызовы init и update - вы можете просто предоставить один или другой, если это все, что вам нужно.

В приведенном ниже фрагменте я добавил привязку «значение» к наблюдаемой VM «marketRate», поэтому, когда это наблюдаемое изменяется, срабатывает обратный вызов «обновления».

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);
<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 }" />

Обратите внимание на «allBindings» аргумент, который дает нам доступ к другим привязок, применяемых к элементу (например, значение) вместе с параметрами из пользовательского связывания себя, в этом случае просто свойство «точность».

Для использования пользовательских связывания с точностью по умолчанию можно использовать numericValue: {} или numericValue: истинный

+0

Хорошая идея и работает очень хорошо. У меня есть правило валидации регулярного выражения для наблюдаемого, которое ограничивает десятичные числа до 2. При этом решении, если введено 1.599, вместо запуска правила проверки, значение округляется до 1.60. Я бы хотел, чтобы правила проверки работали с форматированием. Возможное? – DaveB

+0

Да, изменение вашего наблюдаемого значения в пользовательской привязке должно инициировать правила проверки - просто добавили некоторые простые правила проверки, чтобы продемонстрировать, что –

+0

Введите ваш пример 1.5999, и он будет отформатирован до 1.60 с сообщением максимум 2 десятичных знака, если оно 2 десятичных знака. Я думаю, что это будет запутать как пользователь. Я думаю, что если введенное значение недопустимо, если не следует форматировать. Похоже, должна быть проверка isValid? – DaveB

Смежные вопросы