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.

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:

  1. Add the xlmns for the custom compositions and ajax4jsf
  2. Change the h:messages to rich:messages so the FacesMessages can be automatically re-displayed
  3. Add a:support to the input fields
  4. Add the new actionButtons composition to take care of the form controls
  5. Add spinner.gif to WebContent/img (JBDS) or view/img (seam-gen) which can be downloaded here: spinner.gif and looks like: feel free to customize this.

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.