smolarek999
smolarek999

Reputation: 519

Dynamic reload of items in select many component

I want to achieve sth similar to http://www.primefaces.org/showcase/ui/pprSelect.jsf but i need a collection of double-combos, so i wrapped it in ui:repeat

I need on the backend check which element from collection of double-combos was changed and what I need to reload. For communication is used p:ajax as in the example, but AjaxBehaviorEvent not bring me any idea of index of element ( i mean index of double-combos element generated by ui:repeat)

My client code, the idea is to update bean:selectedIndex everytime when a ajax event will be raised ( on change value of selectOneMenu ), and value of bean:selectedIndex will be set as index of changed selectOneMenu

private List<State> productStates
private int selectedIndex;
private List<Group> groups;
private Map<Integer, Collection<Device>> availableDevicesMap;
<ui:repeat var="state" value="#{bean.productStates}" varStatus="iter">

    <p:selectOneMenu id="devGroup" value="#{state.group}">  
        <f:selectItems value="#{bean.groups}"  />  
            <p:ajax  update="refreshable"  process="devGroup, @this"  listener="#{bean.refreshDevicesForState}" >
                    <f:setPropertyActionListener target="#{bean.selectedIndex}" value="#{iter.index}"/>
            </p:ajax>                                    
    </p:selectOneMenu>

    <!-- THIS WILL BE UPDATED -->
    <h:panelGroup id="refreshable">
            <p:selectManyButton  id="devices" value="#{state.devices}" >  
                <f:selectItems value="#{bean.availableDevicesMap[status.index]}"   />  
            </p:selectManyButton> 
    </h:panelGroup>
</ui:repeat>

Backend which doesn't work as expected. setPropertyActionListener is not invoked and selectOneMenu component hasn't got selected group as value

public refreshDevicesForState(AjaxBehaviorEvent e) {
        SelectOneMenu menu = (SelectOneMenu)e.getComponent();
    // this value is not as selected on frontend
        Group group = (Group)menu.getValue();

    // selectedIndex will not be set, so I assume that setPropertyActionListener didn't invoked
    availableDevicesMap.put(selectedIndex, group.getDevices());
}

I tried also with code below which works but in my opinion it is ugly

// id will be grandpaId:parentId:index:myId 
String selectedIndex = IdHelper.getIdPart(e.getComponent().getClientId(), -2);
State state = productStates.get(Integer.parseInt(selectedIndex));

I am using latest primefaces on glassfish and Mojarra as jsf reference implementation

Thank you for any help

In more general sense:

I have list of objects on backed bean, lets say Cars

List<Car> cars

on frontent I iterate over them and create select brand and select model combos for every car. When user select brand for i.e 4th car i want to get to know on backend that 4th car will be changed and i will reload list of available model for this one car

 <ui:repeat var="state" value="#{bean.cars}" >
    <p:selectOneMenu id="brands"/>// select brand

    <p:selectOneMenu "models"/>// show available models depends on selected brand
 </ui:repeat>

How to handle it correct in the JSF world ?

Upvotes: 2

Views: 3652

Answers (1)

steve
steve

Reputation: 625

My first suggestion is to use converter for Group. SelectOneMenu cannot set custom class, only with the help of a converter. (an example is at autocomplete: http://www.primefaces.org/showcase/ui/autoCompletePojo.jsf)

Second, in your bean handler, productStates variable contains already the selected values (of selectOneMenus). You can use it easier, than access it from the event.

If the values of selectOneMenus depend on State, you have to modify this:

<f:selectItems value="#{bean.groups}"  />

to be able to express which group values should be displayed.

If you want debug it (without eclipse debugging), you can use messages, for example:

add this to xhtml:

<p:growl id="msgs" showDetail="true"/>

and in bean:

public refreshDevicesForState(AjaxBehaviorEvent e) {

 ...
 FacesMessage msg = new FacesMessage("Selected", "any debug info" + productStates.get(0).getGroup());  
 FacesContext.getCurrentInstance().addMessage(null, msg);  

}

I modified my answer, according to your mods. I would do it this way:

xhtml:

<ui:repeat var="state" value="#{bean.productStates}" varStatus="iter">

 <p:selectOneMenu id="devGroup#{iter.index}" value="#{state.group}"
        valueChangeListener="#{bean.updateSubProperty}" immediate="true">  
  <f:selectItems value="#{bean.groups}"  />  
  <f:attribute name="index" value="#{iter.index}" />
 </p:selectOneMenu>

 <p:selectOneMenu id="subDevGroup#{iter.index}">
  ...
 </p:selectOneMenu>
</ui:repeat>

bean:

 public void updateSubProperty(ValueChangeEvent vce) {
  String index = vce.getComponent().getAttributes().get("index").toString();
  int i = Integer.parseInt(index); //this is the index of the selected selectOneMenu
  ///...

  //update sub selectOneMenu
  RequestContext.getCurrentInstance().update("subDevGroup" + index);
 }

Upvotes: 2

Related Questions