В этой статье описываются три типа отчетов апекса: Classic Report, Interactive Report (IR) и Interactive Grid (IG). Classic Report — самый простой из них, но в сочетании с другими компонентами позволяет делать достаточно неожиданные вещи. IR и IG обладают намного более широкими возможностями из коробки, но это в значительной степени вещи в себе. С одной стороны, их широкие возможности делают ненужным какой-то дополнительный тюнинг, с другой, эти же возможности довольно жестко ограничивают возможность сделать что-то дополнительно. Многое из того, что применимо к Classic Report, применимо и к ним тоже.


КДПВ


Оглавление


Classic Report
    Дополнительное форматирование данных
        Форматирование с помощью свойств столбцов
        Форматирование путем изменения шаблона
    Добавляем интерактива
        События вида "клик на строке отчета"
        Изменение данных с помощью Classic Report
Interactive Report (IR)
Interactive Grid (IG)
    Создание столбцов не через SQL
    Новые типы столбцов
    Master-Detail отчеты на основе IG
    Обработка ввода данных в IG


Classic Report


Создать отчет можно двумя способами. Первый — создать вместе со страницей, через мастер создания страниц: выбрать тип страницы "Report", далее — подтип "Classic Report", далее пройти шаги мастера для задания свойств страницы, и последний шаг — указать источник данных. Будет создана новая страница с одним отчетом, на которую, при желании, можно потом добавить что-нибудь еще. Этот способ имеет скорее историческую ценность, до появления современного Page Designer в версии 5.0 так было действительно проще.


Второй способ — на уже созданной странице в Page Designer в левом меню на закладке "Rendering" (она там первая) найти ветку "Regions", кликнуть на ней правой кнопкой и выбрать "Create Region". Далее нужно указать в группе свойств "Identification" на правой панели: "Title" — заголовок, под которым отчет будет отображаться на странице, и "Type" — "Classic Report"; в группе "Source": "Location" — "Local Database" (относительно новое поле, в 5.х его не было), "Type" — "SQL Query", "SQL Query" — собственно запрос. Этого достаточно.


SQL запрос, на котором построены дальнейшие примеры и КДПВ

Создание таблицы:


create table students as
select id, name, surname, 
       trunc(sysdate - dbms_random.value(18 * 365 - 180, 18 * 365 + 180)) birthdate, 
       trunc(sysdate - 8) examdate, 
       trunc(50 + dbms_random.value * 50) rating
  from (select 1 id, 'Маша' name, 'Иванова' surname from dual union all
        select 2 id, 'Петя' name, 'Балалайкин' surname from dual union all
        select 3 id, 'Коля' name, 'Водкин' surname from dual union all
        select 4 id, 'Вася' name, 'Петров' surname from dual union all
        select 5 id, 'Катя' name, 'Медведева' surname from dual union all
        select 6 id, 'Ира' name, 'Сидорова' surname from dual union all
        select 7 id, 'Виктор Степанович' name, 'Победоносцев' surname from dual);

Запрос для отчета:


 select id, name, surname, birthdate, examdate, rating, rn, 
        case when rn <= 3 then 'highrating' else '' end color,
        apex_item.checkbox(1) chbx
   from (select id, name, surname, birthdate, examdate, rating, 
                row_number() over (order by rating desc) rn
           from students)
  order by id

После того, как вы ввели текст запроса, апекс его самостоятельно распарсит и обнаружит столбцы. Настройки региона с отчетом в панели слева будут выглядеть так:


Настройки отчета


Здесь есть три основных раздела:


  • раздел, названием которого является "Title" региона: там настраиваются свойства региона;
  • "Columns": список столбцов, для каждого доступны свои настройки;
  • "Attributes": свойства самого отчета, то есть конкретно таблички с данными (не знаю, кто как, а я поначалу долго путался, где свойства региона, а где — отчета; в частности, и регион, и отчет имеют каждый свой "Template", и если вы "Template" поменяли, а вид отчета/региона не изменился, проверьте, не перепутали ли вы их друг с другом).

