Total Control Edit (1)

This page demonstrates how to edit an object without using BeanEditForm.
The cost is more lines of code in the template. The benefit is total control of the content, layout and style.

(required, up to 10 characters)

(required, up to 10 characters)

(required)

(required, dd/mm/yyyy)

  Refresh
Person is a JPA Entity Bean and PersonFinderService is an EJB3 Session Bean.

References: .form-horizontal, .col-, TextField, DateField, Hidden.

Home

The source for IPersonFinderServiceLocal, IPersonManagerServiceLocal, and @EJB is shown in the Session Beans and @EJB examples.


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- We need a doctype to allow us to use special characters like &nbsp; 
     We use a "strict" DTD to make IE follow the alignment rules. -->
     
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd">
<body class="container">
    <h3>Total Control Edit (1)</h3>

    This page demonstrates how to edit an object <em>without</em> using BeanEditForm. <br/>
    The cost is more lines of code in the template. The benefit is total control of the content, layout and style.
    
    <div class="eg">
        <t:form class="form-horizontal" t:id="person" validate="person">
            <t:if test="person">
                <!-- If optimistic locking is not needed then comment out this next line. It works because Hidden fields are part of the submit. -->
                <t:hidden value="person.version"/>
    
                <div class="form-group">
                    <t:label for="firstName" class="col-sm-3"/>
                    <div class="col-sm-3">
                        <t:textfield t:id="firstName" value="person.firstName"/>
                    </div>
                    <div class="col-sm-6">
                        <p class="form-control-static">
                            <span class="text-muted">(required, up to 10 characters)</span>
                        </p>
                    </div>
                </div>
                <div class="form-group">
                    <t:label for="lastName" class="col-sm-3"/>
                    <div class="col-sm-3">
                        <t:textfield t:id="lastName" value="person.lastName"/>
                    </div>
                    <div class="col-sm-6">
                        <p class="form-control-static">
                            <span class="text-muted">(required, up to 10 characters)</span>
                        </p>
                    </div>
                </div>
                <div class="form-group">
                    <t:label for="region" class="col-sm-3"/>
                    <div class="col-sm-3">
                        <t:select t:id="region" value="person.region"/>
                    </div>
                    <div class="col-sm-6">
                        <p class="form-control-static">
                            <span class="text-muted">(required)</span>
                        </p>
                    </div>
                </div>
                <div class="form-group">
                    <t:label for="startDate" class="col-sm-3"/>
                    <div class="col-sm-3">
                        <t:datefield t:id="startDate" value="person.startDate" format="dd/MM/yyyy"/>
                    </div>
                    <div class="col-sm-6">
                        <p class="form-control-static">
                            <span class="text-muted">(required, dd/mm/yyyy)</span>
                        </p>
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-sm-3 col-sm-offset-3">
                        <t:submit value="Save"/>
                        &nbsp;
                        <t:eventlink event="refresh" class="btn btn-default">Refresh</t:eventlink>
                    </div>
                </div>
    
                <t:errors globalOnly="true"/>
            </t:if>
            <t:if test="!person">
                <div class="alert alert-danger">Person ${personId} does not exist.</div>
            </t:if>
        </t:form>
    </div>
    
    Person is a JPA Entity Bean and PersonFinderService is an EJB3 Session Bean. <br/><br/>
    
    References: 
    <a href="http://getbootstrap.com/css/#forms-horizontal">.form-horizontal</a>, 
    <a href="http://getbootstrap.com/css/#grid">.col-</a>, 
    <a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/corelib/components/TextField.html">TextField</a>, 
    <a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/corelib/components/DateField.html">DateField</a>, 
    <a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/corelib/components/Hidden.html">Hidden</a>.<br/><br/>
    
    <t:pagelink page="Index">Home</t:pagelink><br/><br/>
    
    The source for IPersonFinderServiceLocal, IPersonManagerServiceLocal, and @EJB is shown in the Session Beans and @EJB examples.<br/><br/>
    
    <t:tabgroup>
        <t:sourcecodetab src="/web/src/main/java/jumpstart/web/pages/examples/input/TotalControlEdit1.tml"/>
        <t:sourcecodetab src="/web/src/main/java/jumpstart/web/pages/examples/input/TotalControlEdit1.java"/>
        <t:sourcecodetab src="/web/src/main/resources/META-INF/assets/css/examples/plain.css"/>
        <t:sourcecodetab src="/business/src/main/java/jumpstart/business/domain/person/Person.java"/>
        <t:sourcecodetab src="/business/src/main/java/jumpstart/business/domain/person/Regions.java"/>
    </t:tabgroup>
</body>
</html>


package jumpstart.web.pages.examples.input;

import javax.ejb.EJB;

import jumpstart.business.domain.person.Person;
import jumpstart.business.domain.person.iface.IPersonFinderServiceLocal;
import jumpstart.business.domain.person.iface.IPersonManagerServiceLocal;
import jumpstart.util.ExceptionUtil;

import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.InjectComponent;
import org.apache.tapestry5.annotations.InjectPage;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.corelib.components.Form;
import org.apache.tapestry5.corelib.components.TextField;

// The @Import tells Tapestry to put a link to the file in the head of the page so that the browser will pull it in. 
@Import(stylesheet = "css/examples/plain.css")
public class TotalControlEdit1 {

