Задача к Друпалу относится постольку, поскольку всё что мы делаем - это Друпал, так то это просто фронтенд. Итак, задача проще простого: сделать числовое поле с форматированием по тысячам, то есть группы по 3 цифры должны разделяться пробелом, например: 45 312 862. На удивление, на просторах интернета решений этой задачи не то что бы обнаружилось в избытке. Даже уважаемые калькуляторы типа xe.com форматируют ввод только при потере фокуса полем. В общем, пришлось писать самому.
В поисках совершенства требования были поставлены так:
- форматироваться должно на лету, то есть прямо в поле ввода по ходу ввода
- всё должно работать максимально естественно (удаление, редактирование, вставка)
- в том числе и на мобильных устройствах
- на сервер должно улетать число без всяких пробелов
На фронтенде получилось следующее: https://codepen.io/gease-the-vuer/pen/rNmoMBp
Сначала я пытался прикрутить input type="number", но у него оказалось огромное количество ограничений, в том числе невозможность определить позицию каретки (selectionEnd). Оказалось правильней использовать стандартный input type="text", заблокировать нажатия всех клавиш, кроме цифр, стрелок вперёд-назад и удаления, а также вставки Ctrl+V. После этого при каждом изменении поля (событие input) проводить довольно хитрую манипуляцию с содержимым поля, вставлять его обратно и ставить каретку на место. Плюс заблокировать любую вставку, содержащую запрещённые символы.
Это всё отлично работает на традиционной клавиатуре. Но на мобильных устройствах, во-первых, нет события keyDown, во-вторых, с точки зрения пользователя, будет выглядеть крайне противоестественно, если он пытается нажать кнопку, а она не работает. Там логика другая: нам нужно вызвать виртуальную клавиатуру, на которой нет букв, только цифры. И это обеспечивает атрибут inputmode="numeric". Про атрибут inputmode хорошо написано здесь. Да, у пользователя остаётся возможность ввести разную чушь, но это уже возложим на серверную валидацию.
Итого, в дополнение к упомянутому codepen'у с чисто Друпальной стороны нам остаётся сделать две вещи: убрать пробелы при отправке формы (а она в нашем конкретном случае отправляется AJAX'ом) и валидировать на сервере.
Разработчки ядра предусмотрели возможность менять данные формы при отправке AJAX'ом (core/misc/ajax.es6.js):
/**
* Modify form values prior to form submission.
*
* @param {Array.<object>} formValues
* Processed form values.
* @param {jQuery} element
* The form node as a jQuery object.
* @param {object} options
* jQuery.ajax options.
*/
Drupal.Ajax.prototype.beforeSubmit = function(formValues, element, options) {
// This function is left empty to make it simple to override for modules
// that wish to add functionality here.
};
так что нам остаётся только воспользоваться предоставленной возможностью:
Drupal.Ajax.prototype.beforeSubmit = function (formValues, element, options) {
for (var i in formValues) {
if (formValues[i].name === our_field_name) {
formValues[i].value = formValues[i].value.replace(/\s/g, "");
}
}
};
Что касается серверной валидации, всё ещё проще: добавляем к нужному элементу формы паттерн
<?php$form[$element]['#pattern'] = '\d+';?>