4

I would like to ask you is it possible to format dynamically input value?
So when I'm entering some value it will dynamically add commas to format currency.
For example I'm writing 1234 and it will be formatted to 1'234 dynamically.
If I delete one number it will be changed to 123.
I'm thinking about some custom binding. But is it possible to add a rule for every change?

Cheer

Kosmonaft
  • 1,286
  • 2
  • 17
  • 30

2 Answers2

6

You can use bindinghandler for this and create a custom binding:

ko.bindingHandlers.numeric = {
    update: function(element, valueAccessor, allBindingsAccessor) {
       var value = ko.utils.unwrapObservable(valueAccessor());
       var positions= ko.utils.unwrapObservable(allBindingsAccessor().positions) || ko.bindingHandlers.numeric.defaultPositions;
       var formattedValue = parseFloat(value).toFixed(positions); 
       var finalFormatted = ko.bindingHandlers.numeric.withCommas(formattedValue);  

        ko.bindingHandlers.text.update(element, function() { return finalFormatted; });
    },
    defaultPositions: 2,
    withCommas: function(original){
       original+= '';
        x = original.split('.');
        x1 = x[0];
        x2 = x.length > 1 ? ',' + x[1] : '';
        var rgx = /(\d+)(\d{3})/;
        while (rgx.test(x1)) {
            x1 = x1.replace(rgx, '$1' + '.' + '$2');
        }
        return x1 + x2;      
    } 
};

Then if you bind your value to the element, you just use this binding instead of the normal text binding:

<span data-bind="numeric: myNumericObservable"></span>

The custom binding handler above formats the value to the German value: 1.202,22 But you can easily change it by replacing the '.' and the ','.

By the way, here is a solution for numeric value bindings in input-tags:

ko.bindingHandlers.numericValue = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        ko.utils.registerEventHandler(element, 'change', function (event) {
            var observable = valueAccessor();
            var positions= ko.utils.unwrapObservable(allBindingsAccessor().positions) || ko.bindingHandlers.numericValue.defaultPositions;      

            if(ko.utils.unwrapObservable(allBindingsAccessor().positions)==0){
                positions=0;
            }

            if(isNaN(parseFloat($(element).val())))
                observable(0);
            else {
                if(!ko.utils.unwrapObservable(allBindingsAccessor().noDecimalPoints))
                    observable(parseFloat($(element).val().replace(".","").replace(",",".")).toFixed(positions).replace(",","."));
                else 
                    observable(parseFloat($(element).val().replace(".","").replace(",",".")).toFixed(positions));
            }
        });
    },
    update: function(element, valueAccessor, allBindingsAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        if(value!=null) {
            var positions= ko.utils.unwrapObservable(allBindingsAccessor().positions) || ko.bindingHandlers.numericValue.defaultPositions;      
            if(ko.utils.unwrapObservable(allBindingsAccessor().positions)==0){
                positions=0;
            }
            var formattedValue = parseFloat(value).toFixed(positions); 
            var finalFormatted  = formattedValue;
            if(!ko.utils.unwrapObservable(allBindingsAccessor().noDecimalPoints))
                finalFormatted = ko.bindingHandlers.numericValue.withCommas(formattedValue); 
            ko.bindingHandlers.value.update(element, function() { return finalFormatted; });
        }
    },
    defaultPositions: 2,
    noDecimalPoints:false,
    withCommas: function(original){
       original+= '';
        x = original.split('.');
        x1 = x[0];
        x2 = x.length > 1 ? ',' + x[1] : '';
        var rgx = /(\d+)(\d{3})/;
        while (rgx.test(x1)) {
            x1 = x1.replace(rgx, '$1' + '.' + '$2');
        }
        return x1 + x2;      
    } 
};

You can use this as follows:

<input tabindex="10" data-bind="numericValue: myNumericObservable">

You can also configuarte it:

<input data-bind="numericValue: myNumericObservable, positions: 0, noDecimalPoints: true">
Jan Hommes
  • 5,122
  • 4
  • 33
  • 45
  • Thank you. Unfortunately it's not what I'm looking for. I prepare a [JSFiddler](http://www.jsfiddle.net/e3pkqLww/1/) which show what I want to do. I use a ko.pureComputed but I need a bindinghandler. Deleting is not working. Any idea How can I write a binding with same functionality(it's updating my input value) I don't want to use computed because I will have many of input like this. – Kosmonaft Sep 19 '14 at 09:23
  • I already prepare working [JSFiddle](http://jsfiddle.net/687zv5u0/) but with ko.pureComputed() . Any idea how can change it to bindingHandlers? – Kosmonaft Sep 22 '14 at 07:48
1

I already write what I need :)

Here is my working JSFiddle

And my code:

function TestViewModel() {
  var self = this;
  self.myCurrency = ko.observable();
}

ko.bindingHandlers.currencyFormat = {
  init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
    ko.utils.registerEventHandler(element, 'keyup', function (event) {
        var observable = valueAccessor();
        observable(formatInput(element.value));
        observable.notifySubscribers(5);
    });
  },
  update: function (element, valueAccessor, allBindingsAccessor) {
    var value = ko.utils.unwrapObservable(valueAccessor());
    $(element).val(value);
  }
};

function formatInput(value) {
  value += '';

  value = value.replace(/,/g, '');
  var rgx = /(\d+)(\d{3})/;
  while (rgx.test(value)) {
    value = value.replace(rgx, '$1' + ',' + '$2');
  }

  return value;
}

$(document).ready(function () {
  ko.applyBindings(new TestViewModel());
});

Enjoy it

house9
  • 20,359
  • 8
  • 55
  • 61
Kosmonaft
  • 1,286
  • 2
  • 17
  • 30