Skip navigation header
Oracle ADF UIX Developer's Guide Go to Table of Contents
Contents
Go to previous page
Previous
Go to next page
Next

11. Creating LOVs in ADF UIX

This chapter contains the following sections:

Introduction

Web pages frequently allow an end user to type in or update text data. Sometimes the input values are unbounded, and any user input is acceptable. However, the developer often needs to limit the user's input choices to a particular set of allowable values. This can be done using a radio buttons, check boxes, or pull-down lists. These elements work well when the number of choices is small, but they become cumbersome for large datasets.

When a large dataset is involved, the best way to present choices to the user is through a list of values (fondly referred to as an LOV). The list of values is often presented in a secondary window. It can present the data in a table making it easier for the user to find the value or values they want. With many datasets, the ability to sort the data, or narrow down the number of values presented is very useful.

Oracle ADF UIX provides elements which assist the developer in presenting an LOV to the user. The elements open and manage the secondary window; manage the transfer of data from the main window to the LOV window and back again; provide filtering events; and provide a layout of appropriate fields in the secondary window. The two main elements are the lovInput element and the listOfValues element.

The lovInput element

First we'll look at an the lovInput element. This element, implements a simple text input field and a "flashlight" search icon. In its simplest form, it takes a destination attribute. This attribute points to a UIX page - containing a listOfValues element - which will be used to populate the secondary window. The UIX tag:


    <lovInput id="myLOV"
              name="myLOV"
              columns ="10"
              destination="LOVDlg.uix"
              partialRenderMode="self"/>

will generate this:

The lovInput element rendered
Figure 11-1: An lovInput rendered

Now, when a user enters text into the text field, a partial page lovValidate event will be generated (see the Partial Page Rendering and Client Actions in ADF UIX chapter for details on the PPR architecture). The event will have the user's text in its searchText parameter. The event handler code can evaluate the searchText and, if it matches one of the valid values for that field, accept it. If it doesn't match a valid value, the developer will want to raise a window from which the user can select one of the correct values. To do these things, we need to extend our simple lovInput a little bit:


   <lovInput id="myLOV"
             name="myLOV"
             columns ="10"
             destination="LOVDlg.uix"
             text="${ui:defaulting(uix.data.fieldText,
                                 uix.data.defaultText)}"
             showWindow="${uix.eventResult.showWindow}"
             partialRenderMode="self"/>

Now the text field has a default text but, more importantly, when the event handler updates the provider for fieldText, with the newly validated value, it will automagically show up. So if your value data set is, say, the fourteen 8000 meter mountain peaks, and the user types in the letter 'A', you'll know that the only matching peak is Annapurna, and you can update the fieldData provider with this full value. You already know, from the Data Binding chapter, how to do this. This auto-matching/auto-completion looks really cool! Your event handler would be of the following form:


  public static EventResult handleLovValidateEvent(
    BajaContext context,
    Page        page,
    PageEvent   event
    )
  {
    String searchText = event.getParameter("searchText");

    // Implementation of matchPeak and MatchClass not shown, but
    // matchPeak will validate the search text. MatchClass will keep
    // the match data.
    MatchClass matches = matchPeak(searchText);
    if (matches.isValid())
    {
      // update the provider for the text attribute on the lovInput
      fieldText.put(matches.getValidText());
    }
    return new EventResult(page);
  }

Unfortunately, the user doesn't always know which peak they want. Sometimes they only know the first letter of the peak name, they can't spell it correctly, they mistype it, or they just want to test you. In these instances, you can automatically bring up a new window to display a list of correct values.

This is what the showWindow attribute is all about. In most cases, showWindow should be cleared to false. When it is set to true on a partial refresh, the lovInput element will automatically send a script to the client which will open a new window. Note that because showWindow doesn't need to persist across page renders, it can go right onto the EventResult. Now your event handler now looks like:


  public static EventResult handleLovValidateEvent(
    BajaContext context,
    Page        page,
    PageEvent   event
    )
  {
    String searchText = event.getParameter("searchText");

    // Implementation of matchPeak and MatchClass not shown, but
    // matchPeak will validate the search text. MatchClass will keep
    // the match data. This matchPeak matches on the peak name.
    MatchClass matches = matchPeak(searchText);
    if (matches.isValid())
    {
      // update the provider for the text attribute on the lovInput
      fieldText.put(matches.getValidText());
    }
    else
    {
      // Save the MatchClass off to some persistent store so that searchText
      // and possibly the matches are available for the LOV window.
    }
    EventResult result = new EventResult(page);

    // only pop up the LOV window if there isn't an exact match
    result.setProperty("showWindow", !matches.isValid());

    return result;
  }

Before the window comes up, you will receive an lovFilter event with the same text in the searchText parameter so you can do the real data retrieval at that point. It's important to handle this event because in the non-partial-page-rendering cases you will not get an lovValidate event, and the user may choose to change their search criteria from the LOV window (more on this later). The lovFilter handler need not do anything special, it could look like this:


  public static EventResult handleLovFilterEvent(
    BajaContext context,
    Page page,
    PageEvent event)
  {
    // The source parameter tells you which lovInput field this came from
    String source = (String) event.getParameter("source");

    // Now do the search for the given text in the given category
    // categories may be name, location, altitude etc. for this example
    String cName = event.getParameter("choiceElementName");
    String searchText = event.getParameter("searchText");

    // Implementation of matchPeak and MatchClass not shown, but
    // matchPeak will validate the input text. MatchClass will keep
    //  the match data. This matchPeak matches on the given category.
    MatchClass matches = matchPeak(searchText, cName);

    // and build the display data for the table on the LOV window
    setTableDataProviderFromMatches(matches);

    return new EventResult(page);
  }

