Saturday, December 10, 2011

Highlight Functionality in Apex

Ever pressed CTRL + F in the browser ? ... ( don't answer )

You noticed that when you search for a string within a web page, your browser automatically highlights all the occurrences of that string within the whole page. Recently I've been working a lot with Oracle Apex (Application Express) and I found myself in front of this problem. I needed to implement that highlight functionality in apex.
At first I supposed that the application should have this already implemented, some check-box ready to be checked and would allow the miracle to happen, but turns out that the apex guys did not get the whole job done.

Let's just start with the beginning...
You need to create a report region first which will have the Enable Search set to Yes.
You will also create a text-field, say P100_KEYWORD, in which you will have the text by which you will make the search throughout the region.
Would you want to highlight words within a column, you will have to go that column's details -> Column Formatting and insert the following text within the Highlight Words section: &P100_KEYWORD.
DO NOT FORGET ABOUT THE PERIOD (.) at the end of the definition, as this won't work without it.

Now, you can see apex is setting all the matches to bold and red. Nice functionality, you could say, but it's a bit far from what you would want to accomplish would you try to implement the browser search. For example, try searching: "ABC", then "Abc", then "AbC", etc. :) You'll notice that the highlight is case-sensitive and no, you cannot do anything about it. You are stuck with it. So when you will search for abc, be sure you mean to highlight only abc matches, not ABc, or other combinations of upper and lower cases.

After trying for different ways to accomplish this, I finally realized that once again I have to build my own tools. There are 2 ways to build this functionality: one is by using pl/sql and the other is by directly modifying the UI. For the sake of a standard way to do things, we'll use the same highlight effect that apex uses by default, meaning we'll set the font to color red, and apply bold style.

Method #1:
The idea is to apply the highlight on a specified column and to return the result already with the desired highlight effect. This method is inefficient because the function executes one time for each returned item so in the event that you have CLOBs that need to be highlighted then you'll have some problems with the time that the query consumes. But, could be useful if you don't have to search large quantities of text.


CREATE OR REPLACE FUNCTION HIGHLIGHT
(
  P_TEXT IN CLOB
, P_KEYWORD IN VARCHAR2
, P_COLOR IN VARCHAR2
) RETURN CLOB AS

l_vc_arr2   APEX_APPLICATION_GLOBAL.VC_ARR2;
str         CLOB := '';
pos         NUMBER := 0;

l_str       CLOB := p_text;
last_pos    NUMBER := 0;

BEGIN

  IF p_text IS NULL THEN
    RETURN NULL;
  END IF;

  IF p_keyword IS NULL THEN
    RETURN p_text;
  END IF;

  loop
    exit when length(trim(l_str)) = 0;

    -- find keyword
    pos := regexp_instr(l_str, p_keyword, 1,1, 0, 'i');

    -- remove the parsed text
    l_str := substr(l_str, pos + LENGTH(p_keyword));

    -- set real position
    pos := LENGTH(p_text) - LENGTH(l_str) + 1;

    -- construct the string
    str := str || substr(p_text, last_pos, pos-last_pos - length(p_keyword) -1 ) || '<font color="' || p_color || '"><b>' || substr(p_text, pos-LENGTH(p_keyword), LENGTH(p_keyword) ) || '</b></font>';

    -- set last position
    last_pos := pos;

  end loop;

  RETURN str;

END HIGHLIGHT;


I did not test this function and will not do it, since it's here just to help you get an idea of how to solve the problem. The rest is up to you.

Method #2:
This method is my favorite. Basically you need to write some javascript code that applies to current page only. So the other results that are currently not displayed just don't matter.
In your report region first set a unique identifier for the table, like for example REPORTS_TABLE.
Now, in the Region Footer section you can add the javascript code that will make the modifications on the page.

Get the keyword field:
var keyFilter = document.getElementById("P100_KEYWORD").value;

Get the number of rows on page:
document.getElementById("report_REPORTS_TABLE").rows.length;

Get the first cell of rows on page:
document.getElementById("report_REPORTS_TABLE").rows[r].cells[1].innerHTML;


Don't forget to embed you code within <script type="text/javascript"> </script>.


Good Luck!