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.gif

У большинства таблиц за заголовком следует панель с кнопкой "Добавить", полем для быстрого поиска, навигацией по страницам длинной таблицы и прочими визуальными элементами для уточнения выборки.

Все эти элементы перечисляются в опции 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) для явно заданных столбцов — ответственность программиста.

Gantt.gif

Нижняя панель

Чтобы таблица заработала как форма ввода, необходимо отрисовать кнопку, которая инициировала бы соответствующий 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, то эта ситуация, вам, скорее всего, не встретится.

Персональные инструменты
Пространства имён

Варианты
Действия
Навигация
Разработчику
Администратору
Инструменты