В общем передо мной встала задача — переписать один из контролов, построенный на репиттере и сделать его легче, отзывчивее для клиента. При этом решил использовать knockout. Внутри для отображения цифровых данных использовались DevExpress'овские текстовые поля, они очень удобны и служили исправно, но тут встал вопрос, а как же при замене на обычные текстовые поля, я смогу добавить маску.
Для того чтобы решить данную задачу, я стал искать, какие же есть нормальные плагины jquery или библиотеки, которые позволят просто и быстро решить задачу. В итоге я нашел две библиотеки:
Посмотрев на возможности библиотек, я точно смог сказать, что буду использовать autoNumeric, так как у него есть возможность не показывать маску ввода, а обрабатывать вводимое значение на лету. Его поведение схоже с поведением текстового поля DevExpress. У плагина jquery.maskedinput, к сожалению, такой возможности не обнаружил. Есть только возможность ввода данных по строгой маске, которая при этом появляется в текстовом поле, информируя пользователя о предстоящем формате ввода. Для моего случая такая обработка не подходит.
Ну что ж, с выбором библиотеки маскирования данных в текстовом поле, я определился, но вот как же я буду подвязывать маску для полей. Сначала была мысль, установить для всех необходимых полей, использующих единый формат ввода, определенный класс, а затем при помощи jquery скормить библиотеке autoNumeric. Но мне показалось, что это решение, будет не очень удобным.
Тогда я подумал, а почему бы не воспользоваться мощью knockout! Так как knockout, позволяет реализовывать кастомные обработчики для связывания данных, то я решил создать именно такой обработчик и указывать его в атрибуте
Привожу значение некоторых полезных методов, используемых мной при решении задачи:
Привожу значение опций, передаваемых в метод
Пример передачи, используемых мной опций, для конфигурирования autoNumeric:
* Это означает что autoNumeric будет использовать ',' для разделителя сотен целой части и '.' в качестве разделителя дробной части. А так же кол-во цифр после дробного разделителя будет ровняться 0 (т.е. будут отображаться целые числа).
Выше приведенная реализация позволяет вот таким образом задействовать данный обработчик:
или с указанием опций:
В данном примере обработчика, обновление значения будет происходит при событии 'focusout'. Для пользователя, введенное значение будет выглядеть красиво, а в реальном observable свойстве, оно будет оставаться пригодным для арифметической обработки. Таким образом, я могу сразу решить две проблемы: привязка нужного (передача) контрола autoNumeric, и передача (обновление) значения в observable свойстве.
Спасибо за внимание!
Начало
Для того чтобы решить данную задачу, я стал искать, какие же есть нормальные плагины 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 — указывает, сколько цифр будет указываться после разделителя дробной части
Пример передачи, используемых мной опций, для конфигурирования autoNumeric:
$('.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 }; //привязка 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); } };
Выше приведенная реализация позволяет вот таким образом задействовать данный обработчик:
<input type="text" data-bind="numberMaskedValue: countOfFriends"/>
или с указанием опций:
<input type="text" data-bind="numberMaskedValue: countOfFriends, 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: countOfFriends"/> </div> <div> <label class="label label-interest">Real value "Count of friends"</label> <span data-bind="text: countOfFriends"></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), 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; }
Ссылки
- Полный пример использования данного кастомного обработчика на jsFiddle. В реализации наглядно можно увидеть реальное значение, хранимое в поле и значение выводимое в текстовое поле для ��ользователя.
Спасибо за внимание!
