Help

Built with Seam

You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.

Is it possible to create an editor in JSF to modify the values of a java.util.Map directly? Assume that the UIData component iterates over the entries of a Map<String, String> held in a variable named properties and each row includes an input bound to the entry's value property:

<rich:dataTable value="#{properties}" var="mapEntry">	
  <rich:column>
  <f:facet name="header">Name</f:facet>
    <h:outputLabel for="value" value="#{mapEntry.key}"/>
  </rich:column>
  <rich:column>
  <f:facet name="header">Value</f:facet>
    <h:inputText id="value" value="#{mapEntry.value}"/>
  </rich:column>
</rich:dataTable>

The problem is that Map.Entry is not writable, so you cannot bind directly to the value property. Thankfully, there is a way to accomplish this with Seam if we introduce a generic Seam component.

You need is two context variables, one that holds the native map and one that holds Seam's DataModel wrapper. The native map is outjected when the DataModel wrapper is resolved via a factory. Here's a Seam component, named propertyEditor, which produces these two context variables:

@Name("propertyEditor")
@Scope(ScopeType.CONVERSATION)
public class PropertyEditorAction implements Serializable {
    
    @DataModel private Map<String, String> propertiesModel;
	
    @Out private Map<String, String> properties;
	
    @Begin(join = true) @Factory("propertiesModel")
    public void retrieveProperties() {
        this.propertiesModel = this.properties;
    }
	
    public void save() {}
	
    public void setProperties(Map<String, String> properties) {
        this.properties = properties;
    }
}

A conversation is started to ensure that the DataModel remains stable while the map is being edited.

You now iterate over the DataModel wrapper in the UIData component:

<rich:dataTable value="#{propertiesModel}" var="mapEntry">
  <rich:column>
  <f:facet name="header">Name</f:facet>
    <h:outputLabel for="value" value="#{mapEntry.key}"/>
  </rich:column>
  <rich:column>
  <f:facet name="header">Value</f:facet>
    <h:inputText id="value" value="#{properties[mapEntry.key]}"/>
  </rich:column>
</rich:dataTable>

Notice how I have the native map and the data model working together to negotiate the update.