I have a string that is being set from selectonemenu. The values in the menu depend on the selection in another menu, and are populated using JavaScript. Everything behaves fine on the front end (the menus update appropriately, etc), but when we submit the changes, the string is not being set. The menu has a converter, and getAsObject is being called. Does anybody know why the string isn't being set?
Edit.jspx
<h:selectOneMenu value="#{detectorInstance.type}" converter="detectorConverter" onchange="updateSubTypes(this, '#{applicationName}')">
<s:selectItems value="#{detectors.detectors}" var="detectorSelection" label="#{detectorSelection.name}" noSelectionLabel="Unknown"/>
</h:selectOneMenu>
<a4j:outputPanel id="detectorSubType">
<h:selectOneMenu value="#{detectorInstance.detectorSubType}" converter="detectorSubTypeConverter"/>
</a4j:outputPanel>
DetectorSubTypeConverter.java
@Name("detectorSubTypeConverter")
@BypassInterceptors
@org.jboss.seam.annotations.faces.Converter
public class DetectorSubTypeConverter implements Converter {
public Object getAsObject(final FacesContext facesContext, final UIComponent uiComponent, final String identifier) {
return identifier;
}
public String getAsString(final FacesContext facesContext, final UIComponent uiComponent, final Object o) {
return o.toString();
}
}
detectors.js
function updateSubTypes(typeSelect, appName) {
var jsonRoot = appName + "/services/rest/detectors/detectors";
jQuery.getJSON(jsonRoot, function(data) {
var subTypes = getSubTypes(typeSelect, data);
var subTypeSelect = typeSelect.next().firstChild;
subTypeSelect.options.length = 0;
for (var i = 0; i < subTypes.length; i++) {
subTypeSelect.options[i] = new Option(subTypes[i], subTypes[i], false, false);
}
});
}
function getSubTypes(typeSelect, detectors) {
var selection = typeSelect.options[typeSelect.selectedIndex].text;
var subtypes = new Array();
for (var i = 0; i < detectors.length; i++) {
var uuid = detectors[i]['detector']['name'];
if (selection == uuid) {
for (var j = 0; j < detectors[i]['detector']['subtypes'].length; j++) {
subtypes[j] = detectors[i]['detector']['subtypes'][j]['name'];
}
}
}
return subtypes;
}
I don't think populating items for selectOneMenu by JS is the right way to work with Seam/JSF. The reason is that when you show a dropdown with a list of items, Seam/JSF will check if the submitted value is in the list for security reason.
The right way would be:
The idea is as follows:
<h:selectOneMenu value="#{detectorInstance.type}" converter="detectorConverter" onchange="updateSubTypes(this, '#{applicationName}')">
<s:selectItems value="#{detectors.detectors}" var="detectorSelection" label="#{detectorSelection.name}" noSelectionLabel="Unknown"/>
<a4j:ajax event="click" execute="@this" render="detectorSubType"
listener="#{detectorInstance.onTypeChange}"/>
</h:selectOneMenu>
<a4j:outputPanel id="detectorSubType">
<h:selectOneMenu value="#{detectorInstance.detectorSubType}" converter="detectorSubTypeConverter">
<f:selectItem value="#{detectorInstance.types}" />
</h:selectOneMenu>
</a4j:outputPanel>
The items for second dropdown is from detectorInstance.types
. In detectorInstance.onTypeChange
, you recalculate detectorInstance.types
and then rerender it by render="detectorSubType"