listShuttle - how to change sourceValue
captainvoid Dec 16, 2008 7:07 AMHi,
I have two listshuttles on my page, both with the same initial sourceValue. The targetValues are bound to properties of my model object.
What I want to achieve is that the user cannot select the same value in both listShuttles.
So I want to update the sourceValue of the second listShutttle if the user selects (or deselects) values in the first listShuttle and vice-versa.
This is my page:
<rich:tabPanel id="levelTabPanel" immediate="false" switchType="client"> <rich:tab id="low" switchType="client" label="#{messages['authenticationMethodGroup.tab.low.title']}" > <rich:panel id="mechanismListLow" header="#{messages['authenticationMethodGroup.tabs.authNMechanisms.title']}"> <rich:listShuttle id="methodShuttleLow" sourceValue="#{methodListController.availableMethods}" targetValue="#{refData.leveledGroupLow.authenticationMethods}" var="item" showButtonLabels="false" listHeight="300" listWidth="300" immediate="true" valueChangeListener="#{methodListController.methodSelected}" sourceCaptionLabel="#{messages['authenticationMethodGroup.tabs.authNMechanisms.available']}" targetCaptionLabel="#{messages['authenticationMethodGroup.tabs.authNMechanisms.selected']}" converter="#{refDataConverter}" > <rich:column> <a4j:outputPanel> <h:outputText value="#{item.description}"></h:outputText> <rich:toolTip mode="client" followMouse="true" showDelay="500" layout="block" value="#{item.id}" /> </a4j:outputPanel> </rich:column> <a4j:support event="onlistchanged" ajaxSingle="true" immediate="true" bypassUpdates="true" reRender="methodShuttleLow, methodShuttleMedium" /> </rich:listShuttle> </rich:panel> </rich:tab> <rich:tab id="medium" switchType="client" label="#{messages['authenticationMethodGroup.tab.medium.title']}" > <rich:panel id="mechanismListMedium" header="#{messages['authenticationMethodGroup.tabs.authNMechanisms.title']}"> <rich:listShuttle id="methodShuttleMedium" sourceValue="#{methodListController.availableMethods}" targetValue="#{refData.leveledGroupMedium.authenticationMethods}" var="item" showButtonLabels="false" listHeight="300" listWidth="300" immediate="true" valueChangeListener="#{methodListController.methodSelected}" sourceCaptionLabel="#{messages['authenticationMethodGroup.tabs.authNMechanisms.available']}" targetCaptionLabel="#{messages['authenticationMethodGroup.tabs.authNMechanisms.selected']}" converter="#{refDataConverter}" > <rich:column> <a4j:outputPanel> <h:outputText value="#{item.description}"></h:outputText> <rich:toolTip mode="client" followMouse="true" showDelay="500" layout="block" value="#{item.id}" /> </a4j:outputPanel> </rich:column> <a4j:support event="onlistchanged" ajaxSingle="true" immediate="true" bypassUpdates="true" reRender="methodShuttleLow, methodShuttleMedium" /> </rich:listShuttle> </rich:panel> </rich:tab> </rich:tabPanel>
my bean:
@Name("methodListController") @Scope(ScopeType.PAGE) @AutoCreate public class MethodListController { @Logger private Log logger; private List<AuthenticationMethod> availableMethods; @Create public void init() { // initialize this.availableMethods // querying the database // ... logger.debug("-----> initialize availableMethods -> [#0]", availableMethods); } public List<AuthenticationMethod> getAvailableMethods() { logger.debug("-----> 'getAvailableMethods' called -> return [#0]", availableMethods); return availableMethods; } public void setAvailableMethods(List<AuthenticationMethod> availableMethods) { logger.debug("-----> 'setAvailableMethods' called [#0]", availableMethods); this.availableMethods = availableMethods; } /** * Value change listener for list shuttles 'methodShuttleLow' * and 'methodShuttleMedium' which updates this.availableMethods. * * @param event */ @SuppressWarnings("unchecked") public void methodSelected(ValueChangeEvent event) { logger.debug("list shuttle value changed.."); Object[] oldValue = (Object[]) event.getOldValue(); Object[] newValue = (Object[]) event.getNewValue(); List<AuthenticationMethod> oldAvailableValues = (List<AuthenticationMethod>) oldValue[0]; logger.debug("oldAvailableMethods [#0]", oldAvailableValues); List<AuthenticationMethod> newAvailableValues = (List<AuthenticationMethod>) newValue[0]; logger.debug("newAvailableMethods [#0]", newAvailableValues); if (oldAvailableValues.size() > newAvailableValues.size()) { // method(s) selected List<AuthenticationMethod> selectedMethods = new ArrayList<AuthenticationMethod>(oldAvailableValues); selectedMethods.removeAll(newAvailableValues); logger.debug("===> selectedMethods [#0]", selectedMethods); // update availableMethods availableMethods.removeAll(selectedMethods); } else { // method(s) deselected List<AuthenticationMethod> deselectedMethods = new ArrayList<AuthenticationMethod>(newAvailableValues); deselectedMethods.removeAll(oldAvailableValues); logger.debug("===> deselectedMethods [#0]", deselectedMethods); // update availableMethods availableMethods.addAll(deselectedMethods); } } }
As you can see I'm using a ValueChangeListener which updates the available source values and the a4j:support rerenders both listShuttles.
The problem is that this only works fine the first time. The second time something changes in one of the listShuttles, I get a "invalid value" validation error for a value that I added to the list of available source values. I know - equals/hashcode method and so on, but this is all implemented properly!
What I think is that for changing the source value of a listShuttle something more has to be done and this is what I am missing here.
I already tried with creating the list shuttles in Java (use binding) and then update the source value programmatically (htmlListShuttle.setSourceValue(), but no success, same error.
Could somebody help me?