When designing a screen or form, you may want to render a part of the page when clicking on a link or when choosing an element in a select. This page explains how this can be done in a XHTML template.
This page describes how to do so without having to define a Seam component, keeping the contextual information. This is better for reuse of templates (like in widgets) within the same page, as you do not have to define a new Seam component for every context variable on the page.
The context variable is kept by a nxu:valueHolder tag, which also exposes the corresponding value in the context.
Two examples are given:
- impact a given component with a static value
- impact a given component with a value taken on another component
Impacting with a Static Value
Use Case
When i click on a button, another element should be hidden (or shown).
Available Helper
The Seam Component selectionActions has a method named setStaticValue
that will retrieve attributes named selectedValue
and targetComponentId
from its originating tag. It will lookup the corresponding component in the tree and set the value given by the selectedValue
attribute (which can also be an EL expression).
Sample Template Excerpt
<ui:fragment
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:nxu="http://nuxeo.org/nxweb/util">
<h:form>
[...]
<a4j:outputPanel id="showContentHolderPanel">
<nxu:valueHolder id="showContentHolder"
defaultValue="false"
var="showContent">
<h:commandButton rendered="#{not showContent}" value="Show">
<f:attribute name="selectedValue" value="true" />
<f:attribute name="targetComponentId" value="showContentHolder" />
<nxu:actionListenerMethod value="#{selectionActions.setStaticValue}"/>
<f:ajax execute="@this" render="showContentHolderPanel" />
</h:commandButton>
<h:commandButton rendered="#{showContent}" value="Hide">
<f:attribute name="selectedValue" value="false" />
<f:attribute name="targetComponentId" value="showContentHolder" />
<nxu:actionListenerMethod value="#{selectionActions.setStaticValue}"/>
<f:ajax execute="@this" render="showContentHolderPanel" />
</h:commandButton>
<c:if test="#{showContent}">
My content
</c:if>
</nxu:valueHolder>
</a4j:outputPanel>
[...]
</h:form>
</ui:fragment>
Impacting with a Value Taken on Another Component
First Use Case
When I select an element in a drop down list, another element should be displayed depending on the selected value.
Available Helper
The Seam Component selectionActions has a method named setValueFromComponent
that will retrieve attributes named sourceComponentId
and targetComponentId
from its originating tag. It will lookup the corresponding source component in the tree, retrieve its current value, and set this value to the corresponding target component.
Sample Template Excerpt
This is an excerpt of the widget template displaying additional information about a selected flavor in local configuration:
<h:panelGroup>
<nxu:set var="flavors"
value="#{themeConfigurationActions.getAvailableFlavors(themeActions.defaultTheme)}"
cache="true">
<h:selectOneListbox id="#{widget.id}" value="#{field}">
<nxu:selectItems
var="flavor" value="#{flavors}"
itemValue="#{flavor.name}" itemLabel="#{messages[flavor.label]}" />
<f:attribute name="sourceComponentId" value="#{widget.id}" />
<f:attribute name="targetComponentId" value="#{widget.id}_valueHolder" />
<f:ajax execute="@this" render="#{widget.id}_preview"
listener="#{selectionActions.setValueFromComponent}"
id="#{widget.id}_ajax_select" />
</h:selectOneListbox>
</nxu:set>
<h:message for="#{widget.id}" id="#{widget.id}_message"
styleClass="errorMessage" />
</h:panelGroup>
<nxu:valueHolder id="#{widget.id}_valueHolder"
var="selectedFlavorName"
defaultValue="#{origSelectedFlavorName}"
submitValue="false">
<a4j:outputPanel id="#{widget.id}_preview" layout="block">
<nxu:set var="selectedFlavor"
value="#{themeActions.getFlavor(selectedFlavorName)}"
cache="true">
<c:choose>
<c:when test="#{! empty selectedFlavor.logo.previewPath}">
<h:graphicImage value="#{selectedFlavor.logo.previewPath}" class="palettePreviewLogo" />
</c:when>
<c:otherwise>
<c:choose>
<c:when test="#{! empty selectedFlavor.logo.path}">
<h:graphicImage value="#{selectedFlavor.logo.path}" class="palettePreviewLogo" />
</c:when>
<c:otherwise>
#{messages['label.local.configuration.theme.flavorSelection.noPreviewAvailable']}
</c:otherwise>
</c:choose>
</c:otherwise>
</c:choose>
</nxu:set>
</a4j:outputPanel>
</nxu:valueHolder>
Second Use Case
In a form, when i check some checkbox, additional input fields are shown.
Available Helper
The Seam Component selectionActions has a method named setValueFromComponent
that will retrieve attributes named sourceComponentId
and targetComponentId
from its originating tag. It will lookup the corresponding source component in the tree, retrieve its current value, and set this value to the corresponding target component.
Also, additional input fields to show are declared as standard sub-widgets to a widget of type template
which is displaying the checkbox and holding the re-render logics.
Sample Widget Template
<ui:fragment
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:nxu="http://nuxeo.org/nxweb/util"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:nxl="http://nuxeo.org/nxforms/layout">
<h:selectBooleanCheckbox id="#{widget.id}" value="#{field_1}">
<f:attribute name="sourceComponentId" value="#{widget.id}" />
<f:attribute name="targetComponentId" value="#{widget.id}_valueholder"/>
<f:ajax execute="@this" render="#{widget.id}_panel"
listener="#{selectionActions.setValueFromComponent}" />
</h:selectBooleanCheckbox>
<a4j:outputPanel id="#{widget.id}_panel" layout="block">
<nxu:valueHolder id="#{widget.id}_valueholder"
var="show"
defaultValue="#{field_1}">
<ui:fragment rendered="#{show}">
<nxl:subWidget>
<nxl:widget widget="#{widget}" value="#{value}" />
</nxl:subWidget>
</ui:fragment>
</nxu:valueHolder>
</a4j:outputPanel>
</ui:fragment>
To use this template:
- define a widget of type
template
and if you're in Studio, use an advancedGeneric Widget
to control fields definitions - add two fields for this widget: the first field should be empty, so that subwidgets are still bound to the same document (as first field mapping will be taken into account when building the subwidgets field binding), and second field should match the property holding the boolean value – note that persisting this value is useful for edition, so that persisted value can be taken into account when displaying the form again.
Here is a sample contribution:
<extension target="org.nuxeo.ecm.platform.forms.layout.WebLayoutManager"
point="widgets">
<widget name="checkboxReRender" type="template">
<labels>
<label mode="any">My widget</label>
</labels>
<fields>
<field></field>
<field>myschema:mybooleancheck</field>
</fields>
<properties mode="any">
<property name="template">
/widgets/checkbox_re_render.xhtml
</property>
</properties>
<subWidgets>
<widget name="sub" type="text">
<fields>
<field>myschema:mytext</field>
</fields>
</widget>
</subWidgets>
</widget>
</extension>
Refined version of this template when using it inside the bulk edit form (to make sure sub widget value is handled by the bulk edit too):
<ui:fragment
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:nxu="http://nuxeo.org/nxweb/util"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:nxl="http://nuxeo.org/nxforms/layout">
<h:selectBooleanCheckbox id="#{widget.id}" value="#{field_1}"
onclick="#{widgetProperty_onclick}">
<f:attribute name="sourceComponentId" value="#{widget.id}" />
<f:attribute name="targetComponentId" value="#{widget.id}_valueholder"/>
<f:ajax execute="@this" render="#{widget.id}_panel"
listener="#{selectionActions.setValueFromComponent}" />
</h:selectBooleanCheckbox>
<a4j:outputPanel id="#{widget.id}_panel" layout="block">
<nxu:valueHolder id="#{widget.id}_valueholder"
var="show"
defaultValue="#{field_1}">
<ui:fragment rendered="#{show}">
<nxl:subWidget>
<c:set var="contextDataKey"
value="bulkEdit/#{nxl:fieldDefinitionsAsString(widget.fieldDefinitions)}" />
<h:inputHidden
id="#{widget.id}_bulk_key"
value="#{value.contextData[contextDataKey]}">
<f:attribute name="value" value="true" />
<f:converter converterId="javax.faces.Boolean" />
</h:inputHidden>
<nxl:widget widget="#{widget}" value="#{value}" />
</nxl:subWidget>
</ui:fragment>
</nxu:valueHolder>
</a4j:outputPanel>
</ui:fragment>