Skip to main content

Declaratively enabling multiple value selection for an LOV in an editable UI form

ADF allows you to specify multiple value selection for an LOV when used in an af:query component backed up by ViewCriteria. This is not a new feature and most of you might have tried it out before. Please take a look at the Fusion Developer's Guide if you are not aware of how to do this. What if you want to enable multiple selection for an LOV displayed in a data capture form or in an editable table UI?Started thinking about manual coding? Wait ! Tthis can be done declaratively (no 'visual' editing support though) with some tweaking to fit in to your use case. This post talks about this hidden feature. I was not aware of this feature(as many of you) till I hear about this from Steve Muench. Thanks Steve !

Here are the steps for building a multiple value selection enabled LOV which may look like as shown below.


Following are the steps that you may want to follow:
  • Define a normal SelectOneChoice LOV on appropriate view object attribute. If you are not sure how this is done, please read the section Working with List of Values (LOV) in View Object Attributes in Fusion Developer's Guide.
  • Let us convert this to an LOV that supports multiple value selection. As there is no editor support for this feature, you may want to do the changes manually in the VO xml, its pretty simple though. Open the view object XML in  the source editor(select source tab). Select the attribute on which you defined the LOV and then change the CONTROLTYPE value to delimited_ids_choice. 
<ViewAttribute
 Name="Locations"
 PrecisionRule="true"
 ...   ...  ...
        >
 <DesignTime>
  <Attr Name="_DisplaySize" Value="4000"/>
 </DesignTime>
 <Properties>
  <SchemaBasedProperties>
   <CONTROLTYPE
    Value="delimited_ids_choice"/>
  </SchemaBasedProperties>
 </Properties>
</ViewAttribute>
  • Go to the ListBinding tag for the LOV in the same view object XML and add the Delimiter for the multiple values returned by the LOV component(this example uses comma as delimiter). Your LOV is configured for returning multiple values now. The LOV enable attribute will now receive delimited List attributes(code or key corresponding to each LOV item) when user selects multiple values at run time.
<ListBinding
 Name="LOV_Locations"
 ListVOName="LocView1"
 Delimiter=","
 ListRangeSize="-1"
 NullValueFlag="none"
 MRUCount="0">
 ... ... ...
</ListBinding>
  • Let us build the UI now. Drag and drop the appropriate view object instance from data control on to the page. Now you may need to change the default LOV component added by the IDE to a multiple value selection enabled one. Go to the source tab of the JSF page and change LOV component to af:selectManyChoice. Take a look the following code snippet.
<af:selectManyChoice value="#{bindings.Locations.selectionIndices}"
 label="#{bindings.Locations.hints.label}"
 required="#{bindings.Locations.hints.mandatory}"
 shortDesc="#{bindings.Locations.hints.tooltip}" readOnly="false" autoSubmit="true"
 id="extGen_attr_Loc">
<f:selectItems value="#{bindings.Locations.items}"/>
</af:selectManyChoice>
  • Go to the page definition file, select the <List> binding for the LOV component and make sure you DO NOT have SelectItemValueMode="ListObject", it should be SelectItemValueMode="ListIndex" (which is the default value, no need to set explicitly if found missing for the List binding). See the code snippet given below.  That's it you are done ! Try running your page now.
  <list IterBinding="DeptWithNormalizedLocView1Iterator"
  StaticList="false" Uses="LOV_Locations" id="Locations"
  DTSupportsMRU="false"/>

Multiple Value Attributes pattern

Wait, I haven't yet finished this post ;). The above implementation (by default) stores the multiple values returned by the LOV as comma delimited string in a database table column, which may not be the feature you are looking for in a real life application. You may prefer to store these comma separated values in child table as individual rows for easy management and fast processing (and of course as a best practice as well). This can be achieved by adding few lines of code in your entity implementation class. I'm attaching a sample that does this job.

Download 

You can download the sample workspace from here.
[ Runs with Oracle JDeveloper 12.1.2.0.0 + SCOTT Schema(+ a custom DEPT_LOC  table)]

How to run this sample?

Pre-requisite 
This sample uses SCOTT schema and an additional table called DEPT_LOC. The scripts are available in <MVAPattern>/script folder. You may want to run scott.sql if you don't have  SCOTT schema and then run locdetails.sql (which does not come with SCOTT schema, used by this sample).

