Search code examples
ajaxjsf-2selecteditemselectmanycheckbox

How can I get the clicked item in the ajax method?


Suppose the code of this page:

<h:form prependId="false" id="form">

    <h:selectManyCheckbox id="checkBoxList" value="#{backedBean.lstIdSelectedItems}" layout="pageDirection">
        <f:selectItems value="#{backedBean.lstAvailableItems}" var="item" itemLabel="#{item.label}" itemValue="#{item.value}" />
        <f:ajax listener="#{backedBean.itemClicked}" />
    </h:selectManyCheckbox>

</h:form>

And the code of a session managed bean:

public class BackedBean implements Serializable {
   private List<SelectItem> lstAvailableItems;
   private List<Long> lstIdSelectedItems;

public BackedBean() {
    lstAvailableItems = new ArrayList<SelectItem>();
    lstIdSelectedItems = new ArrayList<Long>();
}

@PostConstruct
private void postConstruct(){
    for (int i = 0; i < 10; i++) {
        SelectItem item = new SelectItem(new Long(i), "CHKID " + i);
        lstAvailableItems.add(item);
    }
}

public void itemClicked(AjaxBehaviorEvent ae){
    HtmlSelectManyCheckbox uiCmp = (HtmlSelectManyCheckbox)ae.getSource();

    // (1) Here I would like to get the ID of the item that has been clicked.

}

In (1) I would like to get the ID of the element that has been clicked by the user. I can see in the lstIdSelectedItems array list the IDs of all elements selected by the user, but how can I get the ID of the element that the user has clicked?

I have tried to use the f:attribute tag inside of the selectManyCheckbox, but the attribute is not in the component map when the ajax listener method is called in the backed bean. I have used this, but doesn't work:

<h:selectManyCheckbox id="checkBoxList" value="#{backedBean.lstIdSelectedItems}" layout="pageDirection">
    <f:selectItems value="#{backedBean.lstAvailableItems}" var="item" itemLabel="#{item.label}" itemValue="#{item.value}">
        <f:attribute name="clicked" value="#{item.value}" />
    </f:selectItems>
    <f:ajax listener="#{backedBean.itemClicked}" />
</h:selectManyCheckbox>

Any ideas?

Regards.


Solution

  • You're thus interested in the actual value change and not only in the new value. Bring in a valueChangeListener which compares the old value with the new value and prepares some properties which the ajax listener method could intercept on.

    E.g.

    <h:selectManyCheckbox value="#{bean.selectedItems}" valueChangeListener="#{bean.selectedItemsChanged}" converter="javax.faces.Long">
        <f:selectItems value="#{bean.availableItems}" />
        <f:ajax listener="#{bean.itemSelected}" />
    </h:selectManyCheckbox>
    

    with

    private Map<String, Long> availableItems; // +getter
    private List<Long> selectedItems; // +getter+setter
    private Long selectedItem;
    private boolean selectedItemRemoved;
    
    @PostConstruct
    public void init() {
        availableItems = new LinkedHashMap<String, Long>();
    
        for (long i = 0; i < 10; i++) {
            availableItems.put("CHKID " + i, i);
        }
    }
    
    public void selectedItemsChanged(ValueChangeEvent event) {
        List<Long> oldValue = (List<Long>) event.getOldValue();
        List<Long> newValue = (List<Long>) event.getNewValue();
    
        if (oldValue == null) {
            oldValue = Collections.emptyList();
        }
    
        if (oldValue.size() > newValue.size()) {
            oldValue = new ArrayList<Long>(oldValue);
            oldValue.removeAll(newValue);
            selectedItem = oldValue.iterator().next();
            selectedItemRemoved = true;
        }
        else {
            newValue = new ArrayList<Long>(newValue);
            newValue.removeAll(oldValue);
            selectedItem = newValue.iterator().next();
            selectedItemRemoved = false;
        }
    }
    
    public void itemSelected(AjaxBehaviorEvent event) {
        System.out.println("Selected item: " + selectedItem);
        System.out.println("Selected item removed? " + selectedItemRemoved);
    }