Sunday 9 September 2012

APEX Report with checkboxes (advanced)

There will be many requirements to build a report with check boxes. Once the user selected all the rows, they would click submit and the application would process the rows. Sounds pretty simple and straight forward but there might be some extra requirements:

- There may be up to 10,000 records in the report

- When the user "scrolls" through the report (i.e. uses pagination), if they checked off a box it should remain checked the entire time (i.e. if they check an row in the 1st 15 rows, then view rows 16~30, then go back to rows 1~15 it should remain checked).

Below are the detailed steps on how to do it.

Step 1:

Create an application item F_EMPNO_LIST. Note you can use a page item as well...

Setup your report:

SELECT apex_item.checkbox (1,
                           empno,
                           'onchange="spCheckChange(this);"',
                           :f_empno_list,
                           ':'
                          ) checkbox,
empno, ename, job
FROM emp;

Once the report is setup set the row display to 5 (you'll need it for this example)

Step 2:

Add an HTML region and add the following code: (Note: the jQuery call is not needed... for now)

<script src="http://www.google.com/jsapi"></script>
<script>
  // Load jQuery
  google.load("jquery", "1.2.6", {uncompressed:true});

  function spCheckChange(pThis){
    var get = new htmldb_Get(null,$v('pFlowId'),'APPLICATION_PROCESS=CHECKBOX_CHANGE',$v('pFlowStepId'));
    get.addParam('x01',pThis.value); //Value that was checked
    get.addParam('x02',pThis.checked ? 'Y':'N'); // Checked Flag
    gReturn = get.get();
    
    $x('checkListDisp').innerHTML=gReturn;
  }
  
</script>
CHECKBOX List:

<div id="checkListDisp">&F_EMPNO_LIST.</div>

Step 3:
Now create an application process (on Demand) called: CHECKBOX_CHANGE
-- Application Process: CHECKBOX_CHANGE
-- On Demand...

DECLARE
  v_item_val NUMBER := apex_application.g_x01;
  v_checked_flag VARCHAR2 (1) := apex_application.g_x02;
BEGIN
  IF v_checked_flag = 'Y' THEN
    -- Add to the list
    IF :f_empno_list IS NULL THEN
      :f_empno_list := ':' || v_item_val || ':';
    ELSE
      :f_empno_list := :f_empno_list || v_item_val || ':';
    END IF;
  ELSE
    -- Remove from the list
    :f_empno_list := REPLACE (:f_empno_list, ':' || v_item_val || ':', ':');
  END IF;

  -- Just for testing
  HTP.p (:f_empno_list);
END;

Step 4:
On the post page create a query to view data (you can process how you need/want)

select *
from emp
where instr(:F_EMPNO_LIST, ':' || empno || ':') > 0

Then you can try out in your report checking the check boxes and paginating. Your previously checked ones in the report will still be checked once you paginate back.



   

q Function Inside a q Function:

Reference: http://www.talkapex.com/2009/03/q-function-escape-single-quotes.html 

Last month I wrote about how to escape single quotes in string using the q function:

I recently had an requirement where I wanted to use a q function inside another q function. Here's what I tried:

DECLARE
  l_code varchar2(4000);

BEGIN
  l_code := q'!BEGIN dbms_output.put_line(q'!It's cold in Calgary!'); END; !';
  
  dbms_output.put_line(l_code);
  
  execute immediate l_code;
  
END;
/

ERROR:
ORA-01756: quoted string not properly terminated
You'll notice that the above code doesn't run. The reason it doesn't work is that I'm using the same character (!) as the quote delimiter. To fix this, each "Q" block must have it's own unique quote delimiter. Below is a copy of the same code but using two different quote delimiters (# and !).
   
DECLARE
  l_code varchar2(4000);

BEGIN
  l_code := q'#BEGIN dbms_output.put_line(q'!It's cold in Calgary!'); END; #';
  
  dbms_output.put_line(l_code);
  
  execute immediate l_code;
  
END;
/

