Как работать со списками в HTML-форме? Точнее, как их формировать, не нарушая стройной логики представления, основанной на структуре mysql-таблицы? Лучший вариант - перенести формирование элементов select в браузер!

Javascript и логика представления (design pattern «JS-словари»)

В статье о генерации HTML формы для wysiwyg редактора мы привели пример небольшой автоматизации в программировании – создание формы на основе структуры mysql-таблицы и последующая обработка данных формы (с минимальным произволом человека). Это ведь очень удобно – когда схема представления напрямую опирается на структуру данных (и благодаря этому упрощаются вычисления при обновлении таблиц)!

Эта блестящая идея полностью провалилась при попытке нормализации базы, когда в таблице ссылок (в каталоге сайтов) вместо поля "Рубрика" (со значениями "Бизнес", "Строительство", "Туризм"...) появилось поле "rubr_id" (со значениями "1", "2", "3"...). То есть идея провалилась частично, именно для этого «нормализованного» поля – но в данном случае ведь «частично» всё равно что «полностью».

Идея была такая: при добавлении нового поля в таблицу mysql, его описание добавляется в соответствующий массив в программе – и поле (элемент input) автоматом подрисовывается к форме редактирования (или добавления) информации. «Нормализованное» поле тоже, конечно, добавится без проблем – только пользователь вместо рубрик будет видеть непонятные цифры (и не сможет осознанно редактировать рубрики):

cat_form1.gif

Рисунок 1.

Как изменить схему представления?

Массив, управляющий представлением, был у нас очень прост:

В него так же просто добавить две новые строки:

– и пользователь увидит те самые непонятные цифры идентификаторов. Ясно, что управляющий массив придётся усложнить. Например, добавить в него название элемента для вывода информации (в нашем случае – select), а также добавить наименование mysql-таблицы, из которой будет формироваться список значений для подстановки в поле:

Но что дальше? По «классической» логике, PHP-скрипт должен будет в процессе генерации формы, натолкнувшись на edit_type "select", обратиться к таблице, указанной в поле table с запросом, и сформировать из неё нужный HTML-элемент. На самом деле ещё хуже: сформировать массив для "HTML-select-one"-шаблона, отправить его функции-шаблонизатору, а потом уже передать в массив для шаблона формы.

Но почему же «ещё хуже»? Логика как логика... Но можно лучше!

Шаблон веб-проектирования «JS-словари»

При создании выпадающих списков на сервере, на страницу будет добавлено 100-200 слов. Это в нашем простом случае. А вообще может быть и 1-2 тысячи. А если нам понадобится ещё и множественный список выбора (<select multiple="multiple">)? Или ещё какое извращение? – Надо будет формировать HTML-элемент совсем по другому принципу и надо будет иметь в запасе больше сложных HTML-шаблонов.

Мы считаем, что в подобных случаях правильнее перенести часть логики на сторону клиента (чтоб он не выглядел совсем уж голым терминалом). Это значит, сервер будет отправлять браузеру HTML именно в таком виде, как на Рисунке 1. Это значит, что без Javascript пользователи редактировать информацию не смогут. Ну... в 21-м веке живём. Пора уже принять Javascript как аксиому. Пользователям с выключенным JS будем выдавать сообщение «Вы не сможете редактировать информацию». А если пользователь не отреагирует на это сообщение – значит, он бот (туда и дорога).

Записи управляющего массива у нас будут выглядеть примерно так (немного неочевидные наименования полей и таблиц, ну, так уж получилось когда-то давно):

И, в полном соответствии с духом разделения вычислений и представления, при генерации формы поле массива edit_type вообще никак не используется! А используется оно при генерации инлайн-скрипта javascript:

Теперь страница, открытая в браузере, «знает» об особых полях формы и о таблицах, из которых должны заполняться эти поля. Таблицы на стороне клиента, разумеется должны уже существовать. Это достаточно распространённая практика – выгружать часть информации MySQL или конфигурации PHP в javascript (посмотрите любой известный wysiwyg редактор на стороне клиента). У нас таблицы лежат в скрипте http://irweb.ir2.ru/dict.js и имеют вид:

В общем-то в нашем случае было бы достаточно и массива (dict.cities = ['', 'Иркутск', ...]); но, во-первых, было бы сложнее работать с точными числовыми индексами (понадобилось бы больше кода: dict.cities[1] = 'Иркутск'; dict.cities[27] = 'Братск' ...]), а главное, индексы в самой базе не обязательно могут быть числовыми. Так что мы сразу выбрали более универсальное решение – объект.

Таблицы выгружаются в файл dict.js функцией PHP js_dict_unload(). Её можно вызвать через соответствующий УРЛ, а можно привязать к изменению таблиц 'ircat' и 'cities' (если это изменение производится через CMS).

Дальше – дело техники. При загрузке страницы с формой редактирования (добавления) ссылок в каталог, из скрипта http://irweb.ir2.ru/edit.js запускается инициирующая функция init_edit(), она проверяет все поля формы, и, натолкнувшись на поле, присутствующее в объекте d.fields_dict инлайн-скрипта страницы, генерирует элемент select и подставляет его вместо текущего элемента input. И выглядит всё (со включённым javascript!) вполне прилично: http://irweb.ir2.ru/add.php.

У нас элемент select намертво вшит в технологию – в нашем каталоге сайтов больше ничего никогда не предвидится (он только и годится теперь для опытов). В других каталогах, впрочем, тоже (ощущается приближение всем им крантов). Но теоретически можно было бы передавать из PHP и ещё один параметр, уточняющий, на что именно следует менять после загрузки страницы определённый input.

Выигрыш в производительности

Кроме удобства в стиле программирования (более «сильное» отделение логики от представления), шаблон JS-словари даёт немалый выигрыш в скорости работы сайта.

Во-первых, простая арифметика: часть вычислений производится не на сервере, а в браузере, что разгружает сервер. Во-вторых, размер страницы становится меньше. Может быть, и не очень намного. Но это у нас – 20 рубрик и 30 городов. А списки ведь бывают и в 2000 позиций (например, в каталогах автозапчастей). И, с учётом того, что страницу с формой нельзя кэшировать, все эти сотни килобайт каждый раз будут скачиваться с сервера (что мы и видим в упомянутых каталогах).

В javascript же они как раз спокойно могут кэшироваться; даже не надо записывать большой срок устаревания информации (достаточно установить expires=0), пусть браузер лезет каждый раз на сервер и получает в ответ "304 not modified" – всё равно выигрыш огромный. А если файл словарей dict.js обновится на сервере, он будет автоматом загружен в браузер заново.

Есть, правда, одно «идеологическое» (системное) препятствие при работе со списками через JS. Оно возникает не от размера списка, а от частоты обновления словаря. Словари бывают разные, иногда их использование невозможно без постоянной синхронизации. В этом случае всё равно мы предпочитаем формировать поля со списками на клиенте, только Javascript берёт списки не из готовых таблиц в файле dict.js, а с сервера – с помощью фоновых HTTP-запросов (типа Ajax). Ну, и поля уже выглядят не как элемент select, а как обычный input с «подсказками» (как в поисковиках).

Есть и одно замечание со стороны пользователей: элемент select с большим количеством опций довольно неудобен, в нём бывает трудно найти нужную позицию. В этом случае мы предпочитаем (да и не только мы!) открывать для списка отдельное окно, в котором более удобно можно отфильтровать элементы. Нечто вроде списка рубрик в справочнике предприятий: http://vostsibspravka.ru/listru.htm.

D.M., admin

Комментарии