
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.
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:
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.
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
I give the value of some useful methods used by me in solving the problem:
I give the value of the options passed to the method
An example of the transfer, the options I use, to configure autoNumeric:
* 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).
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.
Thanks for attention!
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:
- Plugin the jquery jquery.maskedinput plugin page jquery.maskedinput
- Library autoNumeric page autoNumeric library
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 valuevalue
, 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
- Full example of using this custom handler on jsFiddle . In the implementation, you can visually see the real value stored in the field and the value displayed in the text field for the user.
Thanks for attention!