BEGIN dbms_output.put_line(q'!It's cold in Calgary!'); END;
It's cold in Calgary

PL/SQL procedure successfully completed.
For more information on the q function (it's real name is "Q-quote mechanism") read the Oracle documentation: http://docs.oracle.com/cd/B19306_01/appdev.102/b14251/adfns_sqltypes.htm#BABECADE and scroll down to the "Quoting Character Literals" section.

Recover deleted Page with APEX and Firebug

Today a very important Page of an important APEX Application has accidentally been deleted. We didn’t want to step back to yesterdays backup, because right before the deletion many changes took place and we didn’t want to code that again. Oracle APEX includes the really nice feature of exporting an Application “As of x minutes ago”.





But there is also a dialog where you can export a single page, without the whole application overhead:


But this dialog lists only currently existing Pages and doesn’t include our deleted Page.
Here comes our Firebug to the rescue using which we manipulate the HTML-Code of the Page-Select-List and add our deleted Page 99 which then exports easily “As of 30 minutes ago”.

1. Use Firebugs “Inspect Element” on the Page-Select-List

2. Edit the HTML and insert a new <option>





3. Pick the newly added entry from the select-list, enter 30 as number of minutes and press the export button




4. Go to the import menu and import the Page-Export File.




And there you go..... You can find your page which was accidentally deleted.





Sunday 12 August 2012

q Function in APEX: Escape Single Quotes:

Reference: http://www.talkapex.com/2009/03/q-function-escape-single-quotes.html 

Instead of writing out a long description here's an example:
   
DECLARE
  v_sql VARCHAR2 (255);
  v_result VARCHAR2 (255);
BEGIN
  v_sql := 'select ''hello'' into :a from dual';

  EXECUTE IMMEDIATE v_sql
               INTO v_result;

  DBMS_OUTPUT.put_line (v_result);
END;

Notice how I had to put 2 single quotes around "Hello" to escape the single quote characters?

Now using the q function I don't need to do that:
   
DECLARE
  v_sql VARCHAR2 (255);
  v_result varchar2(255);
BEGIN
  v_sql := q'!select 'hello' into :a from dual !';

  EXECUTE IMMEDIATE v_sql
               INTO v_result;

  DBMS_OUTPUT.put_line (v_result);
END;

Notice now how "Hello" is wrapped as it would appear if it were not in variable definition function?

This can save you a lot of time by avoiding having to escape single quotes in strings!

Saving Current Values with Cascading LOV's:

Say you've got two LOVs...STATES and CITIES. They both default to 'ALL' and 'ALL'. Since CITIES is dependent on STATES, as soon as STATES is changed, CITIES is blanked out. What should happen is that CITIES gets re-evaluated as in the following example... let's say STATES is ALL and CITIES is "Houston". If one then changes STATES to "Texas", CITIES should remain "Houston" as that is a valid value for CITIES.
So basically, is it possible to maintain the selected value of an item if that same value exists in the list of values after refreshing?

Click here to see the demo but continue reading to learn how it all works…

There are a three main events you need to be concerned with when it comes to cascading selects:
  • change
  • apexbeforerefresh
  • apexafterrefresh
The change event is a standard part of JavaScript and the DOM. This event fires when the user manually changes the value of the select list but can also be triggered programmatically via JavaScript. The apexbeforerefresh and apexafterrefresh events are custom events in the APEX framework. They fire just before and just after AJAX requests refresh something on the page. The events work with many items and regions that utilize this technology.

In this example we have two select lists: parent and child. If you change the value of the child select list then the change event will fire and that’s it. But if you change the value of the parent select list a lot more happens to the child select. Here are some of the highlights:
  1. The current LOV values are cleared out
  2. The apexbeforerefresh event is triggered
  3. An AJAX request brings back new values. This only happens if
    1. optimize refresh is set to false
    2. optimize refresh is set to true and all parent items are not null
  4. The apexafterrefresh event is triggered
  5. The change event is fired
Now, knowing all of this, how can we utilize the sequence of events to solve the original problem of keeping selected values? The answer lies in creating two dynamic actions.
The first dynamic action will store the current value of the select list so that we can access it later. This will typically happen when the change event fires but it will also happen when the page first loads. Here’s how to create the first Dynamic Action:
  1. Right click the child select list and select Creation Dynamic Action.
  2. Select Advanced.
  3. Click Next >.
  4. Enter a name for the first Dynamic Action.
  5. Click Next >.
  6. Set Event to Change.
  7. Click Next >.
  8. Set Action to Execute JavaScript Code.
  9. Set Code to: $(this.triggeringElement).attr('data-last-value', $(this.triggeringElement).val());
  10. Click Next >.
  11. Click Create.
The second Dynamic Action will take advantage of the apexafterrefresh event and access the previously stored value. That value will then be used to look through the new options and if a match is found it will be selected. Here’s how to create the second Dynamic Action.
  1. Right click the item (again) and select Create Dynamic Action.
  2. Select Advanced.
  3. Click Next >.
  4. Enter a name for the second Dynamic Action.
  5. Click Next >.
  6. Set Event to After Refresh.
  7. Click Next >.
  8. Set Action to Execute JavaScript Code.
  9. Set Code to: $(this.triggeringElement).children('option[value="' + $(this.triggeringElement).attr('data-last-value') + '"]').attr('selected','selected');
  10. Click Next >.
  11. Click Create.
So two lines of actual code, not bad at all! Hope this is useful....

Saturday 11 August 2012

Hidden option for Static List of Values in APEX:

Did you know that the STATIC and STATIC2 keyword used to define Static List of Values for Oracle Application Express (APEX) contains an undocumented option?

The online help documents the usage which is mentioned below
STATIC[2]:Display Value[;Return Value],Display Value[;Return Value]

But what to do if one of your values contains the semi colon or the comma in the text? You will get a problem with the predefined separators!

In such a case you can use

STATIC[2](lov-entries-sep,display-return-sep) : Display Value[<display-return-sep>Return Value]
<lov-entries-sep>Display Value[<display-return-sep>Return Value]

For example:
STATIC2(~,*):Cat, Dog*1~Nemo, Shark*2


Universal Theme - Side Navigation Menu Child Entry Icon Alignment

Hi, There is an issue with the Side Navigation Menu Child Entry Icon Alignment in Universal theme if you are using APEX 5.1 or prior vers...