Vaadin Web应用开发教程(23):UI组件-Form组件

jerry VaadinWeb 2015年11月25日 收藏

大部分的Web应用都包含窗体(Form),Vaadin中的Form组件提供了创建窗体的简洁方法。窗体中的填充域可以直接由绑定的数据源自动生成。BeamItem 适配器(Adapter)支持使用Java Bean对象或是普通的Java 对象做为数据源。 Form组件支持窗体数据的缓存从而只需在填写Form完成后一次性提交。
Form 组件从Layout 派生,可以有边框,标题,描述以及错误提示标识。
和大部分UI组件不同的是,Form构造函数不支持传入窗体标题参数,因为From经常不需要显示标题。你可以使用setCaption 为窗体添加标题。setDescription 可以为窗体添加帮助提示。Form组件缺省使用FormLayout 布局,但你可以使用setLayout 为Form组件设置其它Layout对象。
Form组件可以包含其它UI组件,你可以直接创建UI组件如何添加到Form组件的Layout对象中,但更好的方法是使用数据绑定。
下面代码显示两种方法为Form添加其它UI输入组件,如何添加到窗体中:

  1. Form form = new Form();
  2. form.setCaption("Form Caption");
  3. form.setDescription("This is a description of the Form that is " +
  4. "displayed in the upper part of the form. You normally " +
  5. "enter some descriptive text about the form and its " +
  6. "use here.");
  7.  
  8. // Add a field directly to the layout. This field will
  9. // not be bound to the data source Item of the form.
  10. form.getLayout().addComponent(new TextField("A Field"));
  11.  
  12. // Add a field and bind it to an named item property.
  13. form.addField("another", new TextField("Another Field"));

Form组件可以显示输入错误标识,它可以显示下面几种错误类型:

  • 由setComponentError 设置的错误内容。
  • 由addValidator 添加到窗体的Validator 生成的错误。
  • 由窗体中UI域所关联的Validator引起的错误。此时setValidatorVisible(true)
  • 当setRequired(true) 和setRequiredError 同时设置时未填写某个必需内容时引发的校验错误。

    但Form组件同时只能显示单个错误。
    此外Form组件还定义了页脚区域(footer),footer 缺省使用HorizontalLayout布局。但也可以使用setFooter 来修改缺省布局。

  1. // Set the footer layout.
  2. form.setFooter(new VerticalLayout());
  3.  
  4. form.getFooter().addComponent(
  5. new Label("This is the footer area of the Form. "+
  6. "You can use any layout here. "+
  7. "This is nice for buttons."));
  8.  
  9. // Have a button bar in the footer.
  10. HorizontalLayout okbar = new HorizontalLayout();
  11. okbar.setHeight("25px");
  12. form.getFooter().addComponent(okbar);
  13.  
  14. // Add an Ok (commit), Reset (discard), and Cancel buttons
  15. // for the form.
  16. Button okbutton = new Button("OK", form, "commit");
  17. okbar.addComponent(okbutton);
  18. okbar.setComponentAlignment(okbutton, Alignment.TOP_RIGHT);
  19. okbar.addComponent(new Button("Reset", form, "discard"));
  20. okbar.addComponent(new Button("Cancel"));
  21.  

上面介绍了Form组件的基本用法,接着介绍Form组件如何利用数据绑定来自动创建UI填充域。
数据绑定使用的数据源可以为任何实现Item 接口的Java对象。你可以自行实现Item接口或是使用BeamItem Adapter将Form组件绑定到任意的JavaBean对象。 也可以使用PropertysetItem 将Form组件绑定到一个Propert 对象集合。
下面代码定义了个简单的Java Bean对象-PersonBean.

  1. /** A simple JavaBean. */
  2. public class PersonBean {
  3. String name;
  4. String city;
  5.  
  6. public void setName(String name) {
  7. this.name = name;
  8. }
  9.  
  10. public String getName() {
  11. return name;
  12. }
  13.  
  14. public void setCity(String city) {
  15. this.city = city;
  16. }
  17.  
  18. public String getCity() {
  19. return city;
  20. }
  21. }

