Saturday, September 11, 2010

Getting the String Value or Item Code of an LOV item instead of its index

The example application can be downloaded here : LOV_example

The problem

When you have an LOV (drop down, radio button group etc.) on the page and you want to get the code or String value of the currently selected item(s) in the backing bean; But calling the getValue() on the component returns the index of the item and not its code or value.


The solution

Create the list items using a for loop around an <f:selectItem> instead of using an <af:selectItems>
Dragging and dropping the VO for the LOV from the data control palette to the page creates the list binding for the LOV in the page definition and sets up the LOV for use. The default behavior of the list binding is to use the <af:selectItems> for the selectable LOV elements, which uses indexes rather than the actual values. So to use the actual values, we circumvent the default behavior my setting up the binding ourselves using a for loop around an <f:selectItem> which lets us control both itemLabel and itemValue as opposed to the <af:selectItems> which does not offer such fine grained control. To do this, instead of dragging the VO to the page, use the component palette to select the LOV type you need (single/multi select list/dropdown) and then manually set the EL to get the individual items. The key here is to avoid using the list binding that JDeveloper creates for you and then use a for loop to create the selectable items in the list with finer control. On the page definition file, create a List binding for the VO you need, in this example, it’s the departmentsVO. From the page, you can iterate through the elements of this VO like below

<af:selectOneChoice label="#{bindings.DepartmentLOV.label}" id="textSingle"
                              binding="#{pageFlowScope.LOVValueDemoBean.textLOVSingle}">
            <af:forEach items="#{bindings.DepartmentLOV.iteratorBinding.allRowsInRange}"
                        var="row">
              <f:selectItem id="myItem2" itemLabel="#{row.departmentName}"
                            itemValue="#{row.departmentName}"/>
            </af:forEach>
          </af:selectOneChoice>

Note the use of the <af:forEach> tag. We are not using the JSTL core taglib here, and the items attribute has the EL expression #{bindings.DepartmentLOV.iteratorBinding.allRowsInRange} This EL gives the items in the LOV in a format that’s usable by the <af:forEach> tag. Inside the loop is an <f:selectItem> tag with its label and value set to the values we’d like to see. The itemLabel is displayed on screen, and calling getValue() in the component will return the itemValue. The variable row, of course will be an instance of the DepartmentLOVRowImpl (in this example), so its properties can be accessed with EL as above.
The implementation is slightly different for single/multi select LOVs and what the itemValue is set to, depending on if you want to get the textual value that’s displayed or an LOV code (e.g. get dept no. when choosing the dept name) or some other property in the VO. Note that this works only if you have your VO's VORowImpl class generated/implemented.

Single select LOV

I want to get the string value of the text selected on screen




The following code goes to the page :
<af:selectOneChoice label="#{bindings.DepartmentLOV.label}" id="textSingle"
                              binding="#{pageFlowScope.LOVValueDemoBean.textLOVSingle}">
            <af:forEach items="#{bindings.DepartmentLOV.iteratorBinding.allRowsInRange}"
                        var="row">
              <f:selectItem id="myItem2" itemLabel="#{row.departmentName}"
                            itemValue="#{row.departmentName}"/>
            </af:forEach>
          </af:selectOneChoice>

The backing bean method that is fired (can be bound with a valueChangeListener or as in the example, with a command button's action binding, which is not show below but is in the downloadable project code ):
public String getTextValueSingle()
  {
    String value = String.valueOf(getTextLOVSingle().getValue());
    getValueDisplay().setValue(value);
    return null;
  }

I want to get the LOV code and not the displayed text


This goes to the page. Notice the <f:selectItem>'s itemValue and itemLabel :
<af:selectOneChoice label="#{bindings.DepartmentLOV.hints.label}"
                              id="soc3"
                              binding="#{pageFlowScope.LOVValueDemoBean.textIDLOV}">
   <af:forEach items="#{bindings.DepartmentLOV.iteratorBinding.allRowsInRange}"
                        var="row">
      <f:selectItem id="myItem3" itemLabel="#{row.departmentName}"
                            itemValue="#{row.departmentId}"/>
   </af:forEach>
</af:selectOneChoice>
The code for the backing bean is the same :
public String getTextIDValue()
  {
    String value = String.valueOf(getTextIDLOV().getValue());
    getValueDisplay().setValue(value);
    return null;
  }

Multi select LOV

I want the display text values



The code for the page is the same expect for the change in the component used(its a multi select LOV)
<af:selectManyChoice label="Label 1" id="smc1"
                               binding="#{pageFlowScope.LOVValueDemoBean.textMultiLOV}">
  <af:forEach items="#{bindings.DepartmentLOV.iteratorBinding.allRowsInRange}"
                        var="row">
     <f:selectItem id="myItem4" itemLabel="#{row.departmentName}"
                            itemValue="#{row.departmentName}"/>
  </af:forEach>
</af:selectManyChoice>

The backing bean code is different here, because the multiselect LOV components return a list of Strings in return to the call to getValue(), as shown below :
public String getTextValueMulti()
  {
    List<String> selected = (List<String>) getTextMultiLOV().getValue();
    String csvValue = "";
    for (String s: selected) // For the example, we're displaying the strings as a comm separated list
    {
      csvValue = csvValue + s + ",";
    }
    getValueDisplay().setValue(csvValue);
    return null;
  }

I want to see the LOV codes


The page code, just like the single select example, the <f:selectItem>'s itemValue and itemLabel determine whats displayed on screen and what you get back from a call to getValue()
<af:selectManyChoice label="Label 2" id="smc2"
                               binding="#{pageFlowScope.LOVValueDemoBean.textIDMultiLOV}">
    <af:forEach items="#{bindings.DepartmentLOV.iteratorBinding.allRowsInRange}"
                        var="row">
        <f:selectItem id="myItem5" itemLabel="#{row.departmentName}"
                            itemValue="#{row.departmentId}"/>
    </af:forEach>
</af:selectManyChoice>
The backing bean code, same as above, except that the list you get back is a list of oracle.jbo.domain.Number, the same type from the VOImpl.

public String getAllIDsSelected()
  {
    List<oracle.jbo.domain.Number> selected =  // IDs are numbers, the type from the VO is preserved.
      (List<oracle.jbo.domain.Number>) getTextIDMultiLOV().getValue();
    String csvValue = "";
    for (oracle.jbo.domain.Number s: selected)
    {
      csvValue = csvValue + s.intValue() + ",";
    }
    getValueDisplay().setValue(csvValue);
    return null;
  }
The example application can be downloaded here : LOV_example



6 comments:

  1. hi,

    i tried to do the same cerated vo and expose the rowImpl class but i get error that

    javax.el.PropertyNotFoundException: The class 'mps.model.gate.MpsVendorNameVORowImpl' does not have the property 'MpsVendorId'.

    i compared with your application everything is same in the rowImpl.

    ReplyDelete
  2. Try giving the property name as mpsVendorId instead of MpsVendorId

    ReplyDelete
  3. This comment has been removed by a blog administrator.

    ReplyDelete
  4. What should I take for 'DepartmentLOV'?

    ReplyDelete
  5. nice post, thanks

    ReplyDelete
  6. This is a very good documentation. It would be great if you could give more details on the design by attaching some screenshots

    ReplyDelete