Wednesday, March 25, 2009

Column Groups in APEX Interactive Reports

A while ago Dimitri Gielis helped us to display column groups in Interactive Reports (IR) in APEX. Please see his blog posting to get a background of how to enable column groups in Interactive Reports.



After some testing we've made several changes to the code. The updated code fixes a bug that would display multiple group headers. It does not load the group headers via AJAX so it speeds up response time. I've included the updated code below. The example of this is here.

Step 1: Application Process
Create an Application Process to load the IR colum group header information
Name: AP_GET_IR_COL_GROUPS
Point: On Load: After Header (page template header)


DECLARE
v_sql VARCHAR2 (500);
v_count PLS_INTEGER;
BEGIN
-- Need to ensure at least 1 col group exists
SELECT COUNT (column_group_id)
INTO v_count
FROM apex_application_page_ir ir, apex_application_page_ir_col c
WHERE ir.application_id = :app_id
AND ir.page_id = :app_page_id
AND ir.interactive_report_id = c.interactive_report_id;

-- Need to do join columns to IR rather than the page_id since columns that are added after IR was created need have a null page_id
v_sql := '
SELECT c.column_alias,
c.report_label,
c.column_group
FROM apex_application_page_ir_col c,
apex_application_page_ir i
WHERE i.application_id = :app_id
AND i.page_id = :app_page_id
AND i.interactive_report_id = c.interactive_report_id
AND c.column_group IS NOT NULL';
HTP.p ('');
END;


Step 2: Javascript Code
You can put this on Page 0 or just directly on the page with the Interactive Report


dispIRColGrpHeader=function(){
if(typeof(gPageIRColGrps) != "undefined"){
// retrieve the Interactive report table
var vTbl = $('.apexir_WORKSHEET_DATA');

// change the look and feel of the IR table
$(vTbl).attr("border","1");

// Prevent Duplicate rows
$('#irColGrpRow').remove();

// Add the Column Group row
$(vTbl[0].rows[0]).before('');

var vPrevColGrp = '';
var vColGrpExists = false;
var vColSpan = 1;
// Loop over the row headers and see if we need to add a column group.
for (var i = 0; i < $(vTbl[0].rows[1].cells).length; i++){
// For IR, the column headers have divs with id of apexir
vColId = '';
// Only set the col ID if it exists (needed for IR row_id icon)
if (typeof($('.apexir_WORKSHEET_DATA tr:eq(1) th:eq(' + i + ') div').attr('id')) != "undefined")
vColId = $('.apexir_WORKSHEET_DATA tr:eq(1) th:eq(' + i + ') div').attr('id').replace(/apexir_/, '').toUpperCase();
var vFoundColGrp = false; // This column has an associated column grp
var vColGrp = ''; // Current Column group

// Find the ID in the IR Groups global variable (genereated in AP)
for (var j = 0; j < gPageIRColGrps.row.length; j ++ ){
if (gPageIRColGrps.row[j].COLUMN_ALIAS.toUpperCase() == vColId) {
vFoundColGrp = true;
vColGrpExists = true;
vColGrp = gPageIRColGrps.row[j].COLUMN_GROUP;
break;
}//if
}// For IR Col Groups

// Only print the col group header for the previous entry. This allows us to set the col span for similar groups
// Have to do it this way to support IE (otherwise we could look at the previous entry and update it's col span

// If the current
if (vColGrp.length > 0 && vColGrp == vPrevColGrp){
// Don't display the
vColSpan = vColSpan + 1;
}
else if(i > 0) {
// Display the previous item
$('#irColGrpRow').append('' + vPrevColGrp + '');
vColSpan = 1;
}

// If this is the last item then display it
if (i == $(vTbl[0].rows[1].cells).length-1) {
$('#irColGrpRow').append('' + vColGrp + '');
}

vPrevColGrp = vColGrp;
}// For each column being displayed

// Remove the col group heading if no column groups:
if (!vColGrpExists)
$('#irColGrpRow').remove();
} // Column groups exist

} // dispIRColGrpHeader

