summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarko Grönroos <magi@iki.fi>2008-08-06 11:00:44 +0000
committerMarko Grönroos <magi@iki.fi>2008-08-06 11:00:44 +0000
commitd87bbb9ef094b1290bdb86ebe82a40923802e607 (patch)
tree63840c204e4846f9997b1e7b4d520428e6572ffc
parenta614d853fae53fc93343d54e4c8c4598ed729a89 (diff)
downloadvaadin-framework-d87bbb9ef094b1290bdb86ebe82a40923802e607.tar.gz
vaadin-framework-d87bbb9ef094b1290bdb86ebe82a40923802e607.zip
Added Form example to Feature Browser.
svn changeset:5148/svn branch:trunk
-rw-r--r--WebContent/ITMILL/themes/example/styles.css12
-rw-r--r--src/com/itmill/toolkit/demo/featurebrowser/FeatureBrowser.java3
-rw-r--r--src/com/itmill/toolkit/demo/featurebrowser/FormExample.java307
3 files changed, 321 insertions, 1 deletions
diff --git a/WebContent/ITMILL/themes/example/styles.css b/WebContent/ITMILL/themes/example/styles.css
index bbd070a9da..d923e575b1 100644
--- a/WebContent/ITMILL/themes/example/styles.css
+++ b/WebContent/ITMILL/themes/example/styles.css
@@ -4,4 +4,14 @@
/* full height layout does not need scrollable body;
avoids excess scrollbars if moving sub-window over the window area */
overflow:hidden;
-} \ No newline at end of file
+}
+
+.i-form {
+ border: 1px solid;
+ margin: 10px;
+ padding: 5px;
+}
+
+.i-orderedlayout-formroot td {
+ vertical-align: top;
+}
diff --git a/src/com/itmill/toolkit/demo/featurebrowser/FeatureBrowser.java b/src/com/itmill/toolkit/demo/featurebrowser/FeatureBrowser.java
index c27d48acbf..daa7bb1cb1 100644
--- a/src/com/itmill/toolkit/demo/featurebrowser/FeatureBrowser.java
+++ b/src/com/itmill/toolkit/demo/featurebrowser/FeatureBrowser.java
@@ -87,6 +87,9 @@ public class FeatureBrowser extends com.itmill.toolkit.Application implements
// Wrangling data: Tree
{ "Wrangling data", "Tree", "A hierarchy of things",
TreeExample.class },
+ // Misc: Forms
+ { "Misc", "Forms", "Every application needs forms",
+ FormExample.class },
// Misc: Notifications
{ "Misc", "Notifications", "Notifications can improve usability",
NotificationExample.class },
diff --git a/src/com/itmill/toolkit/demo/featurebrowser/FormExample.java b/src/com/itmill/toolkit/demo/featurebrowser/FormExample.java
new file mode 100644
index 0000000000..465e11f05d
--- /dev/null
+++ b/src/com/itmill/toolkit/demo/featurebrowser/FormExample.java
@@ -0,0 +1,307 @@
+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.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.
+ */
+public class FormExample extends CustomComponent {
+ /** Contact information data model. */
+ public class Contact {
+ String name = "";
+ String address = "";
+ int postalCode = 0;
+ String city;
+ }
+
+ /** Bean wrapper for the data model. */
+ public class ContactBean extends Contact {
+ public ContactBean() {
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setPostalCode(String postalCode) {
+ try {
+ if (postalCode != null)
+ this.postalCode = Integer.parseInt(postalCode);
+ else
+ this.postalCode = 0;
+ } catch (NumberFormatException e) {
+ this.postalCode = 0;
+ }
+ }
+
+ public String getPostalCode() {
+ if (postalCode > 0)
+ return String.valueOf(postalCode);
+ else
+ return "";
+ }
+
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+ public String getCity() {
+ return city;
+ }
+ }
+
+ /**
+ * Factory to create the proper type of field for each property type. We
+ * need to implement just one of the factory methods.
+ */
+ class MyFieldFactory implements FieldFactory {
+
+ public Field createField(Class type, Component uiContext) {
+ return null;
+ }
+
+ public Field createField(Property property, Component uiContext) {
+ return null;
+ }
+
+ 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 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 Field createField(Container container, Object itemId,
+ Object propertyId, Component uiContext) {
+ return null;
+ }
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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 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);
+ }
+}