    // The activation context

    @Property
    private Long personId;

    // Screen fields

    @Property
    private Person person;

    // Other pages

    @InjectPage
    private TotalControlEdit2 page2;

    // Generally useful bits and pieces

    @InjectComponent("person")
    private Form form;

    @InjectComponent("firstName")
    private TextField firstNameField;

    @EJB
    private IPersonFinderServiceLocal personFinderService;

    @EJB
    private IPersonManagerServiceLocal personManagerService;

    // The code

    // onActivate() is called by Tapestry to pass in the activation context from the URL.

    void onActivate(Long personId) {
        this.personId = personId;
    }

    // onPassivate() is called by Tapestry to get the activation context to put in the URL.

    Long onPassivate() {
        return personId;
    }

    // Form bubbles up the PREPARE_FOR_RENDER event during form render.

    void onPrepareForRender() {

        // If fresh start, make sure there's a Person object available.

        if (form.isValid()) {
            person = findPerson(personId);
            // Handle null person in the template (with an If component).
        }

    }

    // Form bubbles up the PREPARE_FOR_SUBMIT event during form submission.

    void onPrepareForSubmit() {

        // Get objects for the form fields to overlay.
        person = findPerson(personId);

        if (person == null) {
            form.recordError("Person has been deleted by another process.");
            // Instantiate an empty person to avoid NPE in the Form.
            person = new Person();
        }
    }

    void onValidateFromPerson() {

        if (person.getFirstName() != null && person.getFirstName().equals("Acme")) {
            form.recordError(firstNameField, firstNameField.getLabel() + " must not be Acme.");
        }

        if (personId == 2 && !person.getFirstName().equals("Mary")) {
            form.recordError(firstNameField, firstNameField.getLabel() + " for this person must be Mary.");
        }

        if (form.getHasErrors()) {
            // We get here only if a server-side validator detected an error.
            return;
        }

        try {
            personManagerService.changePerson(person);
        }
        catch (Exception e) {
            // Display the cause. In a real system we would try harder to get a user-friendly message.
            form.recordError(ExceptionUtil.getRootCauseMessage(e));
        }
    }

    Object onSuccess() {
        page2.set(personId);
        return page2;
    }

    void onRefresh() {
        // By doing nothing the page will be displayed afresh.
    }

    private Person findPerson(Long personId) {
        // Ask business service to find Person
        Person person = personFinderService.findPerson(personId);

        if (person == null && personId < 4) {
            throw new IllegalStateException("Database data has not been set up!");
        }

        // Handle null person in the template (with an If component).

        return person;
    }
}


.eg {
                margin: 20px 0;
                padding: 14px;
                border: 1px solid #ddd;
                border-radius: 6px;
                -webkit-border-radius: 6px;
                -mox-border-radius: 6px;
}


package jumpstart.business.domain.person;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;


/**
 * The Person entity.
 */
@Entity
@SuppressWarnings("serial")
public class Person implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(nullable = false)
    private Long id;

    @Version
    @Column(nullable = false)
    private Integer version;

    @Column(length = 10, nullable = false)
    @NotNull
    @Size(max = 10)
    private String firstName;

    @Column(length = 10, nullable = false)
    @NotNull
    @Size(max = 10)
    private String lastName;
    
    @Enumerated(EnumType.STRING)
    @NotNull
    private Regions region;

    @Temporal(TemporalType.DATE)
    @NotNull
    private Date startDate;

    public String toString() {
        final String DIVIDER = ", ";
        
        StringBuilder buf = new StringBuilder();
        buf.append(this.getClass().getSimpleName() + ": ");
        buf.append("[");
        buf.append("id=" + id + DIVIDER);
        buf.append("version=" + version + DIVIDER);
        buf.append("firstName=" + firstName + DIVIDER);
        buf.append("lastName=" + lastName + DIVIDER);
        buf.append("region=" + region + DIVIDER);
        buf.append("startDate=" + startDate);
        buf.append("]");
        return buf.toString();
    }

    // Default constructor is required by JPA.
    public Person() {
    }

    public Person(String firstName, String lastName, Regions region, Date startDate) {
        super();
        this.firstName = firstName;
        this.lastName = lastName;
        this.region = region;
        this.startDate = startDate;
    }

    // The need for an equals() method is discussed at http://www.hibernate.org/109.html
    
    @Override
    public boolean equals(Object obj) {
        return (obj == this) || (obj instanceof Person) && id != null && id.equals(((Person) obj).getId());
    }

    // The need for a hashCode() method is discussed at http://www.hibernate.org/109.html

    @Override
    public int hashCode() {
        return id == null ? super.hashCode() : id.hashCode();
    }

    @PrePersist
    @PreUpdate
    public void validate() throws ValidationException {

    }

    public Long getId() {
        return id;
    }

    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Regions getRegion() {
        return region;
    }

    public void setRegion(Regions region) {
        this.region = region;
    }

    public Date getStartDate() {
        return startDate;
    }

    public void setStartDate(Date startDate) {
        this.startDate = startDate;
    }

}


package jumpstart.business.domain.person;

public enum Regions {
    EAST_COAST, WEST_COAST;
}