So, if the user were to type in a 'K', they might, now, expect to see a window something like this:

An LOV window listing two choices (K2 and Kangchenjunga)
Figure 11-2: A List Of Values window with dataset filtered to two values
Wow! That'll take a bit of work to put together that page, and wire it up with javascript. Luckily, we've already done all that work for you. Remember that above we said that the lovInput's destination attribute points to a page which will display the actual list of values - using a listOfValues element. Well, what you're seeing above is a page with little more than a listOfValues element and its table.

The listOfValues element

Now we're ready for the listOfValues element. There's a lot to this element, but it's mostly made up of other elements that you're already familiar with. It boils down to this:


   <listOfValues title="Available peak values"
                 id="lovHandler"
                 searchText="${uix.data.fieldData}">
    <headerInstructions>
     <styledText text="Choose a peak"
                 styleClass="OraInstructionText"/>
    </headerInstructions>

    <searchInstructions>
     <styledText text="You can also change or refine your search.
                       Try searching on peaks using a search text of 'A',
                       'K', or 'M'. Try searching locations using an 'N'."
                 styleClass="OraInstructionText"/>
    </searchInstructions>

    <filterChoice>
     <choice name="categoryChoice">
         <!-- fill in the choices from the column headers -->
     </choice>
    </filterChoice>

    <contents>
     <!-- Here's the table definition                 -->
     <!-- This must be a form submitted proxied table -->
     <table name="lovTbl"
            formSubmitted="true"
            proxied="true"
               ... >
     <!-- Populate with some data  -->
     </table>
    </contents>
   </listOfValues>

Yup, that's a lot, but let's take it a bit at a time. The main attributes are easy, we've seem them all before. The id attribute is simple and familiar by now. The title just provides a top level header for the page - it'll appear right after the (translated) "Search and Select: " at the top of the window. And you'll notice that the searchText is bound to the same data that the lovInput's text field was. This makes sense because that's where the search text came from!

OK, next we have two types of instruction text named children.


    <headerInstructions>
     <styledText text="Choose a peak"
                 styleClass="OraInstructionText"/>
    </headerInstructions>

    <searchInstructions>
     <styledText text="You can also change or refine your search.
                       Try searching on peaks using a search text of 'A',
                       'K', or 'M'. Try searching locations using an 'N'."
                 styleClass="OraInstructionText"/>
    </searchInstructions>

The first, headerInstructions, is used to give a general description of what the user is supposed to do with this page. The second, searchInstructions, gives a detailed description of how the user can refine their search (again, more on this soon).

Next, there's a filterChoice named child


    <filterChoice>
     <choice name="categoryChoice">
         <!-- fill in the choices from the column headers -->
     </choice>
    </filterChoice>

This child just takes a choice element to show a pull-down list of possible search categories. This is how the user can refine their search. If the user is not happy with the values they are presented with, but they know that the peak they're interested in is somewhere in Pakistan, they can switch the category choice over to location (one of the table column headers), type 'Pa' into the search text field, and click 'Go'. You'll get an lovFilter event again - you can use the same handler to update the table data.

And, finally, the sole indexed child is a table element to present the matches you got from filtering your base data using the users searchText.


    <contents>
     <!-- Here's the table definition                 -->
     <!-- This must be a form submitted proxied table -->
     <table name="lovTbl"
            formSubmitted="true"
            proxied="true"
               ... >
     <!-- Populate with some data  -->
     </table>
    </contents>

Of course you know all about the table element from the Creating Tables in ADF UIX chapter. One other thing, the contents element usually takes a table element, but it will really take any arbitrary UINode. Using this fact you can add extra data to the submits you get with the table (or the search choice for that matter - they're all in the same form) by setting the contents like this:


    <contents>
     <flowLayout>
      <contents>
       <table>
        ...
       </table>
       <formValue name="extraParam" value="${uix.current.secretParam}"/>
      </contents>
     </flowLayout>
    </contents>

The lovOpenWindowAction Client Action

Sometimes you might want to do something other than transfer the selected value back into a text input field. For these occasions, there is an lovOpenWindowAction Client Action (if you don't know about Client Actions yet, see the Client Actions section of the Partial Page Rendering chapter).

The lovOpenWindowAction Client Action simply generates the code necessary to launch an LOV window. It's pretty much exactly what is attached to the search icon when you use an lovInput element. Using this element, you can launch an LOV from an arbitrary element. This could come in handy if you want to allow the user to add rows to a table, but want to make sure the rows are correct. Here's an example:


      <button id="lovAddButton"
              text="add rows"
              shortDesc="Add more rows to the table">
       <primaryClientAction>
        <lovOpenWindowAction destination="LOVDlg.uix"
                             targets="peaksAddTable"/>
       </primaryClientAction>
      </button>

You could even have the list of values pre-filtered by adding an initial searchText to the action:


      <button id="lovAddButton"
              text="add more 'G' rows"
              shortDesc="Add more 'G' rows to the table">
       <primaryClientAction>
        <lovOpenWindowAction destination="LOVDlg.uix"
                             searchText="G"
                             targets="peaksAddTable"/>
       </primaryClientAction>
      </button>

Conclusion and Demo

And that's it! Now you know the basic steps needed to build your own LOV. There are some more advanced features like Javascript callbacks, which we won't go into here. Please consult the Oracle Technology Network for code samples and demos.