Базовые возможности Classic Report:


  • Выравнивание (по левому краю, по центру, по правому краю) заголовков столбцов и данных в столбцах, задание маски формата для чисел и дат;
  • Сортировка: если указать в свойствах столбца "Sortable" — "Yes", то на странице заголовок этого столбца будет представлен ссылкой, по клику на которую будет происходить сортировка. Вы можете сортировать данные либо добавлением "Order by" в запрос, либо настройкой сортировки в свойствах столбцов, но нельзя делать и то, и другое вместе;
  • "Pagination": можно указать количество строк на страницу ("Attributes" > "Number of Rows") и настроить вид селектора страницы (несколько вариантов на выбор). Также обратите внимание на параметр "Maximum Rows to Process": он задает, сколько строк запроса попадет в итоге в таблицу. Все сортировки, "Pagination" и прочее будет применяться к этому количеству строк. Если ваш запрос, как предполагается, может/должен возвращать больше, увеличьте это число;
  • "When No Data Found": когда запрос возвращает 0 строк, таблица не показывается вообще (даже заголовок, это особенность движка), а в этом разделе можно задать сообщение, которое будет выведено вместо таблицы с результатом;
  • Выгрузка данных в CSV: настраивается в разделе "Download".

У столбцов есть поле "Type", в котором можно выбрать один из нескольких вариантов отображения результата:


  • "Plain Text": это вариант по умолчанию — просто результат из запроса.
  • "Plain Text (based on List of Values)": если у вас в результатах запроса есть поле, ссылающееся на таблицу-справочник, можно подставить значение из справочника с помощью этого поля (но лучше, конечно, просто приджойнить его в самом запросе).
  • "Link": позволяет сделать ссылку. О ссылках будет отдельная статья.
  • "Display Image" и "Download BLOB": эти типы позволяют получить доступ к файлам, которые хранятся в таблице в столбце типа BLOB. Первый отображает файл в виде картинки (если это картинка, конечно), а второй — отображает ссылку для скачивания.
  • "Percent Graph": отображает число от 0 до 100 как картинку с прогресс-баром.
  • "Hidden Column": используется, когда значение из запроса — это что-то вспомогательное (например, для форматирования), но на экране отображаться не должно.

Дополнительное форматирование данных


Форматирование с помощью свойств столбцов

Рассмотрим пример, в котором у нас есть список учащихся, сдающих некий экзамен (SQL запрос был приведен выше). У нас есть табличка с результатами, и мы хотим выделить три лучших результата красным цветом и жирным шрифтом. На помощь нам приходят настройки столбца "Column formatting". Когда апекс рендерит отчет, он создает HTML таблицу, и в ее ячейки (<td></td>) помещает значения из результатов запроса. Свойство "HTML Expression" позволяет задать вместо этого произвольный HTML код. Если оно пустое, апекс поместит внутрь тега td значение из запроса, а если непустое — то содержание свойства. В выражении "HTML Expression" можно использовать ссылки на значение какого-либо столбца запроса, для этого нужно заключить его название между двух символов #. В запросе в моем примере есть столбцы "RATING" (результат, который нужно показать), и "COLOR" (в нем вычисляется имя CSS-класса, который будет задавать стиль подсветки, в зависимости от значения столбца "RATING"). В свойстве "HTML Expression" столбца "RATING" нужно написать:


<span class="#COLOR#">#RATING#<span>

А сам CSS-класс highrating можно описать в свойствах страницы ("CSS" > "Inline"):


.highrating {
  color: red;
  font-weight: bold;    
}

Всё готово. Если теперь открыть страницу, вы увидите выделенные красным три самых высоких результата.


И конечно, данную задачу можно решить множеством альтернативных способов. Например, не использовать CSS-классы, а вычислить в SQL запросе цвет и вставить его в атрибут style, или описать нужный CSS-класс в шаблоне страницы или отчета. Существует множество вариаций, выбирайте, кому что ближе.


Примечание: чтобы скрыть все дополнительные столбцы, используемые для форматирования или каких-то промежуточных вычислений, в свойствах столбца "Identification" > "Type" выберите "Hidden Column".


Форматирование путем изменения шаблона

У форматирования с помощью свойств столбца есть один недостаток: никак нельзя повлиять на формат самой ячейки, то есть тега td. Из-за этого, например, не получится сделать полную заливку ячейки каким-либо цветом (потому что в стандартных апексовых CSS-файлах у ячейки таблицы ненулевой padding, и внутри будет белая рамка).


