AJAX Form Inputs are not updated post validation failure
hristoko Jul 11, 2008 12:09 AMI'm building a very simple AJAX crud application. User has a list, clicks a list value. Value is loaded. User changes the entity and saves. All AJAX, all in a single screen.
The problem I'm running into is that when the entity validation fails, and then a new item is selected, the form fields that did not fail the validation are not updated with the newly selected item's values. Very weird.
Mapped Superclass:
package com.metrics3.crud.entity; import java.io.Serializable; import java.util.Date; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import javax.persistence.Temporal; import javax.persistence.TemporalType; import org.jboss.seam.security.Identity; @MappedSuperclass public abstract class MappedEntity implements Serializable { private static final transient String UNKNOWN_USER = "UNKNOWN_USER"; private long id; private String createdBy; private Date createdOn; private String updatedBy; private Date updatedOn; @PrePersist public void prePersist() { createdBy = resolveUser(); createdOn = new Date(); updatedBy = createdBy; updatedOn = (Date) createdOn.clone(); } @PreUpdate public void preUpdate() { updatedBy = resolveUser(); updatedOn = (Date) createdOn.clone(); } private String resolveUser() { if(Identity.instance() != null && Identity.instance().getUsername() != null) return Identity.instance().getUsername(); return UNKNOWN_USER; } @Column(name = "ID", nullable = false) @Id @GeneratedValue(strategy = GenerationType.AUTO) public long getId() { return id; } public void setId(long id) { this.id = id; } @Column(name = "CREATED_BY", nullable = false, length = 100) public String getCreatedBy() { return createdBy; } public void setCreatedBy(String createdBy) { this.createdBy = createdBy; } @Column(name = "CREATED_ON", nullable = false) @Temporal(TemporalType.TIMESTAMP) public Date getCreatedOn() { return createdOn; } public void setCreatedOn(Date createdOn) { this.createdOn = createdOn; } @Column(name = "UPDATED_BY", nullable = false, length = 100) public String getUpdatedBy() { return updatedBy; } public void setUpdatedBy(String updatedBy) { this.updatedBy = updatedBy; } @Column(name = "UPDATED_ON", nullable = false) @Temporal(TemporalType.TIMESTAMP) public Date getUpdatedOn() { return updatedOn; } public void setUpdatedOn(Date updatedOn) { this.updatedOn = updatedOn; } }
Entity:
package com.metrics3.crud.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import org.hibernate.validator.Length; import org.hibernate.validator.NotEmpty; import org.hibernate.validator.NotNull; @Entity @Table(name = "DIMENSION3S", uniqueConstraints = @UniqueConstraint(columnNames = "NAME")) public class Dimension3 extends MappedEntity { private static final long serialVersionUID = -1972282192725291237L; private String name; private String description; @Override public void prePersist() { fixDescription(); super.prePersist(); } @Override public void preUpdate() { fixDescription(); super.preUpdate(); } private void fixDescription() { System.out.println("Dimension3.fixDescription"); if(getDescription() != null && getDescription().length() == 0) setDescription(null); } @Column(name = "NAME", nullable = false, length = 100) @NotNull @NotEmpty @Length(min = 1, max = 100) public String getName() { return name; } public void setName(String name) { this.name = name; } @Column(name = "DESCRIPTION", nullable = true, length = 4000) public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
EntityHome:
package com.metrics3.crud.session; import java.io.Serializable; import javax.faces.application.FacesMessage; import javax.persistence.EntityManager; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Begin; import org.jboss.seam.annotations.End; import org.jboss.seam.annotations.FlushModeType; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.RaiseEvent; import org.jboss.seam.annotations.Scope; import org.jboss.seam.faces.FacesMessages; import com.metrics3.crud.entity.Dimension3; @Name("dimension3Home") @Scope(ScopeType.CONVERSATION) public class Dimension3Home implements Serializable { private static final long serialVersionUID = -7705525888437978995L; @In private EntityManager entityManager; @In(create = true) private FacesMessages facesMessages; private Dimension3 instance; @Begin(join = true, flushMode = FlushModeType.MANUAL) public void create() { instance = new Dimension3(); } @Begin(join = true, flushMode = FlushModeType.MANUAL) public void read(long id) { instance = entityManager.find(Dimension3.class, id); } public void update() { try { entityManager.merge(instance); entityManager.flush(); } catch (Exception e) { e.printStackTrace(); FacesMessage("Dimension3Home.update.exception:"+e.getMessage()+":"+e.getClass())); } } @RaiseEvent("org.jboss.seam.afterTransactionSuccess.Dimension3") public void delete(long id) { entityManager.createQuery("delete from Dimension3 where id = :id").setParameter("id", id).executeUpdate(); clear(); } @End public void clear() { instance = null; } public Dimension3 getInstance() { return instance; } public void setInstance(Dimension3 instance) { this.instance = instance; } }
View:
<!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" xmlns:a="http://richfaces.org/a4j" template="layout/template.xhtml"> <ui:define name="body"> <h:messages id="globalMessages" globalOnly="true" styleClass="message" /> <a:form id="entityPanelForm" ajaxSubmit="true"> <rich:panel id="entityPanel" rendered="#{dimension3Home.instance != null}" > <f:facet name="header">Dimension 3 [#{dimension3Home.instance.id}]</f:facet> <s:decorate id="nameDecoration" template="layout/edit.xhtml" > <ui:define name="label">Name</ui:define> <h:inputText id="nameField" required="true" value="#{dimension3Home.instance.name}" size="50" /> </s:decorate> <s:decorate id="descriptionDecoration" template="layout/edit.xhtml" > <ui:define name="label">Description</ui:define> <h:inputTextarea id="descriptionField" value="#{dimension3Home.instance.description}" rows="10" cols="40" /> </s:decorate> <div class="actionButtons" style="clear:both"> <a:commandButton value="Create AJAX" action="#{dimension3Home.update}" reRender="entityPanelForm, entityTableForm, globalMessages" rendered="#{dimension3Home.instance.id == 0}" /> <a:commandButton value="Update AJAX" action="#{dimension3Home.update}" reRender="entityPanelForm, entityTableForm, globalMessages" rendered="#{dimension3Home.instance.id != 0}"/> <a:commandButton value="Delete AJAX" immediate="true" action="#{dimension3Home.delete(dimension3Home.instance.id)}" reRender="entityPanelForm, entityTableForm, globalMessages" rendered="#{dimension3Home.instance.id > 0}" /> <a:commandButton value="Close AJAX" reRender="entityPanelForm, globalMessages" action="#{dimension3Home.clear}" immediate="true" > </a:commandButton> <input type="reset"/> <br /> #{dimension3Home.instance.name} <br /> #{dimension3Home.instance.description} </div> </rich:panel> </a:form> <a:form id="entityTableForm" ajaxSubmit="true" ajaxSingle="true"> <rich:dataTable id="entityTable" width="auto" rows="10" value="#{dimension3Query.resultList}" var="e" > <f:facet name="header"> <h:panelGroup> Dimension3s [#{dimension3Query.resultList.size}] <a:commandLink value="{Reload}" style="margin-left: 20px;" action="#{dimension3Query.refresh}" reRender="entityTableForm, globalMessages" /> </h:panelGroup> </f:facet> <f:facet name="footer"> <a:commandLink value="Create AJAX" limitToList="true" action="#{dimension3Home.create}" reRender="entityPanelForm, globalMessages" /> </f:facet> <h:column> <f:facet name="header">Action</f:facet> <a:commandLink value="Open AJAX" limitToList="true" action="#{dimension3Home.read(e.id)}" reRender="entityPanelForm, globalMessages" /> </h:column> <h:column> <f:facet name="header">Id</f:facet> #{e.id} </h:column> <h:column> <f:facet name="header">Name</f:facet> #{e.name} </h:column> </rich:dataTable> <rich:datascroller for="entityTable"/> </a:form> </ui:define> </ui:composition>