Multiple value patterns used in this sample

This sample showcases two approaches for storing comma delimited values returned by the LOV. In the first case multiple locations returned by the LOV are stored in a LOC column in DEPT table as comma separated values. The second approach uses a child table (DEPT_LOC)  to store the values returned by LOV after splitting the delimited string to individual rows.

Case 1 (Storing comma delimited values returned by LOV in a column):
The following business components that you see in model project are used in Case1: DeptWithLoc (EO), DeptWithLocView (VO) and LocLOVView (VO) . LocLOVView (VO) is a static list based view object which act as source view object for the multi-value LOV  enabled attribute defined on Loc attribute in DeptWithLocView. When you commit transaction, the comma delimited values are stored in the LOC column in DEPT table.

You can test Case 1 by running commaDelimitedMVA.jsf page in the view controller project

Case 2 (Storing comma delimited values  returned by LOV  in a child table):
The following business components that you see in model project are used in Case2: DeptWithNormalizedLoc (EO) , DeptWithNormalizedLocView (VO) and LocLOVView (VO) . In this case, the DeptWithNormalizedLocView has a  multi-value selection enabled LOV defined on transient Locations  attribute(this attribute corresponds to DelimitedLoc attribute in DeptWithNormalizedLoc EO ). Unlike in case 1,  the comma delimited values returned by the LOV is not stored in the DEPT table column when you commit transaction. DeptWithNormalizedLoc entity object plays smart here. When you commit transaction, DeptWithNormalizedLoc entity object parses the comma delimited values stored in the transient LOV enabled attribute and passes them to a child entity object called LocDetails(which is mapped to DEPT_LOC table). This is done by overriding DeptWithNormalizedLocImpl::prepareForDML(). Take a look at the source(DeptWithNormalizedLocImpl class) to get a feel of the implementation.

While querying DeptWithNormalizedLocView  uses expert mode query to read values from child table (DEPT_LOC ) as comma separated scalar value and assign the same to the transient LOV enabled attribute(Locations).

You can test Case 2 by running commaDelimitedMVAwithChildTable.jsf in the view controller project

Comments

Don Kleppinger said…
Is comma separated list to multi select LOV supported in 11.1.1.4 ?
Ben Wilcox said…
Same question, is this supported in 11.1.1.6?
Nischal said…
Thanks Jobinesh, very useful, was able to implement in couple of scenarios.
Somehow it does not work in a multi-row scenario though. This is in 11.1.1.7
Will this work with JDeveloper 11.1.2.4 version?
Prabhu Shankar said…
Thanks Jobinesh, this is a very useful one. I was able to follow the blog and get it working. One issue though, the values are delimited using semi-colon, even though I gave Delimiter="," in the VO. Is this normal?

I use 11.1.2.4
Unknown said…
This would work with 11.1.1.6. I did this and it would move to production. I have implemented on a attribute in Transient view object which is exposed on UI as a LOV.

Cheers,
Harsha.

Popular posts from this blog

How to set Bind Variable Values at runtime ?

In this post I'm sharing a couple of approaches for programmatically setting bind variables values at run time. This post is an attempt to explain 'When to use what ?'[ In case if you are familiar with 'Bind Variables' in ADF BC, please refer Section 5.10, Working with Bind Variables in Fusion Developer's Guide ]

1. Set the Bind Variable value using RowSet::setNamedWhereClauseParam(...)

You can use use the setNamedWhereClauseParam(...) method on the ViewObject interface (which extends oracle.jbo.RowSet) to set the value for bind variables. Please note this sets the value on default RowSet. In other words, this doesn't have any effect on the secondary RowSets that you/system generates.
ViewObject vo = am.findViewObject("EmployeesView1"); vo.setNamedWhereClauseParam("bindVarDeptId", new Number(10)); vo.executeQuery();
2. Set the Bind Variable value using ViewObject's VariableValueManager::setVariableValue(...)

VariableValueManager Ma…

Happy New Year 2018 !

We can't go back and change the beginning, but we always can start where we are and change the ending. Believe in yourself and you will be unstoppable!

Wishing you and your family a very happy new year 2018 !!!