Возьмем тот же пример, что и выше, и добавим выделение желтым цветом для строк с тремя самыми лучшими результатами.


Для этого нужно сделать следующие шаги:


  1. Отправляемся в "Shared Components" > "User Interface" > "Templates", в появившемся списке шаблонов кликаем на заголовок столбца "Type", и в появившемся списке значений выбираем "Report". Теперь находим шаблон "Standard" (он используется по умолчанию) и в самом правом столбце списка ("Copy") кликаем на кнопку, чтобы создать копию шаблона. В появившемся окне даем шаблону название, например, "Standard with highlight".
  2. Открываются свойства шаблона. На закладке "Column Template" в поле "Column Template 1" содержится что-то вроде такого:


    <td class="t-Report-cell" #ALIGNMENT# headers="#COLUMN_HEADER_NAME#">#COLUMN_VALUE#</td>

    Это шаблон ячейки в том виде, как он используется сейчас. На этой закладке можно задать до четырех альтернативных шаблонов ячейки, выбор между которыми будет осуществляться в зависимости от результата вычисления условия каждого из шаблонов. У нас в запросе есть поле RN — порядковый номер результата (то есть лучший результат имеет номер 1, следующий — 2, и так далее), здесь мы будем использовать его. Первый шаблон (по условию задачи) должен применяться только к строкам, в которых значение столбца RN больше трёх. В свойствах шаблона в поле "Column Template 1 Condition" выбираем "Use Based on PL/SQL Expression", а в поле "Column Template 1 Expression" пишем:


    :RN > 3

    И остается только добавить альтернативный шаблон (в поле "Column Template 2"):


    <td class="t-Report-cell highlighted" #ALIGNMENT# headers="#COLUMN_HEADER_NAME#">#COLUMN_VALUE#</td>

    Здесь к стандартному классу ячейки (td) добавлен класс highlighted. Теперь осталось нажать на кнопку "Apply Changes".


  3. Возвращаемся в Page Designer. Находим наш отчет, в свойстве "Attributes" > "Appearance" > "Template" выбираем из списка наш новый шаблон "Standard with highlight".
  4. Как и в предыдущем примере, в свойствах страницы ("CSS" > "Inline") описываем наш новый класс highlighted:

.highlighted {
    background-color: lightyellow !important;
}

Сохраняем страницу, проверяем результат (он должен быть как на картинке в начале статьи).


Добавляем интерактива


События вида "клик на строке отчета"

Одна из часто встречающихся задач — определить номер строки, на которую кликнул пользователь. За события в браузере в апексе отвечают компоненты Dynamic Action (DA). К сожалению, несмотря на большое количество самых разных их видов, типового DA для обработки клика на строке отчета нет. Но его относительно просто сымитировать. В качестве примера сделаем Master-Detail отчет из двух Classic Report на основе отчета из предыдущего раздела.
Вторым отчетом здесь будет выступать таблица с номерами телефонов. По клику на первой таблице она будет обновляться и показывать номер телефона выбранного человека (причем без перезагрузки страницы!). Для этого отчета создадим еще один регион типа "Classic Report".


Запрос будет, например, такой
with phone as (
    select 1 id, '1684313' phone_num from dual union all
    select 2, '6843513' from dual union all
    select 3, '8916311' from dual union all
    select 4, '6843213' from dual union all
    select 5, '6513218' from dual union all
    select 6, '9561311' from dual union all
    select 7, '9843135' from dual)
select phone_num
  from phone
 where id = :P2_ROW_ID

