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>