Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knockout bindingHandler for comma separated numbers

I'm building a very number-heavy app in KnockoutJS and I want to have the ability to format large numbers so that they're comma-seperated and nice on the eye (xxx,xxx).

As you'll see from the fiddle below, I do have this working by wrapping the binded value inside of a formatting function with a simple RegEx but the problem with this is that this overwrites the value inside the input and inserts ',' into the underlying value.

The large numbers are used further down the app and so to prevent NaN errors I've had to assign a data attribute to the input value containing the value with no ',' and this is the value that gets stored in sessionStorage.

I feel that I have unncessarily bloated my HTML markup and believe that what I want to achieve is possible with a bindingHandler but my binding handler isn't quite there.

Fiddle: http://jsfiddle.net/36sD9/2

formatLargeNumber = function (number) {
    if (typeof (number) === 'function') {
        return number().toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    }
}

ko.bindingHandlers.largeNumber = {
    init: function(element, valueAccessor) {
        var value = ko.unwrap(valueAccessor());
        var interceptor = ko.computed({
            read: function() {
                return formatLargeNumber(value);
            },
            write: function(newValue) {
                value(reverseFormat(newValue));
            }
        });

        if(element.tagName == 'input' )
            ko.applyBindingsToNode(element, {
                value: interceptor
            });
        else
            ko.applyBindingsToNode(element, {
                text: interceptor
            });
    }
}

Any ideas?

like image 958
leaksterrr Avatar asked Apr 20 '14 09:04

leaksterrr


People also ask

How does KnockoutJS work?

KnockoutJS is basically a library written in JavaScript, based on MVVM pattern that helps developers build rich and responsive websites. The model separates the application's Model (stored data), View (UI) and View Model (JavaScript Representation of model).

Where is Knockout used?

In full-contact fighting sports, including boxing, karate, and kickboxing, the ultimate goal is a match-winning knockout of the opponent. Not all boxing matches end this way, but a knockout is the most exciting (and dangerous) way for a match to end.

What is databind in HTML?

A data binding connects data from a custom element (the host element) to a property or attribute of an element in its local DOM (the child or target element). The host element data can be a property or sub-property represented by a data path, or data generated based on one or more paths.

What is applyBindings in knockout?

applyBindings do, The first parameter says what view model object you want to use with the declarative bindings it activates. Optionally, you can pass a second parameter to define which part of the document you want to search for data-bind attributes. For example, ko.


1 Answers

You have multiple problems with your current approach:

  • element.tagName returns INPUT, etc so you need to take care of the casing when doing the comparing.

  • var value = ko.unwrap(valueAccessor()); you are unwrapping your observable so in your computed you are using its value and not the function itself. So you just need var value = valueAccessor(); and you need to call ko.unwrap in your computed read method.

  • You don't just need to format but you need to "unformat" in the write method, but your formatLargeNumber only do the format direction.

  • You have applied value and your largeNumber on the same input which make the two bindings interfering with each other

  • Don't write the formatting code yourself just use a library which already does this like: http://numeraljs.com/

So here is the corrected version of your binding using numeraljs:

ko.bindingHandlers.largeNumber = {
    init: function(element, valueAccessor) {
        var value = valueAccessor();
        var interceptor = ko.computed({
            read: function() {
                return numeral(ko.unwrap(value)).format('0,0');
            },
            write: function(newValue) {
                value(numeral().unformat(newValue));
                value.valueHasMutated();
            }
        }).extend({notify: 'always'});
        if(element.tagName.toLowerCase() == 'input' )
            ko.applyBindingsToNode(element, {
                value: interceptor
            });
        else
            ko.applyBindingsToNode(element, {
                text: interceptor
            });
    }
}

And use it like this:

<input data-bind="largeNumber: testVal">    

Demo JSFiddle.

like image 65
nemesv Avatar answered Oct 17 '22 09:10

nemesv