Необходимые составляющие решения:


  1. Способ идентификации выбранной пользователем строки. Для этой цели подойдет столбец ID. К сожалению, если этот столбец пометить как скрытый (очень часто лишние айдишники не показывают пользователю, чтобы не перегружать интерфейс), он не будет рендериться движком вообще, и эти данные никак не будут присутствовать на странице. Но их можно добавить к существующему столбцу тем же способом, что использовался для форматирования вывода. Возьмем какой-нибудь столбец (например, первый из видимых) и в поле "Column formatting" > "HTML Expression" напишем:


    <span class="click" data-id="#ID#"></span>#NAME#

    Можно этот код и прямо в SQL запрос вставить, но лучше не надо

    Есть другой вариант сделать то же самое — добавить этот HTML код прямо в запрос:


    select id, '<span class="click" data-id="' || id || '"></span>' || name as name from ...

    Результат в целом будет тот же, но перестанет работать сортировка по полю name, потому что движок СУБД будет считать всю эту строку данными и сортировать по ней. А если делать через "Column formatting", то данные, по которым будет делаться сортировка, не будут содержать ничего лишнего.
    К тому же, в этом случае для вывода HTML кода в столбце вам придется отказаться от экранирования спецсимволов, а это еще и потенциальная дыра в безопасности.


  2. Идентификатор отчета (на случай, если на странице таких отчетов больше одного). У региона есть свойство "Advanced" > "Static ID", поместим туда значение my_rep. Значение этого свойства используется в HTML коде страницы. Элемент div, который содержит регион, получит атрибут id с этим значением. Если поле "Static ID" пустое, апекс будет использовать для атрибута id идентификатор региона из системных таблиц. На него лучше не полагаться, потому что при переносе приложения на другой сервер этот идентификатор может поменяться практически гарантированно изменится.
  3. Итем для временного хранения значения ID выбранной строки. Создадим итем P2_ROW_ID с типом "Hidden" (это название, как вы могли видеть, используется в запросе, возвращающем список телефонов).
  4. Далее нужно создать Dynamic Action. Открываем Page Designer, в левой панели на закладке "Dynamic Actions" находим ветку "Click", кликаем на ней правой кнопкой, выбираем в меню "Create Dynamic Action". Далее нужно заполнить следующие свойства DA:


    • "Identification" > "Name": какое-нибудь осмысленное имя, иначе вы потеряетесь в этих бесконечных "New", "New2", "New3" и т. д.;
    • "When" > "Selection Type": "jQuery Selector";
    • в появившемся далее поле "jQuery Selector": здесь можно указать селектор, который будет указывать на дополнительный невидимый span с идентификатором строки:


      #my_rep table.t-Report-report tr



Далее у этого DA создадим действия ("True Actions"):


  • Первое действие получает ID выбранной строки и сохраняет его в итем. Нужно установить следующие значения свойств: "Action" — "Set Value", "Set Type" — "JavaScript Expression", "JavaScript Expression" — следующий код:


        this.triggeringElement.querySelector('span.click').dataset.id

    В свойстве "Affected Elements" > "Selection Type" выбрать "Item(s)", в свойстве "Item(s)" указать итем P2_ROW_ID.


  • Второе действие отправляет на сервер значение итема. Свойства: "Action" — "Execute PL/SQL Code", "PL/SQL Code" — просто null;, "Items to Submit" — P2_ROW_ID (собственно, всё это действие нужно ради отправки значения итема на сервер — более простого способасделать это нет; как вариант, вместо этих двух Actions можно сделать одно с голым джаваскриптом внутри, который и значение вычислит, и на сервер отправит — и это будет даже не сильно сложнее).
  • Третье действие обновляет второй (Detail) отчет. Свойства: "Action" — "Refresh", "Affected Elements" > "Selection Type" — выбрать "Region", в "Region" выбрать второй регион с телефонами.

Примечание о Dynamic Action

В будущем я напишу отдельную подробную статью про DA и многие другие компоненты апекса начального уровня, чтобы любой новичок сразу понял, как и что из описанного далее работает, а пока представим, что читатель все это уже знает.


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


Изменение данных с помощью Classic Report

В апексе с древних версий существовал вид отчета, известный как Tabular Form (он и сейчас в принципе существует, хотя и помечен как Legacy), который позволял изменять данные в нескольких строках таблицы сразу. На смену ему пришел намного более мощный Interactive Grid. Здесь же я продемонстрирую, как из подручных средств можно собрать аналог теплого лампового Tabular Form. Возьмем ту же таблицу, что и в прошлом примере, и добавим возможность редактировать имя и фамилию человека.
Для редактирования данных, количество которых заранее неизвестно даже приблизительно, нам понадобятся пакеты APEX_ITEM и APEX_APPLICATION. Первый позволяет создавать любые итемы в рантайме (созданием, в данном случае, является генерация соответствующего HTML), а второй — получать доступ к данным, введенным в созданные в рантайме поля.
Рассмотрим на примере функции APEX_ITEM.TEXT (она возвращает HTML код простого текстового инпута). Вот как объявлена эта функция в документации:


APEX_ITEM.TEXT(
    p_idx         IN    NUMBER,
    p_value       IN    VARCHAR2 DEFAULT NULL,
    p_size        IN    NUMBER DEFAULT NULL,
    p_maxlength   IN    NUMBER DEFAULT NULL,
    p_attributes  IN    VARCHAR2 DEFAULT NULL,
    p_item_id     IN    VARCHAR2 DEFAULT NULL,
    p_item_label  IN    VARCHAR2 DEFAULT NULL)
    RETURN VARCHAR2;

Параметры начиная со второго описывают, что будет содержать HTML код: значение в поле для ввода, дополнительные атрибуты, ограничения на длину и т. п. Самый важный в нашем случае параметр — первый (он же единственный обязательный, остальные имеют дефолтные значения). Этот параметр может принимать значения от 1 до 50, и потом введенное здесь число будет использоваться для доступа к данным, введенным пользователем. Каждому числу соответствует PL/SQL коллекция APEX_APPLICATION.G_FXX, где XX — это 01, 02,… 50.
Для нашей задачи нужны три поля таблицы: id, name, surname. Первое должно быть скрытым, второе и третье — полями для ввода.


  1. Создаем отчет на базе SQL запроса:


    select apex_item.hidden(1, id) || apex_item.text(2, name) name, 
             apex_item.text(3, surname) surname, birthdate, examdate
    from students

  2. В свойствах столбцов NAME и SURNAME убираем экранирование символов: в "Security" > "Escape special characters" ставим "No".
  3. Создаем кнопку "Save", настройки можно оставить по умолчанию.
  4. Создаем процесс "After Submit" со свойствами: "Identification" > "Type" — "PL/SQL"; "Server-side Conditions" > "When Button Pressed" — кнопка "Save". И код для сохранения данных в БД:


    begin
      for i in apex_application.g_f01.first .. apex_application.g_f01.last loop
        update students
           set name = apex_application.g_f02(i),
               surname = apex_application.g_f03(i)
         where id = apex_application.g_f01(i);
      end loop;
    end;


Как видите, выглядит совсем просто. Осталось только упомянуть один неприятный подводный камень в работе с APEX_ITEM. Если вы таким образом хотите добавить в отчет чекбоксы:


select ... apex_item.checkbox2(4, id) from students

То у вас в коллекции APEX_APPLICATION.G_F04 окажется столько элементов, сколько чекбоксов отмечено, в то время как во всех остальных коллекциях число элементов будет равно числу строк, отображенных на экране, а в содержимое коллекции попадет значение, которое вы передаете вторым параметром в функцию apex_item.checkbox2. Таким образом, если вы используете коллекцию с данными из чекбоксов в коде, аналогичном приведенному выше, вы можете столкнуться с исключением NO DATA FOUND.


Interactive Report (IR)


Interactive Report — это готовый к применению многоцелевой комбайн, в котором из коробки есть огромное количество функций для работы с данными. Все, что нужно, это включить их в настройках (точнее, не отключать, потому что по умолчанию там почти всё включено). На странице IR выглядит почти как обычный отчет, только над ним есть еще дополнительная панель с полем ввода для поиска и кнопкой "Actions". Кнопка "Actions" показывает дополнительное меню, из которого доступны все остальные функции. Я только вкратце перечислю возможности:


  • Панель поиска: позволяет искать заданный текст во всех столбцах отчета;
  • "Actions" > "Columns": настройка отображения столбцов (порядок следования, скрыть/показать);
  • "Actions" > "Filter": инструменты фильтрации данных, позволяют выбрать поле, операцию фильтрации (больше, меньше, равно, содержит текст, и т. д.);
  • "Actions" > "Data": манипуляции с данными (сортировка, вычисления, агрегирование и Flashback);
  • "Actions" > "Format": настройка отображения данных; позволяет подсвечивать нужные данные (настраиваются цвета подсветки и условия на данные), делать Control Break (не знаю, как это называется по-русски, выглядит это так), выбирать число строк отчета на странице;
  • "Actions" > "Chart", "Group by", "Pivot": названия говорят сами за себя — построение диаграмм, графиков, сводных отчетов на основе данных IR;
  • "Actions" > "Report": позволяет конечному пользователю сохранить отчет с определенным именем. В этом случае сохранятся все сделанные настройки из всех пунктов меню, описанных выше, и у каждого пользователя они будут свои;
  • "Actions" > "Download": экспорт данных, поддерживаются 5 форматов: CSV, PDF, XLS, HTML, RTF.