如何使用BeanItem 适配器将一个PersonBean对象绑定到Form 组件。

  1. // Create a form and use FormLayout as its layout.
  2. final Form form = new Form();
  3.  
  4. // Set form caption and description texts
  5. form.setCaption("Contact Information");
  6. form.setDescription("Please specify name of the person and the city where the person lives in.");
  7.  
  8. // Create the custom bean.
  9. PersonBean bean = new PersonBean();
  10.  
  11. // Create a bean item that is bound to the bean.
  12. BeanItem item = new BeanItem(bean);
  13.  
  14. // Bind the bean item as the data source for the form.
  15. form.setItemDataSource(item);

绑定数据源后,Form组件使用FormLayout,并为Java Bean的每个属性自动创建对应的UI组件(如文本框)。如下图所示:

这种自动创建的UI域的顺序可以不是我们所希望的,这时可以使用setVisibleItemPropertyies 来修改UI域的显示顺序,比如:

  1. // Set the order of the items in the form.
  2. Vector order = new Vector();
  3. order.add("city");
  4. order.add("name");
  5. form.setVisibleItemProperties(order);

这种自动创建的UI组件的标题缺省使用JavaBean对应的属性名称,你可以使用FieldFactory 来修改缺省标题。
自动创建的UI域具有一定的局限性,比如String, int 或double 类型的属性会自动对应到TextField 组件。而此时你可以希望使用Combo组件来输入城市名称。 FormFieldFactory接口用来解决这些局限。
FieldFactory 的缺省实现DefaultFieldFactory 也用于Table组件。 通常可以通过派生DefaultFieldFactory 的方法来定制FieldFactory。
下面为FormFieldFactory接口的一个简单实现,为city 使用一个Select 组件。

  1. class MyFieldFactory implements FormFieldFactory {
  2. public Field createField(Item item, Object propertyId,
  3. Component uiContext) {
  4. // Identify the fields by their Property ID.
  5. String pid = (String) propertyId;
  6. if ("name".equals(pid)) {
  7. return new TextField("Name");
  8. } else if ("city".equals(pid)) {
  9. Select select = new Select("City");
  10. select.addItem("Berlin");
  11. select.addItem("Helsinki");
  12. select.addItem("London");
  13. select.addItem("New York");
  14. select.addItem("Turku");
  15. select.setNewItemsAllowed(true);
  16. return select;
  17. }
  18.  
  19. return null; // Invalid field (property) name.
  20. }
  21. }
  22.  
  23. ...
  24.  
  25. form.setFormFieldFactory(new MyFieldFactory());

此外使用Form组件的一个重要功能是验证用户输入,Vaadin再UI 域(Field)可以关联到validator来验证用户输入以保证输入合法。
Vaadin的validator 为实现了Validator 接口的类。 这个接口定义了两个方法,isValid 和validate.
Vaadin 内置了一些常用的验证类,如IntegerValidator, DoubleValidator, StringLengthValidator, EmailValidator以及RegexValidator 等。
Form校验发生在用户点击“提交”按钮时,当窗体中任何一个Validator失败时,窗体提交都会失败,并显示与之关联的错误标识。如果提交成功,输入数据则写到数据源中。
比如下面代码要求邮编满足给定格式。

  1. // Postal code that must be 5 digits (10000-99999).
  2. TextField field = new TextField("Postal Code");
  3. field.setColumns(5);
  4.  
  5. // Create the validator
  6. Validator postalCodeValidator = new RegexpValidator(
  7. "[1-9][0-9]{4}", "Postal code must be a number 10000-99999.");
  8. field.addValidator(postalCodeValidator);

如果输入错误则窗体显示错误提示:

如果需要指定窗体中某些域是必需填写的,可以使用setRequired(true),并使用setRequiredError设置错误信息在用户没有填写必填输入内容时显示。

  1. form.getField("name").setRequired(true);
  2. form.getField("name").setRequiredError("Name is missing");
  3. form.getField("address").setRequired(true); // No error message

最后提一下Form组件的缓存功能,可以只在用户点击?提交“按钮后才将用户输入数据写回数据源。其实Vaadin的所有Field组件都支持缓存功能。而Form的commit ,discard 实际上是调用对应的Field组件的相关功能。当调用discard 后,将失去之前用户所有修改,UI输入域恢复到数据源的初始内容。

  1. final Form form = new Form();
  2. ...add components...
  3.  
  4. // Enable buffering.
  5. form.setWriteThrough(false);
  6.  
  7. // The Ok button calls form.commit().
  8. Button commit = new Button("Ok", form, "commit");
  9.  
  10. // The Restore button calls form.discard().
  11. Button restore = new Button("Restore", form, "discard");