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.