Draw table
Материал из Eludia.
Содержание |
Процедура draw_table служит для отрисовки таблиц (списков, реестров, журналов). Как правило экран-список содержит единственный вызов draw_table, а экран-форма — единственный вызов draw_form, за которым могут следовать несколько вызовов draw_table (таблицы дочерних объектов).
Визуально таблица состоит из:
- заголовка;
- верхней панели;
- шапки;
- тела;
- нижней панели.
С таблицей могут ассоциироваться две отдельные HTML-формы:
- форма поиска (связана с фильтрами и переключателями верхней панели);
- форма ввода (содержит элементы ввода, отображаемые в клетках тела таблицы).
Синопсис
draw_table (
[ ... шапка ... ],
sub { ... callback для отрисовки строки ... },
$data -> {records}, ## обычно результат sql_select_all_cnt или sql_select_all
{... опции ...},
);
Заголовок таблицы
В первых версиях прототипов Eludia заголовок таблиц генерировался отдельным вызовом draw_table_header. Если вы встретите подобное в унаследованном коде, не копируйте, пожалуйста, этот устаревший стиль.
Сейчас заголовок таблицы прописывается в опции draw_table:
{
...
header => {label => 'Список документов'},
...
},
Помимо label, изредка указывается параметр height, который соответствует вертикальному отступу до таблицы и измеряется в пикселях.
Вместо заголовка у таблицы можно указать путь (path) по аналогии c draw_form:
{
...
path => $data -> {path}, # результат sql_select_path
...
},
Если расположить сверху от таблицы группу закладок, её следует сгенерировать отдельным вызовом draw_form:
draw_form ({menu => ...}, {}, [])
.
draw_table (...)
Верхняя панель
У большинства таблиц за заголовком следует панель с кнопкой "Добавить", полем для быстрого поиска, навигацией по страницам длинной таблицы и прочими визуальными элементами для уточнения выборки.
Все эти элементы перечисляются в опции top_toolbar:
top_toolbar => [{
keep_params => ['type', 'id'],
off => ...
},
{ # кнопка добавления новой карточки
type => 'button',
icon => 'create',
label => '&Добавить',
href => {action => 'create'},
off => ...
},
{ # текстовое поле поиска
type => 'input',
name => 'q',
label => 'Поиск',
keep_params => [],
off => ...
},
{ # поле поиска типа 'дата'
type => 'input_date',
name => 'dt',
label => 'Дата',
off => ...
},
{ # галочка как поле поиска
type => 'input_checkbox',
name => 'only_new',
label => 'Только новые',
off => ...
},
{ # списочное поле поиска
type => 'input_select',
name => 'id_user',
values => $data -> {users}, # sql_select_vocabulary
empty => '[Все авторы]',
# label => 'Автор', # работает, только если указано show_label
# show_label => 1, # используется редко, так как смысл указывается в empty
off => ...
},
{ # всплывающее дерево с множественным выбором
type => 'input_tree',
name => 'id_role',
values => $data -> {roles}, # как на форме
label => '[Выберите роли]',
},
{type => 'pager'} # для листания длинной выборки
fake_select (), # активные / все / удалённые: переключатель для $_REQUEST {fake}
]
Первый хэш содержит опции не для отдельного элемента, а для панели в целом.
Панель, не содержащая ни одного элемента (с учётом опций off), не отображается вовсе.
Опция keep_params задаёт список имён параметров, значения которых должны сохраняться при всех переключениях полей поиска и смене страниц выборки (они закладываются в hidden-поля поисковой формы). Отметим необходимость указания пустой keep_params для текстовых полей поиска. Это нелогично, но так необходимо для совместимости ядра Eludia с некоторыми давно написанными приложениями. В шаблоне StEludio заложен правильный фрагмент кода, который почти не приходится редактировать, так что неприятности здесь возникают крайне редко.
Шапка
В простейшем случае шапка может вообще не указываться: если callback-функция для строки идёт первым аргументом, то столбцы не озаглавлены.
Если вам требуется просто поставить над каждой колонкой текстовый заголовок, то шапку можно задать списком строк:
[ '№', 'Дата', 'Содержание', ],
Каждая строка может быть заменена на хэш (набор опций), где заголовок записывается с ключом label. Среди прочих опций сразу отметим hidden, блокирующую показ клетки при наступлении заданного условия:
[
'ФИО',
{
label => 'Логин',
hidden => !$_USER -> {role} ne 'admin'
},
],
Если tooltip для ячейки шапки должен отличаться от видимой надписи (например, быть её расшифровкой), то его содержимое следует указать в опции title:
[...
{
label => '№ п/п',
title => 'Номер по порядку',
},
...],
Для широких таблиц с горизонтальным скроллингом имеется возможность сделать первые несколько клеток фиксированными:
[
{label => '№', no_scroll => 1},
{label => 'Показатель', no_scroll => 1},
'Январь 2000',
...
],
Кроме того, вы можете указать произвольный набор HTML-атрибутов для тега TH опцией attributes, но, разумеется, этой возможностью не стоит пользоваться без крайней нужды:
[...
{
label => '№ п/п',
attributes => {
id => 'my_very_private_tag',
onmouseover => "getElementById('my_very_private_tag').style.dispay = 'none'",
onmouseout => "getElementById('my_very_private_tag').style.dispay = 'block'",
},
},
...],
Ссылки с заголовков
Для клетки шапки можно определять гиперссылку опцией href. Её значение обрабатывается в соответствии с общими правилами URL rewriting:
[
'Товар',
{label => 'Поставщик 1', href => '/?type=suppliers&id=1'},
{label => 'Поставщик 2', href => '/?type=suppliers&id=2'},
],
Как правило, гиперссылка на шапке должна переключать порядок сортировки выборки: при первом клике устанавливается упорядочение по соответствующему полю, при втором — порядок меняется на противоположный. Для генерации соответствующих фрагментов SQL в API Eludia предусмотрена функция order, использующая два специальных параметра запроса:
- order
- имя поля сортировки;
- desc
- признак того, что порядок — обратный.
Чтобы правильно передавать соответствующие параметры, необходимо ставить значение href в зависимость
от контекста запроса, что в явном виде делать неудобно. Гораздо проще автоматически генерировать нужные ссылки, указав имя поля в качестве опции order клетки шапки:
[
{label => 'Наименование', order => 'label'},
{label => 'Цена', order => 'price'},
{label => 'Количество', order => 'quantity'},
],
Многострочные шапки
Шапка может состоять более чем из одной строки. В этом случае её описанием становится массив массивов:
[ ['№', 'Дата', 'Содержание'], [ 1 , 2 , 3 ], ],
Чаще всего в таких случаях требуется объединение ячеек по вертикали и по горизонтали. Оно обеспечивается опциями colspan и rowspan:
[
[
{label => 'Показатель', rowspan => 2},
{label => "$year г., тыс. руб", colspan => 2},
],
[
'План',
'Факт',
],
],
Тело таблицы
Строки, следующие за шапкой, получаются применением заданной callback-функции (2-й аргумент) к выборке (3-й аргумент).
При этом текущая запись доступна callback-функции в качестве значения глобальной переменной $i.
В подавляющем большинстве случаев callback состоит из предварительной чистки данных и последующего вызова функции draw_cells:
sub {
__d ($i, 'dt_from', 'dt_to');
$i -> {label} ||= 'Неизвестно что';
draw_cells (...);
}
Процедура draw_cells имеет 2 парамера: хэш с опциями, общими для всех клеток, и список описаний самих клеток. В простейшем случае опций нет, а клетки описываются скалярами:
draw_cells ({}, [
$i -> {dt},
$i -> {no},
$i -> {label},
]);
Как и для клеток шапки, скаляр может заменяться на хэш с опциями, среди которых — title, hidden и no_scroll. Отметим также опцию off: в отличие от остальных использований в API Eludia, здесь она не делает невидимым весь соответствующий элемент интерфейса (то есть клетку таблицы; для этого есть hidden), но заменяет его содержимое на пробел. Кроме того, сразу опишем опцию picture, обеспечивающую форматный вывод для чисел.
draw_cells ({}, [
{label => $i -> {dt}, no_scroll => 1, title => "Текст для tooltip'а"},
{label => $i -> {no}, off => $i -> {no} !~ /\d/},
{label => $i -> {label}, hidden => !$_USER -> {role} ne 'admin'},
{label => $i -> {price}, picture => '### ### ### ###,#',
]);
Слишком длинное содержимое
По умолчанию все надписи печатаются в одну строку (тег NOBR) и усекаются до длины $conf -> {max_len} (обычно 50), при этом полное содержимое доступно в качестве tooltip.
Если это требуется переопределить, можно использовать опции max_len, title и no_nobr:
{
label => $i -> {label},
max_len => 1000000,
no_nobr => 1,
title => ,
},
Такое описание клетки характерно для таблиц, представляющих списки реплик (форум, история согласования документа). Если это так, обычно требуется сопоставить каждой строке выборки две (или более) строк(и) таблицы: первая — заголовок (дата, статус, автор), а вторая — текст реплики.
Для этого можно заменить одну callback-функцию на список из нескольких:
draw_table (
[
sub { ... callback для отрисовки заголовка ... },
sub {
$i -> {note} or return undef;
draw_cells ({}, [{
label => $i -> {note},
colspan => 3,
}]);
},
],
$data -> {records},
{... опции ...},
);
Отметим использование опции colspan в последнем примере: как в случае шапки, оно аналогично стандартному HTML.
Ещё одна деталь: если callback возвращает undef, то строка HTML-таблицы (TR) не порождается вовсе.
Ссылки и строки итогов
Как правило, все клетки на одной строке содержат одну гиперссылку (на экран-карточку), она прописывается в общие опции, но может быть переопределена для каждой отдельной клетки:
draw_cells ({
href => "/?type=docs&id=$i->{id}",
}, [
$i -> {dt},
$i -> {no},
{
label => $i -> {label},
href => "/?type=docs&id=$i->{id}&__edit=1",
},
]);
Случается такое: выборка состоит из множества строк, соответствующих отдельным документам и одной (или более) строк(и) итогов (например, сгенерированной при помощи add_totals), гиперссылка для которой в таком случае не имеет смысла. Зато итоговые строки должны выделяться визуально (полужирный шрифт, затенённый фон). Всё это достигается при помощи общей опции is_total. Типичный случай решающего правила для определения итоговой строки — отсутствие компоненты id.
draw_cells ({
href => "/?type=docs&id=$i->{id}",
is_total => !$i -> {id},
}, [
...
]);
Поля ввода
Клетки таблиц могут содержать не только статические строки, но и поля ввода данных, которые, в свою очередь превращаются в статические строки при истинности опции read_only или параметра $_REQUEST {__read_only}, как в draw_form. При этом, для совместимости, значения в полях ввода задаются опцией label:
{
type => 'input',
name => "_value_$i->{id}",
label => $data -> {"value_$i->{id}"},
read_only => $i -> {is_calculated},
picture => $big_money,
},
Отметим, что здесь, в отличие от draw_form, лидирующий подчерк не приписывается к имени параметра автоматически, а значение всегда требуется задавать явно.
Помимо текстовых полей, доступны списки выбора:
{
type => 'select',
name => "_value_$i->{id}",
value => $data -> {"value_$i->{id}"},
values => ... # см. sql_select_vocabulary
read_only => $i -> {is_calculated},
},
и checkbox'ы:
{
type => 'checkbox',
name => "_value_$i->{id}",
}
Уровни отступа (псевдодерево)
Если ваша таблица отображает иерархическую структуру, то бывает удобно визуализировать дерево, просто задавая уровень вложенности целым числом:
{
label => "$i->{ord} $i->{label}",
level => $i -> {level},
...
},
Статусные иконки
Для записей, изображающих этапы утверждения документов, предусмотрена возможность сопровождать надпись в клетке статусной иконкой:
{
label => "$i->{ord} $i->{label}",
status => {code => 100, label => 'Новый документ'},
...
},
Диаграмма Ганта
Текстовую таблицу легко приспособить для отображения псевдографической диаграммы Гантта. Для этого достаточно вписать фрагмент gantt в вызов draw_cells:
draw_cells ({
href => "/?type=tasks&id=$$i{id}",
gantt => {
plan => {
from => $i -> {dt_from_plan},
to => $i -> {dt_to_plan},
title => '...',
},
fact => {
from => $i -> {dt_from_fact},
to => $i -> {dt_to_fact},
},
},
},[
{
label => $i -> {label},
no_scroll => 1,
level => $i -> {__n},
},
При этом автоматически сгенерируется необходимое количество дополнительных столбцов и шапка по годам / кварталам месяцам. Всего у шапки будет ровно 3 строки, простановка rowspan (и, скорее всего, no_scroll) для явно заданных столбцов — ответственность программиста.
Нижняя панель
Чтобы таблица заработала как форма ввода, необходимо отрисовать кнопку, которая инициировала бы соответствующий POST, а рядом с ней, как правило, требуется Esc для отмены редактирования. Этот пункт пока следует признать наименее автоматизированным во всём API Eludia: нижнюю панель с кнопками требуется отрисовывать отдельным вызовом draw_ok_esc_toolbar, результат которой передаётся draw_table опцией toolbar:
toolbar => draw_ok_esc_toolbar (),
Прочие опции
Как везде в API Eludia, опция off позволяет блокировать показ элемента GUI, в данном случае всей таблицы. Характерный пример использования:
off => !$_REQUEST { __read_only},
для таблиц дочерних объектов на экранах-карточках (чтобы не было возможности уйти по ссылке, не записав изменения).
Настройка формы ввода
Опции name, action и target задают имя HTML-формы ввода, действие и целевой фрейм соответственно. По умолчанию принимается
name => 'form', action => 'add', target => 'invisible',
Имя 'form' дублирует значение аналогичного параметра в draw_form, что нельзя признать удачной находкой. Иногда это порождает такую неприятность: сначала разрабатывается экран с единственной формой (draw_form), потом к нему пририсовывается таблица (draw_table) — и при попытке сохранить данные возникает ошибка "Объект не поддерживает это свойство или метод". Исправить положение очень легко: нужно только прописать явное имя в draw_table. Впрочем, если вы пользуетесь шаблонами StEludio, то эта ситуация, вам, скорее всего, не встретится.

