]> source.dussan.org Git - vaadin-framework.git/commitdiff
Added Form example to Feature Browser.
authorMarko Grönroos <magi@iki.fi>
Wed, 6 Aug 2008 11:00:44 +0000 (11:00 +0000)
committerMarko Grönroos <magi@iki.fi>
Wed, 6 Aug 2008 11:00:44 +0000 (11:00 +0000)
svn changeset:5148/svn branch:trunk

WebContent/ITMILL/themes/example/styles.css
src/com/itmill/toolkit/demo/featurebrowser/FeatureBrowser.java
src/com/itmill/toolkit/demo/featurebrowser/FormExample.java [new file with mode: 0644]

index bbd070a9da874070dbce17ded5c3e6d0ccd9fb0b..d923e575b1a5fd45db8aaa57247cd1ff1056673d 100644 (file)
@@ -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;
+}
index c27d48acbf3c078f678f1c311e2be4d0285ca8d3..daa7bb1cb1c2e4c3ad4b7f955949f9e6b6146efb 100644 (file)
@@ -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 (file)
index 0000000..465e11f
--- /dev/null
@@ -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);
+    }
+}