As Published In
Oracle Magazine
September/October 2005

DEVELOPER: Frameworks


Creating Search Pages

By Steve Muench Oracle Employee ACE

Help users find what they're looking for.

In my last column, "Browsing and Editing Data" (Oracle Magazine, July/August 2005), you saw how easy it was to use Oracle JDeveloper 10g and Oracle ADF to create Web pages to browse and edit database data. In this column, we'll explore how to add basic search functionality to an Oracle ADF application. The steps in this article use Oracle JDeveloper 10g Release 10.1.2.

Before you start building pages, follow the same steps as in my last column to build a new application workspace using the "Web Application [default]" template and an application module in your Model project using the Business Components from Tables wizard. The application module, named HRModule , should contain a single EmpView view object instance that queries data from the EMP table in the SCOTT account. With this in place, you will build two different search pages to illustrate functionality you might find useful for your end users.

Using Oracle ADF Find Mode

The first search page you'll create leverages the built-in find mode within Oracle ADF. Every view object supports find mode to simplify the process of building query-by-example functionality. Find mode works in the same way that the Enter Query and Execute Query features of an Oracle Forms block work, by automatically handling any combination of user-entered search criteria to build the appropriate WHERE clause.

To create the page in Oracle JDeveloper, right-click the ViewController project in the Application Navigator and select Open Struts Page Flow Diagram from the context menu. Next, drag a Data Page component from the Component palette, and drop it onto the diagram surface. (If the Component palette isn't visible, select it from the View menu). Name it /FindMode (with a leading slash), double-click on the page icon, and select /FindMode.uix as the page name to create. The UIX Visual Editor will appear.

Next, open the Data Control palette (select it from the View menu if it's not visible), and expand the HRModuleDataControl node. Select the EmpView1 data collection, choose Input Form from the "Drag and Drop As:" list at the bottom of the palette, and then drop EmpView1 onto the page. Back in the Data Control palette, expand EmpView1 and the Operations folder it contains. Drag the Find operation, and drop it onto the page, next to the Submit button. Repeat this step to drop buttons for the Execute, PreviousSet, and NextSet operations too. Finally, select the original Submit button, and delete it.

The input form you just created will allow the user to enter search criteria in find mode. When the user clicks on the Find button, this toggles the page into find mode. When the user clicks on Execute , the application will apply the user-entered criteria, execute the query, and show the results in browse mode.

Next, add a table to display the results of the search. Start by clicking again on the EmpView1 data collection in the Data Control palette, and make sure Read-Only Table is selected under the "Drag and Drop As:" list. You want to drop this read-only table onto the page, just below the row of buttons, but this time you will perform the operation differently. It is important to have the table contained inside the page's form component; otherwise the selection of a current row won't work as expected. To ensure proper placement of the table, drag the EmpView1 table to the Structure Window, and drop it right on top of the existing form - form0 node. Note that the table also appears in the UIX Visual Editor.

As a finishing touch, add some dynamic display behavior so that only the relevant parts of the page are displayed in find mode and browse mode. First, make the input fields show only when the page is in find mode. Select labeledFieldLayout in the Structure Window that contains these fields, find its rendered property in the Property Inspector, and select it. Click on the Bind to Data button in the Property Inspector toolbar to turn the value of the rendered property into a dynamic expression, and change the ${'true'} expression to read ${bindings.findMode} . This J2EE standard expression refers to the value of the findMode property of an object named bindings. At runtime, Oracle ADF ensures that the bindings object always exists for reference. It contains a number of useful objects with properties related to the data the current page uses. In our case, the value of the ${bindings .findMode} expression will be a Boolean that evaluates to true if the page is in find mode and false otherwise. By assigning this expression to the rendered property, you ensure that the whole group of labeled fields will get rendered only when you're in find mode.

Repeat the same steps to bind the rendered property of the Execute button to the same ${bindings.findMode} expression. Finally, turn off client-side validation of query criteria parameters when the user clicks on the Execute button. You want to do this to allow the user to enter criteria like > 1500 in the Sal field, even though > 1500 is not a valid number. To turn off client-side validation, set the unvalidated property of the Execute button to true.

As your last step, you need to make the table displaying the results, as well as the Find, PreviousSet , and NextSet buttons, display only when the user is not in find mode (that is, when the user is browsing the search results). In the UIX Visual Editor, while holding down the [Ctrl] key, select the Find, PreviousSet , and NextSet buttons. Still holding down the [Ctrl] key, click on the table element in the Structure Window to select it. With all four components selected, find the rendered property in the Property Inspector, click on Bind to Data, and enter the expression ${not bindings.findMode} . You're done, so save all your changes.

You can now test your creation by flipping back to the Struts page flow diagram (struts-config.xml), right-clicking on the /FindMode page, and selecting Run from the context menu. Try clicking on the Find button and entering some search criteria, such as %A% in the Ename and > 1500 in the Sal field. Then press the Execute button to see the filtered results.

The Role Bindings Play

Before you build your second page, let's look at what's going on behind the scenes to make the functionality described above possible without writing code. In your application's view layer, UI controls on pages are wired to data from the model layer using helper objects called bindings . The UI Model minitab within the Structure Window contains a visual representation of the bindings the current page uses. There are three basic kinds of binding objects: 

  • Iterator bindings keep track of the current row in a collection of data.

  • Control bindings connect UI components to data attributes in an iterator's collection.

  • Action bindings invoke business service methods to perform a task.

In the UI Model minitab for the /FindMode page you just built, you can see an iterator binding named EmpView1 Iterator . It keeps track of the current row in the underlying data collection provided by the EmpView1 view object instance. You can also see control bindings such as Ename and Sal that connect respective UI components to underlying attributes in that iterator binding. In fact, if you select one of them and examine its Iterator Name property in the Property Inspector, you'll see that relationship reflected. Finally, you can also see action bindings such as Find and Execute that invoke corresponding built-in operations on the business service.

Oracle JDeveloper adds appropriate bindings to the current page's UI model when you drop elements from the Data Control palette onto the page. They are described declaratively in a file called FindModeUIModel.xml that is related to the /FindMode page. The Oracle ADF framework reads these *UIModel.xml files at runtime to create the right set of bindings for each page. This group of bindings—known at runtime as the binding container—is exposed to your application through an object named bindings. It's the same object you referenced above when you used the expression ${bindings.findMode} to determine whether the page was in find mode or not.

Creating a Quick-Search Field

With binding basics behind you, let's create a second page with a quick search field above a table of results. In this example, you'll be searching for employees based on their name. Start by adding a second Data Page to the Struts page flow diagram, and name it /QuickSearch . Double-click on it to create and open the /QuickSearch.uix page in the UIX Visual Editor. As in our first example, use the Data Control palette to drop a read-only table for EmpView1 onto the page.

Now you'll add a prompt, a text field, and a button above the table to support our quick-search functionality. In the Structure Window, find the table node you just created. In the Component palette, select the "All UIX Components" palette page from the dropdown menu at the top, and drop a styled Text component directly onto the Structure Window just before the table node, as a child of the form component. As you drag, you'll see visual drag-cursor feedback in the tree as a horizontal line with a down-arrow to let you know exactly where the component will be placed when you release the mouse. Set the text property of the styledText component to Find by name.

To add the text field, go to the Data Control palette, expand EmpView1 , select the Ename attribute, set the Drag and Drop As: drop-down menu value to TextInput, and drop Ename onto the page to the right of the Find by name: prompt. By creating the input field this way, it is automatically bound to the Ename field so the find mode criteria the user types in gets applied to filter the Ename field. Next, set the readOnly property of the Ename text field to false so that the user can always type into it. (Note: You might have to first set the property to true and then to false to get it to stick.) Finally, drop a submitButton from the Component palette onto the page to the right of the Ename field, and set its text property to Go and its event property to FilterByName . We will discuss our use of this event property a bit later.

There are a few more things we need to do. First, we want to have the Ename input field display the search criteria at all times, so we need to keep the Ename field in find mode. However, as long as the bindings for the table and the Ename field are both related to the same iterator binding, they will both toggle in or out of find mode in unison. Since we don't want that behavior, we need instead to create a second iterator binding based on the same EmpView1 view object.

Start by selecting the QuickSearch.uix page in the visual editor. Click on the UI Model minitab in the Structure Window to see the bindings for that current page. Right-click on the QuickSearchUIModel root node and select Create Binding > Data > Iterator from the context menu. Select EmpView1 for the Data Collection , enter an Iterator Id of EmpView1SearchIterator , and click on OK. Next, click on the Ename binding in the Structure Window and use the Property Inspector to change its related Iterator Name property from EmpView1Iterator to the new EmpView1SearchIterator. 

Next Steps


READ more about
Oracle JDeveloper 10g and Oracle ADF
 "
Creating Search Pages with Both Fixed and Dynamic Criteria"

DOWNLOAD
sample application for this column
Oracle JDeveloper 10g

EXPLORE the tutorial
 "
Building J2EE Applications with Oracle JHeadstart for ADF"

Next, we need to force the EmpView1SearchIterator to always be in find mode. We do that by adding a line of code to an overridden method in the Data Action class associated with the QuickSearch page. In the Struts page flow diagram, right-click on the /QuickSearch page, and select Go to Code from the context menu. Since the associated class is optional and doesn't exist yet, the Create Struts Data Action dialog box will appear. Just click on OK to accept the default names. We'll augment the default behavior for preparing the page's data model by overriding the prepareModel() method and adding our line of code before the call to the superclass. Left-click the mouse anywhere in the code editor for the QuickSearchAction class, and select Tools > Override Methods from the main menu. Check the prepareModel method in the list, and click on OK. Add the following line of code before the call to super.prepareModel() in the body of the now-overridden prepareModel method:

 

actionContext.getBindingContainer()
             .findIteratorBinding("EmpView1SearchIterator")
             .setFindMode(true);


This code finds the EmpView1SearchIterator in the page's binding container and sets it to be in find mode.

Next, remember that we set the event property on the Go button to have a value of FilterByName ? This step causes an event named FilterByName to fire when the user clicks on that button. When we add an appropriately named onFilterByName() method to our QuickSearchAction class, our code can handle the event. In our event handler, we want to execute the query on the EmpView1Iterator to show the results. Here's the method to add to the QuickSearchAction class: 

public void onFilterByName(DataActionContext actionContext) 
{
  actionContext.getBindingContainer()
               .findIteratorBinding("EmpView1Iterator")
               .executeQuery();
}


As a final touch of class, let's allow the user to press the Enter key as an alternative to clicking on the Go button. To cause the same FilterByName event to fire, we just need to add a hidden field named event with the value FilterByName to the form. To do this, select the QuickSearch.uix page in the visual editor, and use the Component palette to drop a formValue component into the Structure Window as a child of the form component. Assign the value event to its name property, and the value FilterByName to its value property, and click on OK. Now we're done, so save your work.

Flip back to the Struts page flow diagram, and run the /QuickSearch page to test the new functionality. Try typing in filter criteria such as A% and %S% to observe the effect of the quick-search. Then clear the field, and press Enter or click on Go to see the unfiltered list.

Search for More

If this quick glimpse of Oracle ADF search page basics has piqued your interest, I recommend following the "Building J2EE Applications with Oracle JHeadstart for ADF" tutorial. In this tutorial, you will see a number of additional kinds of Oracle ADF search pages that the Oracle JHeadstart Application Generator can generate to save you precious development time. And check out the article "Creating Search Pages with Both Fixed and Dynamic Criteria," at for more details on Oracle ADF search functionality.


Steve Muench is a consulting product manager for Oracle JDeveloper and an Oracle ACE. In his more than 15 years at Oracle, he has supported and developed Oracle tools and XML technologies and continues to evangelize them. He authored Building Oracle XML Applications (O'Reilly Media, 2000) and shares tips and tricks on OTN and his "Dive into BC4J and ADF" Weblog.


Send us your comments