I have been recently helping one of Oracle Partners in migrating their Forms application to the ADF technology. It was a retail store solution and one of the main requirements was to provide quick and automatic way to scan and enter the items at the cashier desk. The bar code scanner is a relatively simple device and can be treated simply as an additional keyboard attached to the cashier’s computer. To automate entering consequential items at the desk we need to have a web page with a form which is able to dynamically create new rows (for new items) detecting some kind of key sequence e.g. enter. This way the cashier does not have to even touch the keyboard to repetively add new items to current transaction.
I have created a simple demo application based on the famous HR schema where the roles of the transaction items play the rows in the REGIONS table – this is just for sake of simplicity to reproduce it without creating additional schemas or tables. The form is extremely simple and initially consist of one inputText field where you can enter some text (here the scanner will inject the item’s code) and when you press enter the form will create another field for the next item (with automatic focus) and so on:
You can commit or rollback the new rows/items by using one of the provided buttons. Of course everything is backed by an ADF View Object (RegionsView) and powered by all transactional features of ADF. In order to run the attached sample you need to unpack the application, open it in JDeveloper (v.11.1.1.6), change the database connection details to point it to your HR schema and run the provided sql script (create_regions_sequence.sql) to create the additional sequence for regions ids. Then just launch the reg_form.jspx page.
If you look closer at the application you will see the main conceptual points which the solution is based on:
1. The underlying view object is defined in the AM’s data model as “No Rows” to get rid of existing regions being displayed in the form:
publicvoid createNewRegionAsFirst() {
Row newRegion = this.createRow();
this.insertRowAtRangeIndex(0, newRegion);
}
3. A method binding in the reg_form PageDef which calls the abovementioned method at the page first display (to create first empty row):
<af:iterator
id="iter" value="#{bindings.RegionsView1.collectionModel}"
var="row"
rows="#{bindings.RegionsView1.rangeSize}"
varStatus="st"
binding="#{backingBeanScope.reg_form_backing.iterator}">
<af:inputText
id="input" value="#{row.bindings.RegionName.inputValue}"
simple="true"
binding="#{backingBeanScope.reg_form_backing.input}">
<af:clientListener
method="onPressEnter" type="keyPress"/>
<af:serverListener
type="newRegionEvent"
method="#{backingBeanScope.reg_form_backing.onNewRegionEvent}"/>
</af:inputText>
</af:iterator>
6. Java script method (reg_form_code.js) to catch and propagate the enter key event to the server:
function onPressEnter(evt) {
var inputComp = evt.getSource();
var _keyCode = evt.getKeyCode(); //check
for Enter Key
if (_keyCode ==
AdfKeyStroke.ENTER_KEY ){
AdfCustomEvent.queue(inputComp, "newRegionEvent", null, false);
evt.cancel();
}
}
7. A backing bean method (RegFormBean.java) to handle the enter key on the server site:
publicvoid onNewRegionEvent(ClientEvent clientEvent) {
BindingContainer bindingContainer
= BindingContext.getCurrent().getCurrentBindingsEntry();
OperationBinding createInsert =
bindingContainer.getOperationBinding("createNewRegionAsFirst");
createInsert.execute();
StringBuilder script = new StringBuilder();
//use
client id to ensure component is found if located in
//naming
container
script.append("var textInput = ");
script.append("AdfPage.PAGE.findComponentByAbsoluteId");
script.append ("('"+getFirstClientId()+"');");
script.append("if(textInput != null){");
script.append("textInput.focus();");
script.append("}");
AdfFacesContext.getCurrentInstance().addPartialTarget(iterator);
writeJavaScriptToClient(script.toString());
}
it first calls the createNewRegionAsFirst binding method to create a row and then generates a piece of JavaScript code to set the focus on the new row.
To get the dynamically rendered inputText id it uses the following code:
public String getFirstClientId() {
Object rowKey =
iterator.getRowKey();
String clientId = null;
try
{
iterator.setRowIndex(iterator.getFirst());
clientId =
input.getClientId(FacesContext.getCurrentInstance());
}
finally
{
iterator.setRowKey(rowKey);
}
return clientId;
}
based on the “iterator” and“input” binding properties bound to the af:iterator and af:inputText respectively.
8. Finally the same method for getting the dynamic id of the input text is used to initially set the focus on the first empty field:<af:document id="d1" initialFocusId="#{backingBeanScope.reg_form_backing.firstClientId}">
I hope that the example will prove useful for everyone struggling with all kinds of ADF dynamic forms. Comments, improvements and suggestions are as always welcome.
Best regards,
Lukasz Romaszewski | IMC Migration Center | Oracle