Thursday, March 3, 2016

Tutorial: Creating a Component Similar to Super LOV Using Native Components

I had a lot of fun contributing to Super LOV. Unfortunately, it's not being maintained much these days and it doesn't work so well in APEX 5.0. But is it even needed in APEX 5?

In this post I'll show you how to create a custom component that's very similar to Super LOV, only we'll use a native component recipe that includes a Modal Page, an Interactive Report, some Dynamic Actions, and (you knew this was coming) some custom JavaScript. The result is pretty awesome! Here's a link to a demo if you'd like to see it in action.

The content in this tutorial is not as easy as just installing and using plug-in. I get that. However, there are lots of advantages to a custom solution like we'll be creating, here are a few:
  1. You can get the job done even when you're not allowed to use plug-ins (quite common in government agencies).
  2. You can do things that are not possible in plug-ins (like utilize modal pages and IRs).
  3. You're not limited to the features of the plug-in and can customize things as much as the requirements dictate.
  4. Finally, and perhaps most importantly, you may learn something new that helps you use APEX more effectively going forward.
Have I convinced you? :) Okay, here's an overview of what we'll be up to:
  1. Create a new application
  2. Add some basic components and customize them
  3. Add some custom JavaScript
  4. Create a modal page with an Interactive Report
  5. Configure the modal page
  6. Create a DA to respond to clicks in the modal page
  7. Create a DA to proxy values back to the item
If you don't already have an APEX 5 workspace, or if you'd rather use a new one just for testing, head to http://apex.oracle.com to request a free workspace. Also, I'll be using the DEMO_CUSTOMERS table for this demo. If you don't have that table, install the Sample Database Application from the packaged apps. Let's get started!

Create a new application


We'll kick things off by creating a new application. Log in to your workspace and navigate to the Application Builder, then follow these steps:
  1. Click Create (for a new application).
  2. Leave the default application type of Desktop and click Next >.
  3. Set Name to Native Super LOV Demo.
  4. Click Create Application to skip through the rest of the wizard.
  5. Click Create Application one last time on the confirmation page.
That should create the application and redirect you to the application home page.

Add some basic components and customize them


Next up, we'll add a new region to the home page with two items. One item will be used for the display value (text field) and the other for the return value (hidden).
  1. Click Home in the pages report to go to the Page Designer for that page.
  2. Drag and drop a Static Content region into the Content Body display point.
  1. In the properties panel on the right, set Title to Native Super LOV Demo.
  2. Drag and drop a Hidden item into the Items section new region.
  1. In the component panel on the left, click the new P1_NEW item to select it.
  2. In the properties panel, set Name to P1_CUSTOMER_ID.
  3. Set Value Protected to No.
  4. Drag and drop a Text Field item into the same region as before.
  5. Set Name to P1_CUSTOMER_DISPLAY.
  6. Set Label to Customer.
  7. Set Custom Attributes to the code below. The readonly attribute will keep users from working with the text field directly (they'll use the modal LOV) and the data-lov-search-js attribute is there so that the APEX engine will give us a proper link to the modal page when it's created.
readonly data-lov-search-js="f?p=&APP_ID.:2:&APP_SESSION.:CIR::::"
  1. Set Post Text to the code below. This will add the "clear" and "open LOV" buttons to the right of the text field.

  

  

  1. Click the Save and Run Page button, log in with your APEX workspace credentials, and you should see the following item:

Pretty snazzy, huh? :)

Add some custom JavaScript


In this next part we're going to add some JavaScript to the page that will make the buttons do stuff. Return to the Page Designer for page 1 and complete these steps.
  1. In the component panel on the left, click Page 1: Home (top node in the tree) to access the page properties.
  2. In the properties panel, set Execute when Page Loads to the code below. I've added some comments to explain what's going on.
// This registers an event listener on the clear button. The code 
// expects the element immediately before the clear button (an 'a' 
// tag) to be the display element. It also expects the display name 
// to end with "_DISPLAY" and the hidden/return item name to be the 
// same as the display item but end with "_ID" instead.

$('.lov-clear').on('click', function() {
  var $clear = $(this),
    $display = $clear.prev(),
    $id = $('#' + $display.attr('id').replace(/_DISPLAY$/, '_ID'));

  // Clear out the values in the display and return items and trigger
  // the change event in case anyone is listening for it.
  $display.val('').trigger('change');
  $id.val('').trigger('change');
});

// This registers an event listener on the open LOV button. It gets
// a function body from the "data-lov-search-js" attribute defined on 
// the display item. The function body is modified a little and then 
// invoked in the context of the display item (not the button that 
// was clicked).

