Masking numeric values ​​using autoNumeric and Knockout

    In general, I faced a task - to rewrite one of the controls, built on a repeater and make it easier, more responsive for the client. He decided to use knockout. Inside, DevExpress text fields were used to display digital data, they are very convenient and served properly, but the question arose, but what about replacing with ordinary text fields, I can add a mask.



    Start



    In order to solve this problem, I began to look for what normal jquery plugins or libraries are that will allow you to quickly and easily solve the problem. In the end, I found two libraries:



    Analysis



    Looking at the capabilities of the libraries, I was able to say for sure that I would use autoNumeric , since it has the ability not to show the input mask, but to process the input value on the fly. Its behavior is similar to the behavior of the DevExpress text field. Unfortunately, the jquery.maskedinput plugin did not find such a possibility. There is only the possibility of entering data using a strict mask, which appears in the text box, informing the user about the upcoming input format. For my case, such processing is not suitable.

    Application



    Well, with the choice of data masking library in a text field, I decided, but here's how I will tie the mask for the fields. At first there was an idea, to set for all necessary fields that use a single input format, a specific class, and then use jquery to feed the autoNumeric library . But it seemed to me that this solution would not be very convenient.
    Then I thought, why not use the power of knockout! Since knockout allows you to implement custom handlers for data binding, I decided to create just such a handler and specify it in the attribute data-bind="".

    A bit about autoNumeric


    I give the value of some useful methods used by me in solving the problem:

    • Method autoNumeric()- allows you to activate auto-masking on the specified element, using selector, for example, like this:$('.autonum').autoNumeric();
    • Method autoNumericGet();- allows you to get a value that can be used in arithmetic operations (i.e. the value freed from the separators, decorating the input data). For example:$(selector).autoNumericGet();
    • Method autoNumericSet(value);- processes the value value, decorating it with the necessary separators, for the beauty of the output of numbers. For example$(selector).autoNumericSet(value);


    I give the value of the options passed to the method autoNumeric()as a parameter:

    • aSep - indicates which separator will be used for hundreds
    • aDec - indicates which separator will be used for the fractional part
    • mDec - indicates how many digits will be indicated after the fractional separator


    An example of the transfer, the options I use, to configure autoNumeric:

    $('.autonum').autoNumeric({ aSep: ',', aDec: '.', mDec: 0 });
    


    * This means that autoNumeric will use ',' for the delimiter of hundreds of integer parts and '.' as a separator of the fractional part. And also the number of digits after the fractional separator will be equal to 0 (i.e. integers will be displayed).

    Implementation using knockout


    Implementing a custom data binding handler for autoNumeric


     ko.bindingHandlers.numberMaskedValue = {
            init: function(element, valueAccessor, allBindingsAccessor) {
                //попытка получения опций, если они не указаны, 
                //то устанавливаются опции по умолчанию
                var options = allBindingsAccessor().autoNumericOptions || {
                    aSep: ',',
                    aDec: '.',
                    mDec: 0
                };
                //привязка html элемента и применение опций для маскирования
                $(element).autoNumeric(options);
                //подпись на событие 'focusout' элемента, 
                //при котором будет производиться обновление observable свойства 
                ko.utils.registerEventHandler(element, 'focusout', function() {
                    var observable = valueAccessor();
                    //Вызов метода получения значения пригодного для арифметических операций
                    value = $(element).autoNumericGet();
                    observable(isNaN(value) ? 0 : value);
                });
            },
            update: function(element, valueAccessor) {
                var value = ko.utils.unwrapObservable(valueAccessor());
                //установка значения для отображения, в декорированном виде, пользователю
                $(element).autoNumericSet(value);
            }
        };
    


    The above implementation allows us to use this handler in this way:



    or with options:



    In this example handler, an update of the value will occur on the 'focusout' event . For the user, the entered value will look beautiful, but in a real observable property, it will remain suitable for arithmetic processing. Thus, I can immediately solve two problems: binding the necessary (transfer) control to autoNumeric, and passing (updating) the value in the observable property.

    Full example code
    Html




    Javascript


    $(function() {
        ko.bindingHandlers.numberMaskedValue = {
            init: function(element, valueAccessor, allBindingsAccessor) {
                var options = allBindingsAccessor().autoNumericOptions || {
                    aSep: ',',
                    aDec: '.',
                    mDec: 0
                };
                $(element).autoNumeric(options);
                ko.utils.registerEventHandler(element, 'focusout', function() {
                    var observable = valueAccessor();
                    value = $(element).autoNumericGet();
                    observable(isNaN(value) ? 0 : value);
                });
            },
            update: function(element, valueAccessor) {
                var value = ko.utils.unwrapObservable(valueAccessor());
                $(element).autoNumericSet(value);
            }
        };
        function Human(idv, namev, countOfFriendsv) {
            var id = ko.observable(idv),
                name = ko.observable(namev),
                countOfFriends = ko.observable(countOfFriendsv);
            return {
                id: id,
                name: name,
                countOfFriends: countOfFriends
            }
        }
        function HumansModel() {
            humans = ko.observableArray([new Human(1,'Alex', 1234), new Human(2,'Bob',12457)]);        
        }
        ko.applyBindings(new HumansModel())
    });
    


    Css


    .block-of-data{
        border: solid 1px black;
        margin:10px;
        padding: 5px;
        background-color:#ffffaa;
        -webkit-border-radius: 10px;
        -moz-border-radius: 10px;
        border-radius: 10px;
    }
    .label {
        padding: 1px 4px 2px;
        -webkit-border-radius: 3px;
        -moz-border-radius: 3px;
        border-radius: 3px;
    }
    .label {
        font-size: 10.998px;
        font-weight: bold;
        line-height: 14px;
        color: white;
        text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
        white-space: nowrap;
        vertical-align: baseline;
        background-color: #999;
    }
    .label-info {
        background-color: #3A87AD;
    }
    .label-interest {
        background-color: #ff7722;
    }
    




    References




    Thanks for attention!

    Also popular now: