Draw form
Она может взрывать горы. Она может летать. Она поднимает тяжести. Она дробит руду. Она заменяет кухонную плиту, детскую коляску, дальнобойное орудие...Ю. Олеша, «Зависть».
Содержание |
Процедура draw_form генерирует HTML-код, соответствующий форме ввода. Как правило, каждая процедура draw_item_of_$_REQUEST{type} содержит единственный вызов draw_form, хотя это вовсе не обязательно.
Форма состоит из:
- пути
- большей частью декоративного элемента, который служит для определения мета текущего объекта в иерархии;
- закладок
- локального меню для навигации по родственным экранам;
- панели
- области, где располагаются кнопки, соответствующие действиям, доступным текущему пользователю с данной записью;
- полей
- собственно элементов ввода данных.
В подавляющем большинстве случаев каждая форма рассчитана на 2 режима показа: просмотра и редактирования:
- в режиме просмотра ($_REQUEST{__read_only}) все поля показываются в виде текста, однако доступны разнообразные дополнительные кнопки действий (например, "Удалить", "Восстановить", "Опубликовать" и т. п.), а также прочая навигация;
- в режиме редактирования (!$_REQUEST{__read_only}) поля допускают ввод данных, однако из всей навигации доступны только кнопки "применить" и "отмена".
Описания полей ввода
Все поля одной формы выравниваются при помощи общей таблицы.
Каждое описание поля — это ссылка на хэш с опциями. Некоторые опции могут быть заданы только непосредственно в вызове draw_form, часть может определяться по умолчанию на основании схемы данных. При этом описание поля ищется по значению name в разделе {columns} описания таблицы, имя которой равно $_REQUEST {__the_table}. Данный параметр автоматически устанавливается процедурой sql_select_hash при первом использовании в рамках текущего HTTP-запроса. В подавляющем большинстве случаев процедура извлечения данных для экрана-формы начинается как раз с вызова sql_select_hash или равносильного ему вызова sql, где нужная таблица упоминается первой в JOIN-выражении.
Из каждого такого описания (кроме типа banner) при автоматической вёрстке формы генерируется 2 клетки: для наименования и для самого поля.
Отдельные поля верстаются в столбец, массивы полей — в строку, при этом количество столбцов базовой таблицы определяется наибольшей длиной массива описаний. Для строк с меньшим количеством клеток выравнивание производится за счёт увеличения colspan последней из них.
Списки полей, упомянутых в описаниях типа hgroup, верстаются в одну клетку вместе со своими наименованиями и не влияют на количество столбцов базовой таблицы.
Поля типа banner всегда занимают одну полную строку с единственной клеткой (имеющей, соответственно, максимальный возможный colspan).
Опции, общие для всех типов полей
За редким исключением, каждому описанию поля соответствует один HTML-элемент INPUT. Его атрибут name — это значение опции name с приписанным префиксом '_'. Значение для поля ввода обычно не задаётся напрямую, а берётся по умолчанию из переменной, передаваемой в качестве 3-го аргумента draw_form (обычно она называется $data).
Таким образом, при пустом value для поля с именем 'label' будет выбрано значение $data -> {label} и сгенерирован <input name="_label" ... >.
{ label => ..., # отображаемый заголовок поля (по умолчанию — REMARKS из описания поля), # value => ..., # значение для текущего поля (используется редко), name => ..., # имя передаваемого параметра и одновременно имя компоненты $data, off => ..., # если непусто, то поле скрыто read_only => ..., # если непусто, то поле отображается как static даже при !$_REQUEST {__read_only} ... },
Типы полей и их специфические опции
Текстовые
Поля этой группы передают на сервер текст, введённый пользователем.
string
Однострочное поле для ввода строки. Тип поля string принимается по умолчанию, явно указывать его не обязательно.
size => ..., # видимый размер поля max_len => ..., # максимальная длина вводимого текста picture => ..., # формат числовых значений (см. Number::Format)
date
Поле для ввода даты. Введённые значения рекомендуется обрабатывать процедурой vld_date.
# format => ..., # Формат даты/времени, например, '%d.%m.%Y' (почти не применяется).
text
Многострочное поле для ввода длинного текста.
rows => ..., # количество строк cols => ..., # количество столбцов
password
Поле для пароля, аналогичное string, но с маскированным вводом.
size => ..., # размер поля
Ссылки на справочники
Поля этой группы передают на сервер id элементов справочников, выбранных пользователем.
select
Выпадающий список для выбора одного элемента из списка. Список значений должен состоять иэ ссылок на хэши с компонентами id и label.
Последняя строка может соответствовать не фиксации значения, а переходу на другой экран со специальным параметром $_REQUEST {__select}: так можно реализовать выбор с попутным дополнением справочника.
values => $data -> {...}, # список значений, как правило, формируемый add_vocabularies empty => '[Выберите ...]', # заголовок пустой строки (если пусто, пустой строки нет) other => '/?type=...' # ссылка на экран редактирования справочника # detail => ['id_voc_city', 'id_voc_street'], # список имён зависимых полей
radio
Группа радио-кнопок для выбора одного значения из списка, аналогично select.
values => $data -> {...}, # список значений
Каждая радиокнопка может сопровождаться дополнительным полем ввода, которое видно только при выборе данной кнопки. Зачастую при этом используется тип hgroup, позволяющий привязать к кнопке целую группу полей:
{ label => 'Офицер ли это', name => 'is_officer', values => [ { id => 0, label => 'нет', type => 'hgroup', items => [ # поля для паспортных данных ], }, { id => 1, label => 'да', type => 'hgroup', items => [ # поля для номера офицерского удостоверения ], }, ] }
checkboxes
Группа галочек для множественного выбора из списка вариантов. Сам список имеет формат, аналогичный тому, что используется для select, однако может иметь иерархическую структуру: каждый элемент id/label может сопровождаться элементом items со ссылкой на список такой же структуры. В этом случае отображается дерево, у котрого для доступа к дочернему элементу должен быть отмечен родительский.
values => $data -> {...}, # список опций height => ..., # если задано, то фиксирует высоту окна с прокруткой cols => ..., # количество столбцов (если список большой, а ширины хватает)
У хэша, передаваемого 2-м параметром draw_form, и традиционно называемого $data, данному полю должна соответствовать ссылка на список id. Если нужный набор id вычисляется по единственной таблице типа "многие-ко-многим", то его удобно извлечь, прмиенив опцию ids процедуры add_vocabularies.
Векторные значения
В Eludia никогда не используются множественные HTML-поля ввода с одинаковыми именами. Все checkbox'ы, логически соответствующие одному полю, имеют разные значения атрибута name. При этом к исходному имени поля как постфиксы приписываются соответствующие id. Например, checkbox для поля 'id_user', соответствующий записи с id=3, имеет атрибут name="_id_user_3", а для id=7 — name="_id_user_7".
Таким образом, если у поля checkboxes c name=user отмечены строки с id=3 и id=7, то при обработке запроса $_REQUEST {_id_user_3} и $_REQUEST {_id_user_7} будут иметь значение 1.
Для получения списка id, заданных таким образом, можно пользоваться функцией get_ids. В подавляющем большинстве случаев с этим списком нужно просто привести в соответствие содержимое связной таблицы: для этого служит процедура sql_store_ids. Которую опять-таки в стандартный ситуациях не обязательно вызывать явно: add_vocabularies заготовит параметры, а do_update_DEFAULT осуществит вызов автоматически.
Кроме того, при предварительной обработке параметров будут установлены следующие значения:
$_REQUEST {_id_user} = [3, 7] # можно не вызывать get_ids $_REQUEST {'_id_user,-1'} = '3,7,-1' # для подстановки в SQL-выражение IN (...)
tree
Поле для множественного выбора, аналогичное checkboxes или radio, однако отображаемое иначе: в виде дерева, любая вершина которого может содержать или не содержать поле для галочки / радиокнопки. Поддеревья в данном случае раскрываются независимо от отмечания полей.
values => [ {id => 1, label => 'Дерево групп', parent => 0}, {id => 2, label => 'Группа 1', parent => 1, is_checkbox => 1}, # или is_radio => 1 {id => 3, label => 'Группа 2', parent => 2, is_checkbox => 1}, # -- "" -- ... {id => n, label => 'Группа 2', parent => p, is_checkbox => 1}, # -- "" -- ],
is_checkbox / is_radio: проблема с $_REQUEST
Долгое время tree рассматривалось исключительно как аналог checkboxes. Поэтому для tree, как и для checkboxes, производится преобразование %_REQUEST на предмет векторных значений. По этой причине в настоящее время, если содержимое дерева с именем id_dep заявлено с опциями is_radio, то $_REQUEST {_id_dep} = [] вне зависимости от выбранного значения. Возможно, это будет исправлено, но для совместимости с checkboxes сейчас рекомендуется брать нужное значение из %_REQUEST_VERBATIM.
suggest
Ввод с подсказкой, сочетающий в себе select и string.
{ name => 'id_voc_goods', label => 'Что', type => 'suggest', size => 60, values => sub {sql (voc_goods => [ 'id', ['label LIKE ?%' => $_REQUEST {_id_voc_goods__label}], [LIMIT => [15]], ])}, href => "/?type=voc_goods&id=$data->{id_voc_goods}", },
Нередактируемые
П[севдоп]оля этой группы служат не для ввода, а для оформления данных.
banner
Поле-заглушка, в действительности представляющее собой промежуточный заголовок на форме. В отличие от подавляющего большинства типов полей, занимает не 2 клетки таблицы, а 1. Не имеет привязки к данным, надпись определяется опцией label.
static
Поскольку любое поле может быть отображено как static (при $_REQUEST {__read_only} или локальном read_only), следующие опции имеют смысл для всех полей.
href => ..., # гиперссылка с теущего поля add_hidden => ..., # непустое значение обеспечивает генерацию hidden-поля с текущим значением
А эта опция имеет смысл только для тех static, что получаются из checkboxes в режиме read_only:
separator => ..., # по умолчанию '< br >', но можно поставить, скажем, ", "
Часто приходится показывать на форме статическое поле, значение которого берётся не из текущей записи (обычно доступной как переменная $data), а родительской. В этом случае можно определить его напрямую:
value => $data -> {some_parent_object} -> {label};
а можно воспользоваться одной из более удобных форм, на ваш вкус:
name => 'some_parent_object.label'; # для тех, кто скучает по java name => 'some_parent_object / label'; # для фанатов XPath
(подходит любой разделитель из символов, не могущих быть частью C-идентификатора)
article
Горизонтальная врезка в тело формы, аналогичная banner, однако представляющая собой не однострочный заголовок, а текст из (как правило) нескольких абзацев.
Ещё отличия от banner: связь с данными по полю name, такая же, как для всех текстовых полей, а опция label в данном случае не имеет смысла.
Прочие
hgroup
Группа полей, отображаемых в одной ячейке последовательно по горизонтали. Если задана опция read_only, то она тиражируется на все элементы.
items => [...], # ссылка на список описаний элементов
Каждому подполю предшествует его наименоdание (label) с двоеточием на конце. Иногда бывает нужно подавить вывод этого двоеточия, для этого служит опция no_colon:
{ label => 'Время', type => 'hgroup', items => [ { name => 'hours', size => 2, }, { label => 'ч.', no_colon => 1, name => 'minutes', size => 2, }, { label => 'мин.', no_colon => 1, }, ], },
checkbox
Поле для галочки. Отмеченное, передаёт значение 1, неотмеченное — 0.
checked => ..., # если истина, то отмечен вне зависимости от value
file
Поле выбора файла для загрузки. Для полей этого типа автоматически приписывается опция href, позволяющая воспользоваться процедурой do_download_$_REQUEST{type}. Если таковая не определена, то вызывается do_download_DEFAULT. Чтобы она корректно работала, достаточно просто следовать шаблонам, заложенным в StEludio.
size => ..., # размер поля
files
Расширяемая группа файловых полей. Данные о файлах, указываемых в этих полях, сохраняются не в той таблице, где хранятся скалярные атрибуты формы, а в другой, относящейся к ней как много-к-1. Например, если требуется хранить множество файлов для документа (таблица docs), то имеет смысл завести таблицу doc_files со ссылкой id_doc и стандартными файловыми столбцами (file_name, file_size, file_type, file_path и, возможно, file_body). Эту дополнительную таблицу и её поле-ссылку необходимо указать в опции field. Для записи файлов в БД служит процедура sql_upload_files. Данная процедура с параметром {name => 'file'} вызывается в do_update_DEFAULT, так что для единственной группы с именем file код обработчика разрабатывать не требуется.
В режиме просмотра поле содержит имена загруженных файлов со ссылками, активизирующими действие download для типа, совпадающего по имени с таблицей. Данные извлекаются автоматически.
Если требуется обработка множественных файлов, для которой описанный способ хранения данных не подходит, можно воспользоваться процедурой upload_files.
size => ..., # размер поля (одинаковый для всех) field => 'dos_files.id_doc' # таблица и её поле, ссылающееся на текущий $_REQUEST {id}
color
Поле для выбора цвета. Значения представляются в обычном HTML-формате #RRGGBB. Специфических опций не имеет.
Описания кнопок-действий
У draw_form есть 3 аналогичных опции: left_buttons, additional_buttons и right_buttons, являющиеся списками описаний кнопок, расположенных, соответственно: левее OK, между OK и Cancel и, наконец, правее Cancel на стандартной панели. Типичное описание кнопки имеет вид:
{ icon => '...', # имя GIF-файла иконки label => '...', # надпись href => {action => '...'}, # гиперссылка target => 'invisible', # целевое окно/фрейм hotkey => {code => F..}, # горячая клавиша confirm => "Вы уверены, что..?", off => # условие отключения !$_REQUEST {__read_only} || ... , keep_esc => 1 # только для перехода на экран с возвратом! },
Если href не содержит параметра action (то есть соответствующий запрос не предполагает действия, хотя бы псевдодействия print), то опции confirm и target, очевидно, не имеют смысла. Зато именно в этом случае (как правило, это переход на экран, с которого в дальнейшем потребуется вернуться), стоит поставить опцию keep_esc для корректного функционирования Esc.
В список right_buttons часто включается результат функции del, которая генерирует описание кнопки "удалить" или "восстановить" в зависимости от состояния текущей записи.
Закладки (меню)
Набор закладок предназначен для навигации по родственным формам, которые представляют один и тот же объект с разных точек зрения (например: скалярные реквизиты договора; его список актов и его же список платежей). Соответственно, такой набор описаний должен дублироваться минимум на 2 экранах. По этой причине его обычно выносят в функцию с именем __$_REQUEST{type}_menu, описанную в файле Content/menu.pm:
sub __contrat_menu { return [ { type => 'contrat', label => 'Основные реквизиты (F2)', hotkey => {code => F2}, }, { type => 'contrat_acts', label => 'Акты (F6)', hotkey => {code => F6}, }, { type => 'contrat_pays', label => 'Платежи (F7)', hotkey => {code => F7}, }, ] }
Далее эта функция используется в опции menu при вызове draw_form:
... menu => __contrat_menu (); ...
Для каждого описания автоматически генерируется href, сохраняющий все параметры запроса, кроме type, и обеспечивающий корректную в смысле esc навигацию (перемещеие по закладкам не считаются заходами на новые экраны).
Обычно переключение type при сохранении id — это всё, что требуется, однако в некоторых случаях приходится прописывать ссылки напрямую. Это вполне возможно, только следует позаботиться о том, чтобы на соответствующем экране текущая ссылка оказалась подсвеченной:
... { href => {type => 'contrat_report', template => 1}, label => 'Отчёт №1 (F6)', hotkey => {code => F6}, is_active => $_REQUEST {type} eq 'contrat_report' && $_REQUEST {template} == 1 }, { href => {type => 'contrat_report', template => 2}, label => 'Отчёт №2 (F7)', hotkey => {code => F7}, is_active => $_REQUEST {type} eq 'contrat_report' && $_REQUEST {template} == 2 }, ...
Прочие опции
Общие
- off
- если истина, то форма не отображается;
Управление стандартными кнопками
- no_edit
- если истина, то кнопка "редактировать" не отображается (часто вычисляется в зависимости от полномочий текущего пользователя);
- no_ok
- если истина, то кнопки "OK" и "редактировать" не отображаются.
- no_cancel
- если истина, то кнопка "вернуться" не отображается.
- esc
- ссылка с кнопки "вернуться" (рекомендуется не задавать явно за исключением особо нестандартных случаев)
- bottom_toolbar
- HTML панели с кнопками в целом. Основной сценарий использования: значение ' ' для подавленя вывода панели.
- label_ok
- нестандартная надпись на кнопке ОК ("применить")
- label_cancel
- нестандартная надпись на кнопке Cancel ("отмена")
- left_buttons
- список кнопок, добавляемых левее "редактировать"
- additional_buttons
- список кнопок, добавляемых между "редактировать" и "вернуться"
- right_buttons
- список кнопок, добавляемых правее "вернуться"
Управление параметрами формы
- name
- имя HTML-формы (form/@name), по умолчанию 'form'.
- action
- действие, по умолчанию 'update'.
- target
- целевое окно, по умолчанию 'invisible'.
- keep_params
- список имён параметров, значения которых будут переданы через hidden-поля. По умолчанию передаются только type и id.