Что из перечисленного выше будет доступно пользователю, настраивается в свойствах отчета: "Attributes" > группы свойств "Search Bar", "Actions Menu", "Download".
К Interactive Report применимо практически все то же самое, что и к Classic Report. Точно так же можно делать собственное форматирование столбцов, использовать HTML элементы в столбцах (отключая при этом экранирование спецсимволов). Также есть дополнительный тип столбца — "Remove HTML", который убирает HTML теги из результата запроса (я лично ни разу не сталкивался с тем, чтобы это было нужно).
Но есть пара нюансов, на которые стоит сразу обратить внимание.
Во-первых, у IR в атрибутах отчета нет свойства "Template". То есть если вы разрабатываете своё собственное оформление, CSS-стили и прочее, то вы в пролете — IR будет выглядеть только так, как это заложено в него изначально. Выбирать придется из того, что есть. (Точнее, способ все-таки есть, но такой окольный, что проще считать, что его нет.)
Во-вторых, очень коряво устроена настройка порядка следования столбцов. Поле "Sequence" у столбцов есть, но оно не работает. Чтобы изменить порядок столбцов, вам придется сделать следующее: создать отчет, запустить страницу с ним, открыть меню "Actions" > "Columns" и настроить порядок через него. Далее нужно сохранить: там же в "Actions" > "Report" выбрать "Save Report", а в появившемся окне выбрать тип "Primary" — тогда текущий вид отчета сохранится для всех пользователей как основной (а чтобы тип "Primary" был доступен, вы должны быть залогинены в IDE как разработчик и запустить страницу из IDE).


Interactive Grid (IG)


Interactive Grid — это логическое продолжение Interactive Report. Впервые он появился в версии 5.1. IG выглядит почти как IR, но в дополнение он имеет режим редактирования, а также несколько дополнительных возможностей. Еще IG имеет богатое javascript API, но публичным оно стало только совсем недавно (версии с 18.2, кажется). По этой причине я не успел с ним достаточно хорошо ознакомиться.


Как мы жили в те тяжелые времена, которые наконец-то закончились

До публикации документации приходилось довольствоваться только циклом из четырех статей (раз, два, три, четыре), основанных, как я понимаю, на результатах реверс-инжиниринга в браузерном отладчике. Вся официальная документация для версии 5.1, с которой я работал больше всего, выглядела вот так:


Оффдокументация JS API IG


(ссылка)


Возможно, вы будете смеяться (а мне тогда было не смешно), но даже эта единственная документированная строчка кода была неправильной. Чтобы добавить строку к отчету (код делает именно это), вместо .invoke( "add-row" ) нужно было написать .invoke( "row-add-row" ).
Но теперь-то, надеюсь, заживем.


А вообще, я не люблю Interactive Grid, и вот послушайте, почему

Первая причина изложена в предыдущем спойлере. Она уже частично недействительна (раз уж документация вышла), хотя версии 5.х по-прежнему остаются недокументированными, а документацию из более поздних версий нужно использовать осторожно. Вряд ли там будет написано, что именно появилось нового по сравнению с предыдущими версиями. Ну и вообще — "осадочек остался".


