aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoonas Lehtinen <joonas.lehtinen@itmill.com>2008-10-24 17:56:46 +0000
committerJoonas Lehtinen <joonas.lehtinen@itmill.com>2008-10-24 17:56:46 +0000
commit81e4838ebe5fa11d4ab802db0e42a4283ad41c77 (patch)
tree19a74d9d57b5c11610375687fe54f376fd62ac29
parent2392d4e182f91896c326d4ffb3161efaaedf71af (diff)
downloadvaadin-framework-81e4838ebe5fa11d4ab802db0e42a4283ad41c77.tar.gz
vaadin-framework-81e4838ebe5fa11d4ab802db0e42a4283ad41c77.zip
Rewrote the FormExample: new implementation should be cleaner, shorter and demonstrate more features.
svn changeset:5724/svn branch:trunk
-rw-r--r--src/com/itmill/toolkit/demo/featurebrowser/FormExample.java400
1 files changed, 148 insertions, 252 deletions
diff --git a/src/com/itmill/toolkit/demo/featurebrowser/FormExample.java b/src/com/itmill/toolkit/demo/featurebrowser/FormExample.java
index 465e11f05d..ebbd01cccc 100644
--- a/src/com/itmill/toolkit/demo/featurebrowser/FormExample.java
+++ b/src/com/itmill/toolkit/demo/featurebrowser/FormExample.java
@@ -1,307 +1,203 @@
package com.itmill.toolkit.demo.featurebrowser;
-import java.util.Vector;
-
-import org.apache.commons.digester.SetRootRule;
-
import com.itmill.toolkit.data.Container;
-import com.itmill.toolkit.data.Item;
-import com.itmill.toolkit.data.Property;
import com.itmill.toolkit.data.Validator;
-import com.itmill.toolkit.data.Item.PropertySetChangeEvent;
-import com.itmill.toolkit.data.Item.PropertySetChangeListener;
import com.itmill.toolkit.data.util.BeanItem;
-import com.itmill.toolkit.data.util.ObjectProperty;
+import com.itmill.toolkit.ui.BaseFieldFactory;
import com.itmill.toolkit.ui.Button;
import com.itmill.toolkit.ui.Component;
import com.itmill.toolkit.ui.CustomComponent;
-import com.itmill.toolkit.ui.ExpandLayout;
import com.itmill.toolkit.ui.Field;
-import com.itmill.toolkit.ui.FieldFactory;
import com.itmill.toolkit.ui.Form;
-import com.itmill.toolkit.ui.FormLayout;
-import com.itmill.toolkit.ui.Label;
import com.itmill.toolkit.ui.OrderedLayout;
-import com.itmill.toolkit.ui.Panel;
-import com.itmill.toolkit.ui.Select;
-import com.itmill.toolkit.ui.Table;
import com.itmill.toolkit.ui.TextField;
import com.itmill.toolkit.ui.Button.ClickEvent;
/**
* This example demonstrates the most important features of the Form component:
* binding Form to a JavaBean so that form fields are automatically generated
- * from the bean properties, creation of fields with proper types for each bean
- * properly using a FieldFactory, buffering (commit/discard), and validation.
- *
- * The Form is used with a FormLayout, which automatically lays the components
- * out in a format typical for forms.
+ * from the bean properties, creation of custom field editors using a
+ * FieldFactory, customizing the form without FieldFactory, buffering
+ * (commit/discard) and validation. Please note that the example is quite a bit
+ * more complex than real use, as it tries to demonstrate more features than
+ * needed in general case.
*/
public class FormExample extends CustomComponent {
- /** Contact information data model. */
- public class Contact {
- String name = "";
- String address = "";
- int postalCode = 0;
- String city;
+
+ static final String cities[] = { "Amsterdam", "Berlin", "Helsinki",
+ "Hong Kong", "London", "Luxemburg", "New York", "Oslo", "Paris",
+ "Rome", "Stockholm", "Tokyo", "Turku" };
+
+ /** Compose the demo. */
+ public FormExample() {
+
+ // Example data model
+ final Address dataModel = new Address();
+ Button peekDataModelState = new Button("Show the data model state",
+ new Button.ClickListener() {
+
+ public void buttonClick(ClickEvent event) {
+ getWindow().showNotification(
+ dataModel.getAddressAsText());
+ }
+ });
+
+ // Example form
+ final AddressForm form = new AddressForm("Contact Information");
+ form.setDataSource(dataModel);
+ form
+ .setDescription("Please enter valid name and address. Fields marked with * are required. "
+ + "If you try to commit with invalid values, a form error message is displayed. "
+ + "(Address is required but failing to give it a value does not display an error.)");
+
+ // Layout the example
+ OrderedLayout root = new OrderedLayout();
+ root.addComponent(form);
+ root.addComponent(peekDataModelState);
+ setCompositionRoot(root);
}
- /** Bean wrapper for the data model. */
- public class ContactBean extends Contact {
- public ContactBean() {
- }
+ public static class AddressForm extends Form {
+ public AddressForm(String caption) {
- public void setName(String name) {
- this.name = name;
- }
+ setCaption(caption);
- public String getName() {
- return name;
- }
+ // Use custom field factory to modify the defaults on how the
+ // components are created
+ setFieldFactory(new MyFieldFactory());
- public void setAddress(String address) {
- this.address = address;
+ // Add Commit and Discard controls to the form.
+ Button commit = new Button("Save", this, "commit");
+ Button discard = new Button("Reset", this, "discard");
+ OrderedLayout footer = new OrderedLayout(
+ OrderedLayout.ORIENTATION_HORIZONTAL);
+ footer.addComponent(commit);
+ footer.addComponent(discard);
+ setFooter(footer);
}
- public String getAddress() {
- return address;
+ public void setDataSource(Address dataModel) {
+
+ // Set the form to edit given datamodel by converting pojo used as
+ // the datamodel to Item
+ setItemDataSource(new BeanItem(dataModel));
+
+ // Ensure that the fields are shown in correct order as the
+ // datamodel does not force any specific order.
+ setVisibleItemProperties(new String[] { "name", "address",
+ "postalCode", "city" });
+
+ // For examples sake, customize some of the form fields directly
+ // here. The alternative way is to use custom field factory as shown
+ // above.
+ getField("name").setRequired(true);
+ getField("name").setRequiredError("Name is missing");
+ getField("address").setRequired(true); // No error message
+ replaceWithSelect("city", cities, cities).setNewItemsAllowed(true);
+ getField("address").setCaption("Street Address");
+
+ // Set the form to act immediately on user input. This is
+ // automatically transports data between the client and the server
+ // to do server-side validation.
+ setImmediate(true);
+
+ // Enable buffering so that commit() must be called for the form
+ // before input is written to the data. (Form input is not written
+ // immediately through to the underlying object.)
+ setWriteThrough(false);
}
+ }
- public void setPostalCode(String postalCode) {
- try {
- if (postalCode != null)
- this.postalCode = Integer.parseInt(postalCode);
- else
- this.postalCode = 0;
- } catch (NumberFormatException e) {
- this.postalCode = 0;
+ /**
+ * This is example on how to customize field creation. Any kind of field
+ * components could be created on the fly.
+ */
+ static class MyFieldFactory extends BaseFieldFactory {
+
+ public Field createField(Container container, Object itemId,
+ Object propertyId, Component uiContext) {
+
+ if ("postalCode".equals(propertyId)) {
+ TextField postalCode = new TextField("Postal Code");
+ postalCode.setColumns(5);
+ postalCode.addValidator(new PostalCodeValidator());
+ return postalCode;
}
- }
- public String getPostalCode() {
- if (postalCode > 0)
- return String.valueOf(postalCode);
- else
- return "";
+ return super.createField(container, itemId, propertyId, uiContext);
}
- public void setCity(String city) {
- this.city = city;
+ }
+
+ /**
+ * This is an example of how to create a custom validator for automatic
+ * input validation.
+ */
+ static class PostalCodeValidator implements Validator {
+
+ public boolean isValid(Object value) {
+ if (value == null || !(value instanceof String)) {
+ return false;
+ }
+
+ return ((String) value).matches("[0-9]{5}");
}
- public String getCity() {
- return city;
+ public void validate(Object value) throws InvalidValueException {
+ if (!isValid(value)) {
+ throw new InvalidValueException(
+ "Postal code must be a five digit number.");
+ }
}
}
/**
- * Factory to create the proper type of field for each property type. We
- * need to implement just one of the factory methods.
+ * Contact information data model created as POJO. Note that in many cases
+ * it would be a good idea to implement Item -interface for the datamodel to
+ * make it directly bindable to form (without BeanItem wrapper)
*/
- class MyFieldFactory implements FieldFactory {
+ public static class Address {
+ String name = "";
+ String address = "";
+ String postalCode = "";
+ String city;
- public Field createField(Class type, Component uiContext) {
- return null;
+ public String getAddressAsText() {
+ return name + "\n" + address + "\n" + postalCode + " "
+ + (city == null ? "" : city);
}
- public Field createField(Property property, Component uiContext) {
- return null;
+ public void setName(String name) {
+ this.name = name;
}
- public Field createField(Item item, Object propertyId,
- Component uiContext) {
- String pid = (String) propertyId;
-
- if (pid.equals("name"))
- return new TextField("Name");
-
- if (pid.equals("address"))
- return new TextField("Street Address");
-
- if (pid.equals("postalCode")) {
- TextField field = new TextField("Postal Code");
- field.setColumns(5);
- Validator postalCodeValidator = new Validator() {
-
- public boolean isValid(Object value) {
- if (value == null || !(value instanceof String)) {
- return false;
- }
-
- return ((String) value).matches("[0-9]{5}");
- }
+ public String getName() {
+ return name;
+ }
- public void validate(Object value) throws InvalidValueException {
- if (!isValid(value)) {
- throw new InvalidValueException(
- "Postal code must be a number 10000-99999.");
- }
- }
- };
- field.addValidator(postalCodeValidator);
- return field;
- }
-
- if (pid.equals("city")) {
- Select select = new Select("City");
- final String cities[] = new String[] { "Amsterdam", "Berlin",
- "Helsinki", "Hong Kong", "London", "Luxemburg",
- "New York", "Oslo", "Paris", "Rome", "Stockholm",
- "Tokyo", "Turku" };
- for (int i = 0; i < cities.length; i++)
- select.addItem(cities[i]);
- return select;
- }
- return null;
+ public void setAddress(String address) {
+ this.address = address;
}
- public Field createField(Container container, Object itemId,
- Object propertyId, Component uiContext) {
- return null;
+ public String getAddress() {
+ return address;
}
- }
- /**
- * Displays the contents of the bean in a table.
- *
- * This is not as clean as it should be. Currently, components can not be
- * bound to a BeanItem so that the when the data in the BeanItem changes,
- * the displayed value would be automatically refreshed. We therefore do the
- * refreshing manually.
- **/
- public class ContactDisplay extends CustomComponent {
- ContactBean contact;
- Label name;
- Label address;
- Label postalCode;
- Label city;
-
- public ContactDisplay (ContactBean contact) {
- this.contact = contact;
-
- // Use a Form merely as a layout component. The CSS will add
- // a border to the form.
- Form layout = new Form();
- setCompositionRoot(layout);
- layout.setCaption("Data Model State");
- layout.setDescription("Below is the state of the actual stored data. "+
- "It is updated only when the form is committed successfully. "+
- "Discarding the form input reverts the form to this state.");
- layout.setWidth("400px");
-
- // Manually create read-only components for each of the fields.
- name = new Label(contact.getName());
- name.setCaption("Name:");
- address = new Label(contact.getAddress());
- address.setCaption("Address:");
- postalCode = new Label(contact.getPostalCode());
- postalCode.setCaption("Postal Code:");
- city = new Label(contact.getCity());
- city.setCaption("City:");
-
- layout.getLayout().addComponent(name);
- layout.getLayout().addComponent(address);
- layout.getLayout().addComponent(postalCode);
- layout.getLayout().addComponent(city);
+ public void setPostalCode(String postalCode) {
+ this.postalCode = postalCode;
}
- /**
- * The Label components are not bound to the bean properties, so we have
- * to refresh the components manually.
- */
- public void refresh() {
- name.setValue(contact.getName());
- address.setValue(contact.getAddress());
- postalCode.setValue(contact.getPostalCode());
- city.setValue(contact.getCity());
+ public String getPostalCode() {
+ return postalCode;
}
- }
- public FormExample() {
- // The root layout of the custom component.
- OrderedLayout root = new OrderedLayout(OrderedLayout.ORIENTATION_HORIZONTAL);
- root.addStyleName("formroot");
- setCompositionRoot(root);
-
- // Create a form. It will use FormLayout as its layout by default.
- final Form form = new Form();
- root.addComponent(form);
- form.setWidth("400px");
-
- // The caption appears within the border of the form box. The form box
- // is enabled in the CSS styles with "border: 1px solid".
- form.setCaption("Contact Information");
-
- // Set description that will appear on top of the form.
- form.setDescription("Please enter valid name and address. Fields marked with * are required. "+
- "If you try to commit with invalid values, a form error message is displayed. " +
- "(Address is required but failing to give it a value does not display an error.)");
-
- // Use custom field factory to create the fields in the form.
- form.setFieldFactory(new MyFieldFactory());
-
- // Create the custom bean.
- ContactBean bean = new ContactBean();
-
- // Create a bean item that is bound to the bean.
- BeanItem item = new BeanItem(bean);
-
- // Bind the bean item as the data source for the form.
- form.setItemDataSource(item);
-
- // Set the order of the items in the form.
- Vector order = new Vector();
- order.add("name");
- order.add("address");
- order.add("postalCode");
- order.add("city");
- form.setVisibleItemProperties(order);
-
- // Set required fields. The required error is displayed in
- // the error indication are of the Form if a required
- // field is empty. If it is not set, no error is displayed
- // about an empty required field.
- form.getField("name").setRequired(true);
- form.getField("name").setRequiredError("Name is missing");
- form.getField("address").setRequired(true); // No error message
-
- // Set the form to act immediately on user input. This is
- // necessary for the validation of the fields to occur immediately when
- // the input focus changes and not just on commit.
- form.setImmediate(true);
-
- // Set buffering so that commit() must be called for the form
- // before input is written to the data. (Form input is not written
- // immediately through to the underlying object.)
- form.setWriteThrough(false);
-
- // If the state of the bound data source changes, the changes are shown
- // immediately in the form, so there is no buffering. (This is the default.)
- form.setReadThrough(true);
-
- // Have a read-only component to display the actual current state
- // of the bean (POJO).
- final ContactDisplay display = new ContactDisplay(bean);
- root.addComponent(display);
-
- // Add Commit and Discard controls to the form.
- ExpandLayout footer = new ExpandLayout(OrderedLayout.ORIENTATION_HORIZONTAL);
-
- // The Commit button calls form.commit().
- Button commit = new Button("Commit", new Button.ClickListener() {
- public void buttonClick(ClickEvent event) {
- form.commit();
- display.refresh();
- }
- });
-
- // The Discard button calls form.discard().
- Button discard = new Button("Discard", form, "discard");
- footer.addComponent(commit);
- footer.setComponentAlignment(commit, ExpandLayout.ALIGNMENT_RIGHT,
- ExpandLayout.ALIGNMENT_TOP);
- footer.setHeight("25px"); // Has to be set explicitly for ExpandLayout.
- footer.addComponent(discard);
- form.setFooter(footer);
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+ public String getCity() {
+ return city;
+ }
}
+
}