]> source.dussan.org Git - vaadin-framework.git/commitdiff
Rewrote the FormExample: new implementation should be cleaner, shorter and demonstrat...
authorJoonas Lehtinen <joonas.lehtinen@itmill.com>
Fri, 24 Oct 2008 17:56:46 +0000 (17:56 +0000)
committerJoonas Lehtinen <joonas.lehtinen@itmill.com>
Fri, 24 Oct 2008 17:56:46 +0000 (17:56 +0000)
svn changeset:5724/svn branch:trunk

src/com/itmill/toolkit/demo/featurebrowser/FormExample.java

index 465e11f05d80d5e41a136126ef83b1d4896a696a..ebbd01cccc78041a8301a753c61c553911aeffbe 100644 (file)
 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;
+        }
     }
+
 }