From: Marko Grönroos Date: Wed, 6 Aug 2008 11:00:44 +0000 (+0000) Subject: Added Form example to Feature Browser. X-Git-Tag: 6.7.0.beta1~4392 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=d87bbb9ef094b1290bdb86ebe82a40923802e607;p=vaadin-framework.git Added Form example to Feature Browser. svn changeset:5148/svn branch:trunk --- 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); + } +}