В общем передо мной встала задач переписать один из контролов построенный на репиттере, сделать его легче и отзывчивее для клиента. При этом решил использовать knockout. Только вот внутри для отображения цифровых данных использовались DevExpress'овские текстовые поля, они очень удобны и служили исправно, но тут встал вопрос, а как же при замене на обычные текстовые поля, я смогу добавить маску.
Начало
Для того чтобы решить данную задачу я стал искать, какие же есть нормальные плагины jquery или библиотеки, при помощи которых было бы как можно проще реализовать задачу. В итоге я нашел две библиотеки:
- Плагин jquery jquery.maskedinput Страница плагина jquery.maskedinput
- Библиотека autoNumeric Страница библиотеки autoNumeric
Анализ
Посмотрев на возможности библиотек, я точно смог сказать, что буду использовать autoNumeric, так как у него есть возможность не показывать маску ввода, а обрабатывать вводимое значение на лету. Его поведение схоже с поведением текстового поля DevExpress. У плагина jquery.maskedinput, к сожалению, такой возможности не обнаружил. Есть только возможность ввода данных по строгой маске, которая при этом появляется в текстовом поле, информируя пользователя предстоящем формате ввода и для моего случая такая обработка не подходит.
Применение
Ну что ж, с выбором библиотеки для маскирования данных вводимых в текстовое поле, я определился, но вот тут встала задача как же я буду подвязывать маску для полей. Сначала была мысль, указать для всех необходимых полей одного формата, определенный класс, а затем при помощи jquery скормить библиотеке autoNumeric. Но мне показалось это решение не очень удобным.
Тогда я подумал, а почему бы не воспользоваться мощью knockout! Так как knockout позволяет реализовывать кастомные обработчики связывания данных, то я решил создать именно такой обработчик и указывать его в атрибуте data-bind=""
.
Немного об autoNumeric
Привожу значение некоторых методов, испльзуемых мной:
- Метод
autoNumeric()
— позволяет активировать авто маскирование на указанном элементе, посредством selector, к примеру вот так:$('.autonum').autoNumeric();
- Метод
autoNumericGet();
— позволяет получить значение, которое может использоваться в арифметических операциях (т.е. значение освобожденное от разделителей, которые используются лишь для красивого вывода данных). К примеру:$(selector).autoNumericGet();
- Метод
autoNumericSet(value);
— обрабатывает значениеvalue
, декорируя его необходимыми разделителями, для красоты вывода чисел. К примеру$(selector).autoNumericSet(value);
Привожу значение опций, передаваемых в метод autoNumeric()
в качестве параметра:
- aSep — указывает, какой разделитель будет использоваться для сотен
- aDec — указывает, какой разделитель будет использоваться для дробной части
- mDec — указывает, сколько цифр будет указываться после разделителя дробной части
Полный формат передачи указанных выше опций, указан ниже:
$('.autonum').autoNumeric({ aSep: ',', aDec: '.', mDec: 0 });
* Это означает что autoNumeric будет использовать ',' для разделителя сотен целой части и '.' в качестве разделителя дробной части. А так же кол-во цифр после дробного разделителя 0 (т.е. будут отображаться целые числа).
Реализация с использованием knockout
Реализация кастомного обработчика связывания данных для autoNumeric
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);
}
};
Выше приведенная реализация позволяет вот таким образом задействовать данный обработчик:
<input type="text" data-bind="numberMaskedValue: countOfFriend"/>
или с указанием опций:
<input type="text" data-bind="numberMaskedValue: countOfFriend, autoNumericOptions:{aSep: ',', aDec: '.', mDec: 3}"/>
В данном примере обработчика, обновление значения будет происходит при событии 'focusout'. Что самое главное, при этом для пользователя, значение будет выглядеть красиво, а в реальном observable свойстве, оно будет оставаться пригодным для арифметической обработки. Что очень радует, я могу сразу фактически решить две проблемы: привязка нужного (передача) контрола autoNumeric, и передачу значения в observable свойство.
Html
<div data-bind="foreach: humans">
<div class="block-of-data">
<div>
<label class="label label-info">id</label>
<span data-bind="text: id"></span>
</div>
<div>
<label class="label label-info">Name</label>
<span data-bind="text: name"></span>
</div>
<div>
<label class="label label-info">Count of friends</label>
<input type="text" data-bind="numberMaskedValue: countOfFriend"/>
</div>
<div>
<label class="label label-interest">Real value "Count of friends"</label>
<span data-bind="text: countOfFriend"></span>
</div>
</div>
<div>
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),
countOfFriend = ko.observable(countOfFriendsv);
return {
id: id,
name: name,
countOfFriend: countOfFriend
}
}
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;
}
Ссылки
- Полный пример использования данного кастомного обработчика на jsFiddle. В реализации наглядно можно увидеть реальное значение, хранимое в поле и значение выводимое в текстовое поле для пользователя.
Спасибо за внимание!
Автор: CyberLight