Вторая причина (общая с IR) — это архитектурная кривизна. Для настройки (и сохранения настроек) порядка столбцов нужно запустить страницу с отчетом, настроить всё на странице и потом сохранить отчет через интерфейс. В IDE по-прежнему настроить нельзя. Конкретно в IG появилась и другая проблема: там добавили возможность менять ширину столбцов мышкой, но если вы поменяете ширину одного столбца, ширина всех остальных столбцов тоже изменится, причем совершенно непредсказуемым образом. Например, если у вас 5 столбцов и вы уменьшаете ширину первого, остальные четыре станут шире, каждый по чуть-чуть. А если вам нужно сделать так, чтобы первые три были одинаковые, а остальные два — как получится, на все оставшееся место, то лучше забудьте сразу, мышью это не сделать (это можно сделать через меню "Actions" > "Columns": там можно тупо вбить руками количество пикселей).


И третье — это глючность. Может, в последующих версиях это и пофиксили, но в 5.1 я несколько раз натыкался на баги IG. Один раз IG перестал сохранять данные, введенные в режиме редактирования, и несколько раз — не сохранял настройки отчета через меню "Actions" > "Report" > "Save". В каждом случае помогало только пересоздание отчета с нуля. При активной разработке напарываться раз в месяц на такой баг — это много, по-моему.


Создание столбцов не через SQL

Если в Classic Report или в Interactive Report столбцы создавались только на основе SQL запроса (столбцов в отчете может быть ровно столько, сколько их есть в SQL запросе), то в IG кроме этих столбцов можно создать дополнительные. Для этого нужно кликнуть правой кнопкой на списке столбцов в левой панели и выбрать пункт "Create Column". После этого появится столбец, которому можно в свойстве "Source" > "Type" указать "None" (и тогда он будет полностью независим от SQL запроса, на базе которого создан отчет), или указать "Database Column" / "SQL Expression" и заполнить его данными на основе результатов запроса. Это удобно, потому что раньше иногда приходилось делать в запросе дубликат столбца с дополнительными вычислениями. И неудобно при отладке, если злоупотреблять (придется выяснять сначала, какой столбец откуда берется), поэтому лучше не злоупотреблять.


Новые типы столбцов

Новых типов столбцов (по сравнению с IR) добавили очень много. Новые типы:


  • "Actions Menu". Столбец такого типа будет выглядеть как кнопка с иконкой-"бутербродом" (три горизонтальные полоски — кажется, это называется "бутерброд"?). Эта кнопка активирует меню с действиями над строками — добавить, удалить, сделать копию строки и т. д.
  • "Checkbox". Столбец будет показывать чекбоксы, но функционально это будут не такие чекбоксы, как в случае со столбцом типа "Row Selector" (см. ниже). Про то, что из себя представляют чекбоксы в апексе, я расскажу отдельно (там можно много чего рассказать), пока только скажу, что чекбоксы — это не то, чем они кажутся.
  • "Color picker". Натурально, выбор цвета, почти как в фотошопе. Я даже скриншот вам покажу:

Color picker screenshot


  • "Row Selector". Столбец этого типа содержит чекбокс, которым можно выделять строки, а потом, при необходимости, к этим строкам можно обращаться в javascript или PL/SQL коде.
  • остальные ("Date picker", "Number field", "Password", и куча других) — это дополнительные настройки для ввода данных в режиме редактирования. То есть вместо создания таких полей вручную через пакет APEX_ITEM (как это было в примере для Classic Report) теперь можно указать нужный тип столбца.

При этом почему-то из типов столбцов убрали "Download BLOB". То есть ссылку на файл не получится сделать так же просто, как это можно было в Interactive Report и даже в Classic Report. И вот тут я не понял — кому этот тип мешал? (Этот пункт тоже можно добавить в раздел "почему я не люблю IG".)


Master-Detail отчеты на основе IG

С IG стало намного проще делать Master-Detail отчеты. Не нужно ничего из того, что описано для Classic Report выше. Код писать больше не нужно, достаточно создать два IG отчета. Далее в Master отчете выбираем столбец, являющийся первичным ключом, и указываем у него это свойство (свойства столбца > "Source" > "Primary Key" указываем "Yes"). В Detail отчете в свойствах отчета находим "Master-Detail" > "Master Region" и в выпадающем списке выбираем нужный регион. Потом находим столбец, который является внешним ключом, в свойствах столбца находим "Master-Detail" > "Master Column" и выбираем из списка нужный столбец. Теперь всё должно заработать. При клике на строке Master отчета данные в Detail отчете будут обновляться.


