X

The Mobile & Digital Assistant Blog covers the latest in mobile and conversational AI development and engagement

How-to Access Selected Values in a Multi Select List

Frank Nimphius
Master Principal Product Manager

MAF Version: 2.1

Problem Statement

Using multi-item-select components, like amx:selectManyCheckbox, in a MAF application does not directly write the values back to the model but saves an array of selected row indexes in the inputValue attribute of the list binding. In addition, when users start selecting items in a muli-select list, each time they click on an item, the component valueChangeListener fires notifying the event listener in the managed bean about the selection. Both leads to the question of how to access the underlying data object (the row) for each selected item to read its properties.

For example, the assumed requirement for the sample application you can download at the end of this blog entry is to display the name of selected customers in a text box below the list component on the AMX page.

The Solution

The best part of a trip often is the journey because its where you see things and learn new skills. The same is true for the solution to the above challenge, as you will see when going through the implementation. For this solution, the decision was made to use the valueChangeListener and - for each selection - print the actual list values.

 

As shown in the image below, each selection in the list of customers is immediately mirrored in the text field below the list.

Multi Select List Sample Screen Shot

Note: If you don't need this information on each selection you would read the selected index values from the attribute binding (list binding) inputValue property upon e.g. a press on a button. In this case the code shown in this blog will guide you the way.

The implementation in the sample performs the following steps

  1. A valueChangeListener tracks all user item selection and de-selection
  2. For the selected items, the managed bean code reads the indexes from the component event
  3. The indexes are then changed from an array of String to List of Integer
  4. The list binding in the pageDef file (the binding) is accessed to obtain access to the list iterator
  5. For each index in the list of selected items, the iterator current row is set and then read

Note: Keep in mind that multiple binding artifacts may point to the same iterator. If you change the current row of a shared iterator, make sure to first read the current row key or index so it can be set back at the end of your action. In the sample the iterator is not shared and therefore the current row doesn't need to be set back.

Implementation Steps

The implementation is based on a POJO model, as this model is the most common in MAF. POJO models are usually created for REST services (using the REST Service Adapter in MAF), or when accessing the SOAP DC from Java, or when exposing the local SQLite database to the UI. In each of the cases mentioned before, you create a Java bean that then is exposed as a Data Control for use in MAF. From the data control you then create the UI using drag and drop from the Data Controls panel in JDeveloper or Oracle Enterprise Package for Eclipse (OEPE).

Note: This sample has customer data contained in the POJO data control (so to not require any dependency to a service, database or SQLite in MAF)

AMX view

The sample uses a single AMX view in the feature (which is good for demos and sample. For real MAF applications my personal preference is to use bounded task flows as the feature content). The page source of the list and the item mirroring the selections is shown below

<amx:selectManyCheckbox value="#{bindings.customers.inputValue}" 
         label="#{bindings.customers.label}" id="smc1"
         valueChangeListener="#{viewScope.SelectHelper.onValueChange}">
    <amx:selectItems value="#{bindings.customers.items}" id="si1"/>
</amx:selectManyCheckbox>
<amx:inputText label="Selected Values" id="it1" rows="7" inputType="text"
                   value="#{viewScope.SelectHelper.selectedValues}"/> 

To explain the important bits:

 