// Once the page is finsihed loading run the following (which will activate the column grouping display).
$(document).ready(function(){

// If page has an IR then set up for column grouping
if ($('.apexir_WORKSHEET_DATA').length > 0) {

function dispIRColGroups(){
// Set the class for div hover since can't do in IE
$('table.apexir_WORKSHEET_DATA th div').mouseover(function(){
$(this).addClass('apexir_WORKSHEET_DATA_th_div_hover');
});
$('table.apexir_WORKSHEET_DATA th div').mouseout(function(){
$(this).removeClass('apexir_WORKSHEET_DATA_th_div_hover');
});

dispIRColGrpHeader();
// This time out is required since after the report is refreshed via AJAX, need to reattach the l_LastFunction command
setTimeout(
function(){
gReport.l_LastFunction = function(){dispIRColGroups();};
},
1000
);
}

gReport = new apex.worksheet.ws('');
gReport.l_LastFunction = function(){dispIRColGroups();}
dispIRColGroups();
}//if
});

15 comments:

Stew said...

Thanks for the update. This is pretty cool stuff.

Maurice said...

Cool stuff. Is this also possible for regular reports?

Martin Giffy D'Souza said...

Hi Maurice,

You could do this for regular reports, however you'd need some sort of mechanism to associate column groups with the columns.

dmcghan said...

Martin,

If you could, send me an email at dmcghan@gmail.com... I'm curious about your code.

Regards,
Dan

jlhotte said...

Hi,

Is there a way to also code a function that would identify when an IR has been filtered so I could trigger a refresh on a different report using javascript?

Thanks.

Martin Giffy D'Souza said...

Hi,

Yes you could probably do that. Do you want to trigger the report refresh when a filter is added via the column drop down, or when it is added via the IR tools menu?

And just to clarify, you want to refresh another non IR?

Martin

jlhotte said...

Hi,

Yes i have a mapviewer on my page that displays the items on a map and an IR at the bottom that displays the items in a list. If the user filters the list (either with the Go button or the filter option; or again removes a filter or changes the enabled/disable etc), i want to update the mapviewer to now only show the filtered items. I was able from one of your other post to see an example where you altered the function of the Go and onkeyup to track the filter but it did not apply to the other filtering tools.

Martin Giffy D'Souza said...

Hi,

So you want to do the following when a filter is applied (either through the tools option or through the column drop down list):

- Pass the filter values to your report region
- Refresh another report region (or some JS call) and apply the filter values to it.

Do you have an example on apex.oracle.com for this?

Martin

jlhotte said...

Yes,

I have already a JS function that loads the info in the map using jQuery and then loads the IR.

I need to track the event in the browser when the IR is refreshed so that I can call the JS function that load the map.

From your example, I was able to override the search function and at the end could reload the map but if the user does the filtering via the Filter option in the menu then it does not call the search function or when a filter is displayed and the user clicks on the icon to remove it, the report is refreshed but again Apex does not use the search function. I am trying to figure out which functions need to be overriden.

I have not seen any example for this anywhere.

Thanksé

Martin Giffy D'Souza said...

Hi,

You may need to dig around the APEX views for this one to find the filters that get applied to the users current IR. If you look at my code for downloading IR (http://apex-smb.blogspot.com/2009/04/custom-download-for-apex-interactive.html) I go over how you can find the current report that the user is looking at. With that information and with the APEX views you should be able to find the filters that the user has applied to their IR.

If you're still having problems with this I can look into it some more in a few weeks.

Martin

jlhotte said...

I think i have figured it out. Thanks again.

stonemaster said...

Martin, Is it possible for you to share the sample/example application. I have not been successful in make it work. Your help is appreciated.

Martin Giffy D'Souza said...

Hi,

I'm going to work on turning all my examples into a sample application over the next week or 2. I'll post a comment on this page, or just a blog entry about it.

Martin

Sam said...

Hi Martin,

I just came across your post and this feature of column groups is really useful for report that I need to create.

However it's not working for me.
I use apex 3.1.

I created the interactive report groups as per Dimitri's blog and then used your code.

STEP 1 :I created PL/SQL AP_GET_IR_COL_GROUPS on "On Load(After Header)" . I assume that this is the "page template header" (Or is it different) ?

STEP 2 :And then I added Javascript Code to Page"Footer Text".

When I run the page I get following javascript error in IE "Object Expect" at line "$(document).ready(function()".

No error in Netscape but it also does not show the grouping.

Kindly help.

Martin Giffy D'Souza said...

Hi Sam,

I think it could be because you're not using jQuery. Have you included it?


Martin