$('.lov-open').on('click', function() {
  var $opener = $(this),
    $display = $opener.prev().prev(),
    funcBody = $display.data('lov-search-js')
      .replace(/^"javascript:/, '')
      .replace(/\"$/,'');

  new Function(funcBody).call($display.get(0));
});

That wasn't so bad was it? Don't try testing things just yet. You'll need to wait till after the next part for that.

Create a modal page with an Interactive Report


Alright, we are now ready to create the page that will serve as the list of values (LOV). This will be a modal page so we get that nice overlay effect when it's open. The data will be displayed using an Interactive Report which very powerful! Return to the application home page and follow along...

  1. Click Create Page >.
  2. Click Report.
  3. Click Interactive Report.
  4. On the Page and Region Attributes step, make sure Page Number is set to 2.
  5. Set Page Name to Select Customer.
  6. Set Page Mode to Modal Dialog.
  7. Click Next >.
  8. Click Next > to skip the Navigation Menu settings.
  9. On the Report Source step, make sure Page Number is set to 2.
  10. Set Table / View Name to DEMO_CUSTOMERS (table).
  11. Set Link to Single Row View to DEMO_CUSTOMERS (table).
  12. Click Next >.
  13. Click Create.
At this point you can run page 1 to test. Click the "open LOV" button and you should see the interactive report page open as a modal dialog. Things are coming along nicely!

Configure the modal page


Now we need to start configuring the modal page so that it functions like an LOV. We'll add some CSS so that hovering over the rows makes the cursor a pointer. Then we'll add some items that correspond to the 2 items on the first page for proxying purposes (relevant in the next 2 steps). Finally, we'll make some changes to the report so that it better meets our needs. Navigate to the Page Designer for page 2 and complete the following steps.

  1. In the component panel on the left, click Page 2: Select Customer (top node in the tree) to access the page properties.
  2. Set Inline (under CSS) to the code below.
.t-fht-tbody tr{
  cursor: pointer;
}
  1. Drag and drop a Hidden item into the Select Customer region.
  2. In the component panel, click the new P2_NEW item to select it.
  3. In the properties panel, set Name to P2_ID.
  4. Drag and drop another Hidden item into the Select Customer region.
  5. In the component panel, click the new P2_NEW (may be named P2_NEW_1) item to select it.
  6. In the properties panel, set Name to P2_DISPLAY.
  7. In the component panel, open the Columns of the Select Customer region, and click CUSTOMER_ID to select it.
  8. In the properties panel, set HTML Expression to the code below.
#CUSTOMER_ID#


  1. Set Static ID to customer_id.
  2. Set Hide (under Enable Users To) to No.
  3. Click Save in the upper right-hand corner to save your changes.

I recommend continuing through the end before running a final test...

Create a DA to respond to clicks in the modal page


Now we need to make it so that if a user clicks a row in the report two things happen. First, the hidden values from the row clicked must be proxied to the 2 new hidden items created in the last step. Once that's done we can just close the modal. If you're not already there, return to the Page Designer for page 2 and follow along.
  1. In the component panel, switch to the Dynamic Action tab.
  2. Right-click the Click event.
  3. Click Create Dynamic Action.

  1. In the properties panel, set Name to Row clicked.
  2. Set Selection Type to jQuery Selector.
  3. Set jQuery Selector to the code below. This jQuery selector selects table rows in the Interactive Report.
.t-fht-tbody tr
  1. Set Event Scope to Dynamic.
  2. In the component panel, click the Show action under the True events.
  3. In the properties panel, set Action to Execute JavaScript Code.
  4. Set Code to the code below. This JavaScript will locate the column in the row with the hidden values and copy the values to the hidden items.
var columnStaticId = 'customer_id';
var displayTd = $(this.triggeringElement).closest('tr')
  .find('td[headers=' + columnStaticId + ']');
var displayVal = displayTd.find('.display-val').val();
var returnVal = displayTd.find('.return-val').val();

$s('P2_ID', returnVal);
$s('P2_DISPLAY', displayVal);
  1. Set Selection Type to - Select -.
  2. Set Fire On Page Load to No.
  3. In the component panel, right-click True above the existing true action.
  4. Click Create TRUE Action.
  5. In the properties panel, set Action to Close Dialog.
  6. Set Items to Return to P2_DISPLAY,P2_ID.
  7. Click Save in the upper right-hand corner to save your changes.
At this point, if you click a row in the LOV, you should see the modal page close. We're getting close!

Create a DA to proxy values back to the item


In this last step we'll be creating a Dynamic Action on the home page that responds to the modal closing. We'll grab the values passed from the event and proxy them to our custom LOV items. Navigate to the Page Designer for page 1 and complete these steps.
  1. In the component panel, right-click the P1_CUSTOMER_DISPLAY item.
  2. Click Create Dynamic Action.
  3. In the properties panel, set Name to Dialog closed.
  4. Set Event to Dialog Closed.
  5. In the component panel, click the Show action under the True events.
  6. In the properties panel, set Action to Execute JavaScript Code.
  7. Set Code to the code below. This JavaScript uses the context keyword "this" to gain access to some values passed in the dialog close event and uses them to set the values of the LOV items.
$s('P1_CUSTOMER_DISPLAY', this.data.P2_DISPLAY);
$s('P1_CUSTOMER_ID', this.data.P2_ID);
  1. Set Selection Type to - Select -.
  2. Set Fire On Page Load to No.
  3. Click Save in the upper right-hand corner to save your changes.

Now your handcrafted, super duper LOV item should be good to go. Enjoy!

For those of you that are interested, I'm planning a follow up to this post the shows how to make the item enterable. :)

11 comments:

  1. Dan, I'm having trouble getting your code to work. Can't get the open LOV button to launch the modal page. I have tried several times to carefully follow your instructions, which are clear. I don't know javascript, but see what might be an inconsistency in the execute when page loads code on the home page. Should the funcBody line refer to 'lov-search-js' as you have written, or to 'data-lov-search-js'? I have tried both ways but the result is the same - nothing happens.

    $('.lov-open').on('click', function() {
    var $opener = $(this),
    $display = $opener.prev().prev(),
    funcBody = $display.data('lov-search-js')
    .replace(/^"javascript:/, '')

    I can run the modal page with a normal button link, and selecting a row does close the page and return to the home page.
    .replace(/\"$/,'');

    new Function(funcBody).call($display.get(0));

    ReplyDelete
    Replies
    1. Hi Skip back in 2016, so it's not surprising that it doesn't work anymore. Different versions of APEX often change the HTML emitted in different ways. If you create an example on apex.oracle.com that reproduces the issue and provide me with developer credentials (workspace/username/password), I'll take a look when I can. Feel free to email me (see links on right of blog) the credentials.

      Delete
  2. hi, I have tried twice this in Apex 19.1 but failed to get it working, I can open the modal dialog but it not display anything returned from dialog, is there anything to change because of Apex version or anything else? kindly help because i am in need this type of solution to return multiple values within multiple items/columns on calling page. regards

    ReplyDelete
    Replies
    1. Hi Ahmed,

      Checkout this plug-in:
      https://apex.world/ords/f?p=100:710:::::P710_PLG_ID:PL.OSTROWSKIBARTOSZ.APEX.ENHANCEDLOVITEM

      Until Modal LOVs are directly supported, this is probably your best bet.

      Delete
    2. thanks Dan, but I think still it is not what I want as it returns multiple values from single row but (in demo) it is selecting multiple Employee Name and return all in a single column of calling form. here is an example ( it requires google account to login ) which showing what I want but I think on the example/sample page something missing.
      https://apex.oracle.com/pls/apex/f?p=111708:3
      regards

      Delete
    3. Ahmed,

      Please download the plug-in and test it. Multi-selection is an option, but you don't have to use it.

      Regards,
      Dan

      Delete
  3. hi, its done without the code you have mentioned here in "Execute When Page Loads" with help of @fac586 who referred me here at your this page.
    topic link: https://community.oracle.com/thread/4267714
    thanks for everything you did for us :)
    regards

    ReplyDelete
  4. Hi Dan,

    I am not sure if you remember or not that we met few years back in Florida ODTUG. I am trying to implement LOV with Color where colors are displayed as background color. The only way I can do it using custom modal page. I am having some issues with that approach. I tried using Pretius APEX Enhanced LOV item plugin but I don't think that is also helping me.

    Do you have any suggestions or any example that I can reference.

    Appreciate your help

    Thank you
    Swaroop

    ReplyDelete
    Replies
    1. Hi Swaroop,

      Please send me a direct email. Look under "Important Links" on the right-hand side of the blog.

      Regards,
      Dan

      Delete
  5. Hi Dan,

    I think this is a great solution to replace the super LOV.

    I was trying to work out how easy it would be to have the CUSTOMER_ID (from your example) as a hidden field in the popup modal. My javascript isn't quite good enough to do it on my own, but I feel sure it should be possible.

    Regards
    Clare

    ReplyDelete
  6. Hi Clare,

    I don't recommend doing this anymore. APEX 19.2 (which was released two weeks ago) includes a redesigned Popup LOV that should meet most peoples need. Read about that and other new features here: https://blogs.oracle.com/apex/announcing-oracle-apex-192

    ReplyDelete