#{viewScope.SelectHelper.onValueChange} 
" valign="top"> Managed bean reference to the value change listener.  The managed bean doesn't hold state and thus is in the smallest scope available for MAF, which is viewScope
#{bindings.customers.items}
The customers reference is the list binding in the AMX pageDef file. Items gives you access to a list of items that then are iterated over by the component. This part doesn't need to be manually created by you and is generated by JDevelper or OEPE
#{bindings.customers.inputValue}
The inputValue property exists on the list binding and holds the value of the selected items (for the use case in which you need to access the selected row indexes in response to a command button or link action. As you see, the value is EL accessible and as such can be referenced from Java too using the AdfmfJavaUtilities class in MAF
#{viewScope.SelectHelper.selectedValues}

This EL is added to the input text field that shows the selected customer name and references a set/get method pair (bean properties) in the managed bean. The value change listener associated with the select component calls the set-method to post changes to the text field. A property change event is used notify the UI field to refresh

 

 

 

Note: most of the AMX page configuration is created for you when dragging and dropping a collection from the data control panel to the page. The only manual step is the creation of the value change listener and the input text field referencing the managed bean properties.

Managed Bean

The value change listener method - onValueChange(...) - in the managed bean is doing the real work. This is where the journey starts becoming exciting. The important parts of the code - in addition to the code comments, are explained further at the end

 

public void onValueChange(ValueChangeEvent valueChangeEvent) {
 //the newValue property of the valueChangeEvent shows an array of Strings 
 //with the selected values. This array is updated with each new selection 
 //or de-select
 Object[] newVal = (Object[]) valueChangeEvent.getNewValue();
 //the oldValue property of the valueChangeEvent shows an array of Strings 
 //with the values of the previous selection. This property allows you to 
 //tell what exactly has been changed within a new selection
 Object[]  oldVal = (Object[]) valueChangeEvent.getOldValue();
 //converting array of Strings to list of Integer to read the real 
 //objects from the list binding iterator
 ArrayList<Integer> selectedCustomers = new ArrayList<Integer>();      
 for (int i = 0; i < newVal.length; i++) {
     selectedCustomers.add(new Integer((String)newVal[i]));
 }
 //lists are represented by the  AmxAttributeBindingAmxAttributeBinding customerList = (AmxAttributeBinding) AdfmfJavaUtilities
                               .evaluateELExpression("#{bindings.customers}");
 StringBuffer selectedCustomerNames = new StringBuffer();
 //access the list iterator to first set the current row by the indexes obtained 
 //from the value change event and then read the row object, which in this sample
 //represents a customer
 AmxIteratorBinding amxListIterator =  customerList.getIteratorBinding();
 
 //the basic iterator in the AmxIteratorBinding is what we need to work with
  BasicIterator      basicIterator = amxListIterator.getIterator();
        
  for (Integer customerIndx : selectedCustomers) {
     //set new current row in list iterator
     basicIterator.setCurrentIndex(customerIndx.intValue());
     //get current row. Note that for POJO models you call getDataProvider and cast to 
     //the entity. For SOAP models you call getCurrentRow and cast it to GenericType 
     Customer customer = (Customer) basicIterator.getDataProvider();
     //for this sample, print selected customers into a text field on the page
     selectedCustomerNames.append("Customer on index  " + (customerIndx.intValue() + 1) + ": " + customer.getName()+"\n");
  }
  //update the input text field with the selection. The set-method of the 
  //selectedValue property uses the PropertyChane listener in MAF to trigger
  //the field to refresh
  this.setSelectedValues(selectedCustomerNames.toString());
}

Following table contains further explanations for parts of the code

 

 

valueChangeEvent.getNewValue() The list component has a "ValueChange" property that can be linked to a managed bean that defines a public method with the ValueChangeEvent as the only argument. The event argument gives developers access to the current selected items as well as the previously selected items, allowing them to tell the change. In the sample, the selected list is used only to lookup the associated binding object (aka. model objects) for more details. The getNewValue() method returns an array of String, where each string represents the index of a selected item in the list. The index is the same as in the binding layer, which makes it easy to lookup the data object.
AmxAttributeBinding There is no list binding in MAF (and I used teh term for simplicity in the article). List binds are attribute bindings and allow access to the list of items and the iterator. The iterator is created from the collection that in this example displays customer information. The attribute binding however does not display the full customer object and only knows of a value property (usually you select this as the PK of a object) and one to any display properties.
AmxIteratorBinding The iterator is an instance of AmcIteratorBinding, which is a wrapper for BasicIterator, which is the object instance that gives access to the data provider, which in the POJO case represents the data object (entity) representing a row.
basicIterator.getDataProvider(); The getDataProvider() method works for POJO models (PojoDC) wheras getCurrentRow provides equivalent access to the current row object for the SOAP DC. The code in the sample sets the current row for each selected item (using the item index) before accessing the row data object (entity)


Summary

This blog article and its sample shows one possible solution to work with multi select  lists in MAF. The use case covered is where the application requirement is to respond to any selections users make in the list. The use case in which the selected list is read after all items are selected is not covered. The handling for this is to access the list binding (which now you know is an attribute binding) to access the inputValue property for the submitted values. You then iterate over the values and set the current row in the BasicIterator using the keys you find in the array you obtain from the inputValue. The rest then remains the same


Download the Sample

The sample for this blog entry has been written for MAF 2.1 using JDeveloper. The sample code can be download from http://www.oracle.com//technetwork/developer-tools/adf/learnmore/multiselectlist-2423622.zip . In JDeveloper, open the MAF workspace and run the AMX page contained within. When the application shows up in the simulator, emulator or on-device, select items in the list to see the customer name - which is not part of the list - displaying in the text field. 

--------------

Disclaimer

The sample explained in the blog and provided for this blog entry is provided as is. The sample will not be upgraded automatically to newer versions of MAF and is also not supported by Oracle world wide customer support. Samples are community efforts that we support in a community effort: If you experience problems or have questions, please post a note to the MAF community forum on OTN:
https://community.oracle.com/community/oracle-mobile/oraclemaf/content?customTheme=otn

 

 

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.