Обработка ввода данных в IG


И последнее, что хотелось бы сказать об IG — как сделать дополнительную обработку данных, введенных в режиме редактирования. Из коробки IG умеет только вставлять новые строки или изменять/удалять существующие. Теперь, допустим, нужно сделать отчет (на базе таблицы STUDENTS из примера выше) с возможностью редактирования данных и ввода новых строк, а также сделать дополнительную проверку данных: когда пользователь вводит имя и фамилию, нужно убедиться, что первая буква — прописная, а остальные — строчные. Для этого нужно создать IG отчет на базе запроса к таблице STUDENTS:


select id, name, surname, birthdate, examdate
    from students

Сделать его редактируемым ("Attributes" > "Edit" > "Enabled"), потом зайти на закладку "Processing", найти там созданный апексом процесс для сохранения данных IG (по умолчанию он будет называться примерно так: "IG — Save Interactive Grid Data"), и в свойстве "Settings" > "Target Type" указать "PL/SQL Code", а в появившемся поле "PL/SQL Code to Insert/Update/Delete" написать следующий код:


begin
  :NAME := initcap(:NAME);
  :SURNAME := initcap(:SURNAME);
  case :APEX$ROW_STATUS
    when 'I' then
      insert into students (name, surname, birthdate, examdate)
      values (:NAME, :SURNAME, :BIRTHDATE, :EXAMDATE);
    when 'U' then
      update students
         set name  = :NAME,  
             surname = :SURNAME,
             birthdate = :BIRTHDATE,
             examdate = :EXAMDATE
       where id = :ID;
    when 'D' then
         -- 'D' означает удаление, в случае удаления строк 
         -- тоже какой-то код выполнить можно
     end case;
end;

Этот код будет выполнен для каждой строки отчета. Специальная переменная APEX$ROW_STATUS при этом содержит значения I, U или D (insert, update, delete) в зависимости от того, что происходит с текущей строкой (добавляется новая, изменяется/удаляется старая). Чтобы обратиться к данным столбцов, необходимо следующее (апекс делает это автоматически, вмешиваться практически не нужно): когда вы создаете отчет и указываете имя столбца в SQL-запросе, это имя будет указано потом в качестве источника данных в свойствах столбца отчета, и его же потом нужно использовать в коде выше для получения доступа к обновленным данным. В данном случае, используются столбцы NAME, SURNAME, BIRTHDATE и EXAMDATE.
Также при необходимости можно использовать переменную APEX$ROW_NUM, которая содержит порядковый номер строки, обрабатываемой в данный момент, и APEX$ROW_SELECTOR, которая содержит значение X для строк, выделенных с помощью чекбоксов в столбце типа "Row Selector".

Комментарии (4)


  1. Spartak13
    17.06.2019 21:27

    Хорошая, подробная статья.
    Не скажу, что все в ней было новое (для меня оказались интересными возможности IG), но за добротную русскую справку спасибо.
    Сам же APEX по скорости развития сильно отстаёт от передового HTML. Что-то простое сделать элементарно, но малейшее отклонение от канона сильно бьёт по пальцам.
    Взять тот же master-detail. Реализация его на одной странице «из коробки» появилась только в 5 версии. До этого приходилось городить хитрые dynamic actions.


    1. StrangerInTheKy Автор
      18.06.2019 18:25

      Реализация его на одной странице «из коробки» появилась только в 5 версии.
      Не совсем так. Master-Detail был всегда, еще в 3-й версии (хоть я с ней и не работал), в пятой появился Master-Detail без перезагрузки страницы. Кстати, я совсем забыл упомянуть об этом в тексте, надо будет поправить :(


      1. Spartak13
        18.06.2019 18:45

        Master-detail был и раньше, не спорю. И даже с визардом. Но сделан был коряво, например, в 4 версии (ее я помню лучше) генерировал две страницы: одну для master, другую для detail.
        При этом можно было сделать и на одной странице, с динамическим обновлением, но основательно поупражнявшись с DA. И да, только на classic report'e, интерактивный репорт мог быть только один на страницу


  1. StrangerInTheKy Автор
    18.06.2019 18:25

    -