Search code examples
asp.net-mvc-3knockout.jsknockout-mvc

Knockout JS and comma as decimal separator


I'm using Knockout MVC im my project (ASP.NET MVC3, Razor). I can't find now to change decimal format. I want to use comma as a decimal separator. When I bind data using non-knockout Razor helper, it renders it correctly (with comma), but when I bind using Knockout binding it renders the number with the dot as comma separator.

How to change the decimal format that is would use comma?


Solution

  • You're looking at the differences between your server and client locale.

    Your .NET code formats numbers with respect to the server locale whereas your JS code formats numbers with respect to the browsers locale.

    Try changing the locale/region within your browser.

    EDIT:

    (I am leaving the above in case it helps anyone else, even though it did not help you)

    The issue is your understanding of the differences between server-side and client-side.

    Razor code is executed on the server and 'translated' into HTML.

    Whereas, the server treats Javascript as text and just part of the HTML document it is sending.

    Javascript is executed on the clients machine (i.e. the browser).

    How the Razor Helper formats the number is based on the locale set on the server. Whereas, the javascript will format the number based on the locale set in the browser.

    To force Knockout/Javascript to format the number how you want regardless of the locale (on the client-side), you can write a custom-binding using the following method at it's core:

    function formatWithComma(x, precision, seperator) {
        var options = {
            precision: precision || 2,
            seperator: seperator || ','
        }
        var formatted = x.toFixed( options.precision );
        var regex = new RegExp('^(\\d+)[^\\d](\\d{' + options.precision + '})$');
        formatted = formatted.replace(regex, '$1' + options.seperator + '$2');
        return formatted;
    }
    

    So your binding will look something like this:

    ko.bindingHandlers.commaDecimalFormatter = {
        init: function(element, valueAccessor) {
    
            var observable = valueAccessor();
    
            var interceptor = ko.computed(function() {
                return formatWithComma( observable() );
            }
    
            ko.applyBindingsToNode( element , { value: interceptor } );
    
        }
    }
    

    And then in your Razor view:

    @ko.Bind.Custom("commaDecimalFormatter ", m => m.MyCustom)
    

    (Please note, I've never used KnockoutMVC and so this last line is straight from the documentation with the binding name changed - it is untested.

    Also, I have used a ko.computed and so this binding is read-only - using it on an <input> element will be pointless. It's better used on a <span>. You can make this a two-way binding implementing the reverse:

    ko.computed( {
        read: function() {
            /* method as above */
        },
        write: function(newValue) {
            /* implement reverse */
            observable( newValue );
        }
    }
    

    EDIT 2

    Hope this fiddle makes it clearer