[Из песочницы] Oracle APEX: Interactive Report with Checkboxes
что читатель имеет базовые знания о среде разработки APEX и БД Oracle.
Отчет с возможностью отметки отдельных записей для обработки — вещь очень удобная, но к сожалению до сих пор не реализованная «из коробки». В APEX API имеется функция apex_item.checkbox2 которая позволяет показать в отчете чекбоксы, но обрабатывать их приходится программисту. Обработку сильно осложняет то, что в многостраничном отчете чекбоксы существуют только для текущей страницы, и при переходах между страницами отчета значения отмеченных чекбоксов нужно где-то сохранять. В данном случае для этого используется коллекция APEX.
Несколько слов о коллекции APEX. Коллекция APEX — это именованная структура данных, существующая в пределах пользовательской сессии, предназначенная для манипуляций с данными при построении сложных форм ввода данных, мастеров и пр. Коллекция напоминает своей структурой таблицу из 50 атрибутов типа VARCHAR2(4000), 5 атрибутов типа Number, 5 атрибутов типа Date, 1 атрибута типа XML, 1 атрибута типа BLOB и 1 атрибута типа СLOB. Работают с коллекцией через PL/SQL API APEX_COLLECTION.
Итак, на страничке будем использовать такие хитрости и трюки:
- чекбоксы в отчет добавим при помощи функции apex_item.checkbox2.
- изменение состояния чекбокса будем обрабатывать при помощи javascript функции, вызываемой из Dynamic Action.
- сохранять состояние чекбоксов будем в коллекции APEX, для этого будем вызывать AJAX-ом OnDemand процедуру записи в коллекцию.
Будем считать, что у нас уже есть страничка с интерактивным отчетом на базе запроса:
select id, name, descr from geo
где id — поле первичного ключа таблицы. Его мы будем передавать в качестве атрибута чекбокса в коллекцию, и его будем использовать при обработке отмеченных записей.
П.1 Создадим процесс на странице:
Create → Page Component → Process →
Process type: PL/SQL code
Name: AJAX_UpdateChBoxCollection
Point: Ajax Callback
PL/SQL Page Process заполняем следующим кодом:
declare
l_value varchar2(4000);
l_seq_id number := 0;
seq number := 0;
l_collection_name constant varchar2(30) := 'CHBOXCOLL';
begin
------------------------------------------------------------------
-- Get the value of the global var which was set by JavaScript
------------------------------------------------------------------
l_value := apex_application.g_x01;
------------------------------------------------------------------------
-- If our collection named doesn't exist yet, create it
------------------------------------------------------------------------
if apex_collection.collection_exists( l_collection_name ) = FALSE then
apex_collection.create_collection( p_collection_name => l_collection_name );
end if;
---------------------------------------------------------------------
-- See if the specified value is already present in the collection
---------------------------------------------------------------------
for c1 in (select seq_id
from apex_collections
where collection_name = l_collection_name
and c001 = l_value) loop
l_seq_id := c1.seq_id;
exit;
end loop;
-------------------------------------------------------------------
-- If the current value was not found in the colleciton, add it.
-- Otherwise, delete it from the collection.
-------------------------------------------------------------------
-- Htp.Prn('Seq:'||l_seq_id);
if l_seq_id = 0 then
begin
seq := apex_collection.add_member( p_collection_name => l_collection_name,
p_c001 => l_value );
-- Htp.Prn(' Set:'||l_value||' seq_id:'||seq);
end;
else
begin
apex_collection.delete_member( p_collection_name => l_collection_name,
p_seq => l_seq_id );
-- Htp.Prn(' Rst:'||l_value);
end;
end if;
commit;
end;
Эта процедура создает коллекцию с именем «CHBOXCOLL» если она еще не была создана,
и добавляет/удаляет запись в коллекцию для изменившего состояние чекбокса. Название коллекции везде пишется капсом. Оно должно быть уникальным. В результате в коллекции будут атрибуты отмеченных записей.
П.2 Далее добавим на страницу код javascript функции который будет вызывать этот процесс:
Edit Page → JavaScript → Function and Global Variable Declaration
function ajax_call_func(val)
{ apex.server.process( "AJAX_UpdateChBoxCollection",
{ x01: val},
{ dataType: "text",
success: function( pData )
{ console.log(pData); }
}
);
};
Эта функция вызовет ранее нами созданную функцию с именем «AJAX_UpdateChBoxCollection» и передаст ей значение «val» через глобальную переменную apex_application.g_x01.
Результат типа «text» может быть возвращен из процедуры «AJAX_UpdateChBoxCollection» при
помощи функции Htp.Prn ('какой-то результат').
П.3 Создадим чекбоксы в отчете, для этого отредактируем запрос следующим образом:
select apex_item.checkbox2(p_idx => 1,
p_value => id,
p_attributes => 'class="chbox_UpdColl"',
p_checked_values => a.c001) cbox,
id,name,descr
from geo,apex_collections a
where a.c001 (+)= id
and a.collection_name (+)= 'CHBOXCOLL'
В данном случае в запрос добавляется колонка на базе функции apex_item.checkbox2,
, а также наша коллекция, связанная с нашей таблицей по id. Коллекция нам нужна лишь для получения состояния чекбокса при первоначальной загрузке страницы и при переходе между страницами отчета. Тип колонки с чекбоксами должен быть «Standart report column».
Коротко опишу используемые параметры apex_item.checkbox2:
p_idx => 1
номер переменной APEX_APPLICATION 1 соответствует F01; 2 — F02 и тп.
p_value => id
значение, которое будем передавать в коллекцию, в данном случае это поле «id» таблицы
p_attributes => 'class=«chbox_UpdColl»'
HTML атрибуты, в данном случае используется тэг class=«chbox_UpdColl» который потом используется в качестве jQuery selector в Dynamic Action.
p_checked_values => a.c001
использует значение из коллекции для указания состояния чекбокса
Для наглядности добавим еще один отчет на базе Sql запроса, который назовем Collecton и который будет показывать нам содержимое коллекции:
select a.seq_id,a.c001
from apex_collections a
where a.collection_name (+)= 'CHBOXCOLL'
П.4 Создадим Dynamic Action который будет срабатывать по изменению состояния чекбокса и вызывать созданную в П.3 JavaScript функцию:
Event: Change
Selection Type: jQuery Selector
jQuery Selector: .chbox_UpdColl
Condition: none
Action: Execute JavaScript Code
Fire When Event Result Is: True
Fire On Page Load: False
Code:
var
$checkBox = $(this.triggeringElement);
ajax_call_func( $checkBox.val() );
В поле jQuery Selector записывается значение атрибута 'class' из параметров функции apex_item.checkbox2 в П.3. Перед ним нужно обязательно поставить точку.
В созданный Dynamic Action добавим еще одно действие — обновление отчета коллекции после нажатия на чекбокс:
Action: Refresh
Selection Type: Region
Region: Collecton
Функционал чекбоксов уже работает, остается только добавить обработчик отмеченных чекбоксами записей. Пример такого обработчика с последовательным извлечением данных из коллекции при помощи курсора и записью их в файл собираюсь оформить в виде отдельной статьи.
Основная идея почерпнута здесь. Автор сохраняет отмеченные чекбоксы списком в Item на странице.
Недостаток — максимальная длина хранимого списка — 4000 символов. Использование коллекции в нашем случае снимает это ограничение.
Про использование Ajax Callback в APEX хорошо написано здесь.
Код тестировался на сайте apex.oracle.com на Application Express 5.1.0.00.45
Пожелания и конструктивная критика приветствуется.