11 мая 2010

jqGrid custom date picker

Да, давно не писал, но это жизнь, которой мало, и работа, которой много. Вот по работе иногда приходится хлопать одной ладонью.

Так и на прошлой неделе - нужно было приделать к редактору модели в jqGrid свой элемент для редактирования дат, наподобие того, что генерят рельсы в ответ на select_date. Скудость документации и отсутствие толковых примеров и как следствие полдня экспериментов на пути к желаемому и стали причинами появления данного поста.

Для начала обратимся к истокам и объявим наше поле как custom, указав свои функции для создания элемента и извлечения значения из него.
{name:'v_birth', index:'v_birth',formoptions:{elmprefix:"* &nbsp"},edittype:'custom',editable:true,editoptions:{custom_element:dinput, custom_value:dvalue}}


Дальше немного JS магии.

function dinput (value, options) {
var m = document.createElement("select");
var d = document.createElement("select");
var y = document.createElement("select");
m.setAttribute('class', 'month');
var o = document.createElement('option');
var t = document.createTextNode('--');
o.appendChild(t);
m.appendChild(o);
for(var month = 1; month <= 12 ; month++)
{
o = document.createElement('option');
t = document.createTextNode(month);
o.setAttribute('value', month);
if(month == Number(value.substring(0,2)))
{
o.setAttribute('selected', 'yes');
}
o.appendChild(t);
m.appendChild(o);
}
d.setAttribute('class', 'day');
o = document.createElement('option');
t = document.createTextNode('--');
o.appendChild(t);
d.appendChild(o);
for( var day = 1; day <= 31; day++ ) {
o = document.createElement('option');
t = document.createTextNode(day);
o.setAttribute('value', day);
if(day == Number(value.substring(3,5)))
{
o.setAttribute('selected', 'yes');
}
o.appendChild(t);
d.appendChild(o);
}
y.setAttribute('class', 'year');
o = document.createElement('option');
t = document.createTextNode('--');
o.appendChild(t);
y.appendChild(o);
for( var year = 1850 ; year <= 2010; year++ ) {
o = document.createElement('option');
t = document.createTextNode(year);
o.setAttribute('value', year);
if(year == Number(value.substring(6,10)))
{
o.setAttribute('selected', 'yes');
}
o.appendChild(t);
y.appendChild(o);
}
var b = document.createElement('button');
t = document.createTextNode('X');
b.appendChild(t);
$(b).click(function(){
$(m).find('option:first').attr('selected', 'yes');
$(d).find('option:first').attr('selected', 'yes');
$(y).find('option:first').attr('selected', 'yes');
});
return [m,d,y, b];
}
function dvalue(elem)
{
var res = $(elem).filter('select').map(function() {
return $(this).val();
}).get().join('-');
if(res == '--------')
{
return '';
} else {
return res;
}
}


Создаём, заполняем значениями с выбором одного из них, и возвращаем массивом три select'а и кнопку сброса.

Подразумевается, что в таблицу дата приходит в американском формате (mm-dd-yyyy), именно поэтому у меня поле называется v_birth - это виртуальный атрибут, в базе это естественно тип Date. О валидации таких атрибутов и их применении совместно с jqGrid стоит сказать, пожалуй, отдельно, своим постом.

Пользователь увидит три селекта и кнопку сброса их в начальное значение (--). Браузер - невалидный код (три селекта с одинаковым id), но промолчит. Функция извлечения значения благодаря этому увидит все три select'а, отмапит их в массив выбранных значений и склеит из него строку. Бэкенд - либо опять же дату в американском формате, либо пустую строку, если пользователь сбросил дату, либо неверную дату, и может сохранить, об NULL'ить или остановить валидацию на своё усмотрение.

И для завершения - ещё один штрих. Истоки.

...
.navGrid('#my_pager',
{edit:true,add:true,del:true,search:false,refresh:true},
{recreateForm:true, afterSubmit:function(r,data){return afterSubmit(r,data,'edit');}},
....
Обратите внимание на recreateForm:true. С этой опцией элемент будет создаваться каждый раз при открытии формы редактирования и устанавливать начальные значения из нужной модели, а не из первой, для которой он был вызван.
А теперь отмазки. JS код грязноват и неоптимален, но я иррационально не люблю JS. Кроме того, на самом деле я использую 2dcJqgrid Rails plugin, но уже сильно модифицированный (оригинал бы не позволил сделать описываемое), так что привожу JS код для создания таблицы.

Такие вот пироги. Надеюсь, юбилейная 200-я запись была вам полезна.