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.
This article describes how to break out your CRUD action buttons into a separate template and in the process, ajaxifying the form so reloading the page is no longer required for any operations.
See Creating Custom EL Functions as a starting point
Create another xml called META-INF/compositions.taglib.xml
<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC
"-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
"facelet-taglib_1_0.dtd">
<facelet-taglib>
<namespace>http://enhancements.seam/jsf</namespace>
<tag>
<tag-name>actionButtons</tag-name>
<source>../layout/enhancements/actionButtons.xhtml</source>
</tag>
</facelet-taglib>
Add that to your web.xml
<context-param>
<param-name>facelets.LIBRARIES</param-name>
<param-value>
/META-INF/elfunctions.taglib.xml; compositions.taglib.xml
</param-value>
</context-param>
Create the actionButtons.xhtml in WebContent/layout/enhancements (JBDS) or view/layout/enhancements (seam-gen) :
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:rich="http://richfaces.org/rich"
xmlns:a="http://richfaces.org/a4j"
xmlns:e="http://org.el.func/SeamFunc"
xmlns:s="http://jboss.com/products/seam/taglib">
<c:if test="${empty create}">
<c:set var="create" value="persist" />
</c:if>
<c:if test="${empty update}">
<c:set var="update" value="update" />
</c:if>
<c:if test="${empty delete}">
<c:set var="delete" value="remove" />
</c:if>
<c:if test="${empty managed}">
<c:set var="managed" value="managed" />
</c:if>
<c:if test="${empty reRender}">
<c:set var="reRender" value="#{controlKey}Form" />
</c:if>
<c:if test="${empty backingBean}">
<c:set var="backingBean" value="#{e:evalEl(e:concat(controlKey, 'Home'))}" />
</c:if>
<c:if test="${empty searchPage}">
<c:set var="searchPage" value="/#{controlKey}List.xhtml" />
</c:if>
<c:if test="${empty eventsQueue}">
<c:set var="eventsQueue" value="#{controlKey}Queue" />
</c:if>
<div class="actionButtons">
<table>
<tr>
<td>
<a:status id="formStatus">
<f:facet name="start">
<h:graphicImage value="img/spinner.gif" />
</f:facet>
</a:status>
</td>
<td>
<a:commandButton id="create"
value="Create"
action="#{backingBean[create]}"
rendered="#{!backingBean[managed]}"
reRender="#{reRender}"
eventsQueue="#{eventsQueue}"
ignoreDupResponses="true"
requestDelay="200"
status="formStatus" />
</td>
<td>
<a:commandButton id="update"
value="Update"
action="#{backingBean[update]}"
rendered="#{backingBean[managed]}"
reRender="#{reRender}"
eventsQueue="#{eventsQueue}"
ignoreDupResponses="true"
requestDelay="200"
status="formStatus" />
</td>
<td>
<a:commandButton id="delete"
value="Delete"
action="#{backingBean[delete]}"
rendered="#{backingBean[managed]}"
reRender="#{reRender}"
eventsQueue="#{eventsQueue}"
ignoreDupResponses="true"
requestDelay="200"
status="formStatus"
immediate="true" />
</td>
<td>
<s:button id="done"
value="Done"
propagation="end"
view="#{searchPage}"
rendered="#{backingBean[managed]}"
/>
</td>
<td>
<s:button id="cancel"
value="Cancel"
propagation="end"
view="#{searchPage}"
rendered="#{!backingBean[managed]}" />
</td>
</tr>
</table>
</div>
</ui:composition>
Now let's create an entity to play with, 'New Entity' with JBDS or 'seam create-entity' with seam-gen and name it Employee, this will create the associated xhtml and java files (Employee.java, EmployeeHome.java, EmployeeList.java, employee.xhtml, and employeeList.xhtml)
The default generated employee.xhtml looks like:
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:rich="http://richfaces.org/rich"
template="layout/template.xhtml">
<ui:define name="body">
<h:messages globalOnly="true" styleClass="message"/>
<h:form id="employeeForm">
<rich:panel>
<f:facet name="header">employee</f:facet>
<s:decorate id="nameDecoration" template="layout/edit.xhtml">
<ui:define name="label">Name</ui:define>
<h:inputText id="name" required="true"
value="#{employeeHome.instance.name}"/>
</s:decorate>
<div style="clear:both"/>
</rich:panel>
<div class="actionButtons">
<h:commandButton id="save"
value="Save"
action="#{employeeHome.persist}"
rendered="#{!employeeHome.managed}"/>
<h:commandButton id="update"
value="Save"
action="#{employeeHome.update}"
rendered="#{employeeHome.managed}"/>
<h:commandButton id="delete"
value="Delete"
action="#{employeeHome.remove}"
immediate="true"
rendered="#{employeeHome.managed}"/>
<s:button propagation="end"
id="done"
value="Done"
view="/employeeList.xhtml"/>
</div>
</h:form>
</ui:define>
</ui:composition>
Several things need to be modified to make the form ajaxified:
With the above modifications the new employee.xhtml looks like:
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
xmlns:custom="http://enhancements.seam/jsf"
template="layout/template.xhtml">
<ui:define name="body">
<rich:messages globalOnly="true" styleClass="message"/>
<h:form id="employeeForm">
<rich:panel>
<f:facet name="header">employee</f:facet>
<s:decorate id="nameDecoration" template="layout/edit.xhtml">
<ui:define name="label">Name</ui:define>
<h:inputText id="name" required="true" value="#{employeeHome.instance.name}">
<a:support event="onblur" reRender="nameDecoration" eventsQueue="employee" requestDelay="200" ignoreDupResponses="true"/>
</h:inputText>
</s:decorate>
<div style="clear:both"/>
</rich:panel>
<custom:actionButtons reRender="employeeForm" backingBean="#{employeeHome}" searchPage="/employeeList.xhtml" eventsQueue="employee" />
</h:form>
</ui:define>
</ui:composition>
If you create an entity, delete it, and try to create it again you'll notice you get:
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: org.domain.knowledgebase.entity.Employee
To fix this you need to override and @End the remove method in the EmployeeHome.java like so:
@Override
@Transactional
@End
public String remove() {
getEntityManager().remove( getInstance() );
getEntityManager().flush();
deletedMessage();
raiseAfterTransactionSuccessEvent();
return "removed";
}
Now, if you didn't follow Creating Custom EL Functions as a starting point you'll be getting exceptions that the xlmns e is not bound, so follow that, because we are going to eliminate even more code. If, and only if, you used seam-gen or JBDS to generate the Entity, which results in EmployeeHome, then you can do the following:
<custom:actionButtons controlKey="employee" />
This is possible due to the additional EL functions added in Creating Custom EL Functions namely, concat and evalEl.
Now all you need to do is add more input fields.