Vaadin Best Practices
Best practices are procedures that are accepted or prescribed as being correct or most effective.
Table Of Contents
- 1. The tools
- 2. Introduction
- 3. Prerequisites
- 4. Set up the project
- 5. Coding the example
- 6. The complete source code
- 7. Running the example
- 8. Results
- 9. Download the Source Code
1. The tools
- Java JDK 8
- Latest Eclipse Mars
- Vaadin 7.6.8
- Tomcat Server 8
2. Introduction
In this example we are going to illustrate best practices used to make Vaadin applications. We are going to make a Vaadin example to illustrate these practices.
3. Prerequisites
- JDK installed
- Eclipse Mars installed and working
- Vaadin plug-in installed
- Tomcat 8 installed and running
4. Set up the project
In the file menu choose File -> New -> Other
Now from the list choose Vaadin 7 project
Click next and name your project then click finish.
5. The example
5.1 Make a design
The design is the blueprint of our program. It is better to invest some time making a good design and when the design is ready, start coding the application.
In our case we have an application that has a menu and three views, each button on the menu changes the view. We have a welcome view that displays a welcome label.
An input view to input some fields, and a view to show all the data.
5.2 Annotations
It is recommended to use annotations to define our servlet, because Vaadin uses annotations by default for convenience.
Convenience over configuration is a design pattern used to avoid huge configuration files and promotes flexibility.
Annotations
@WebServlet(value = "/*", asyncSupported = true) @VaadinServletConfiguration(productionMode = false, ui = VaadinbestpracticesUI.class, widgetset = "com.example.vaadinbestpractices.widgetset.VaadinbestpracticesWidgetset")
5.3 Navigator
Use a navigator to change the views in the application. The navigator was created for this task. We use the navigator in our init application method.
5.3.1 Layout & content
In our init method, first we create the layout and the content panel to use with the navigator.
Layout & content
final VerticalLayout layout = new VerticalLayout(); layout.setMargin(true); setContent(layout); Panel contentPanel = new Panel("Main Panel"); contentPanel.setSizeUndefined();
final VerticalLayout layout = new VerticalLayout();
Creates the layout.
layout.setMargin(true);
Sets the margin of the layout.
setContent(layout);
Sets the layout as the main layout.
Panel contentPanel = new Panel("Main Panel");
Creates a panel yo use with the navigator.
contentPanel.setSizeUndefined();
Sets the size of the panel.
5.3.2 Navigator views
We create the navigator and attach the views used in our application. In this case we have 3 views: welcome, input and data.
Navigators views
new Navigator(this, contentPanel); getNavigator().addView(InputPage.NAME, InputPage.class); getNavigator().addView(WelcomePage.NAME, WelcomePage.class); getNavigator().addView(DataPage.NAME, DataPage.class);
new Navigator(this, contentPanel);
Creates the navigator using the panel as a placeholder.
getNavigator().addView(InputPage.NAME, InputPage.class);
Adds the input view to the navigator.
getNavigator().addView(WelcomePage.NAME, WelcomePage.class);
Adds the welcome view to the navigator.
getNavigator().addView(DataPage.NAME, DataPage.class);
Adds the data view to the navigator.
5.3.3 Menu listeners
We are going to navigate our application using a menu. Each time we click on a menu button the navigator changes the view. For each menu button we have a listener to change the view.
Menubar listeners
MenuBar.Command welcome = new Command() { @Override public void menuSelected(MenuItem selectedItem) { getNavigator().navigateTo(WelcomePage.NAME); } }; MenuBar.Command input = new Command() { @Override public void menuSelected(MenuItem selectedItem) { getNavigator().navigateTo(InputPage.NAME); } }; MenuBar.Command data = new Command() { @Override public void menuSelected(MenuItem selectedItem) { getNavigator().navigateTo(DataPage.NAME); } };
MenuBar.Command welcome = new Command()
Creates a new menu command welcome.
getNavigator().navigateTo(WelcomePage.NAME);
Navigates to the welcome page.
MenuBar.Command input = new Command()
Creates a new menu command input.
getNavigator().navigateTo(InputPage.NAME);
Navigates to the input view.
MenuBar.Command data = new Command()
Creates a new menu command data.
getNavigator().navigateTo(DataPage.NAME);
Navigates to the data view.
5.3.4 Menu
We create the menu and attach the buttons to it. When a button is attached to the menu we use the menu command listener created before.
Main menu
MenuBar mainMenu = new MenuBar(); mainMenu.addItem("Welcome", FontAwesome.ARROW_CIRCLE_LEFT, welcome); mainMenu.addItem("Input", FontAwesome.WEIXIN, input); mainMenu.addItem("Data", FontAwesome.LIST, data);
MenuBar mainMenu = new MenuBar();
Creates a new menu bar.
mainMenu.addItem("Welcome", FontAwesome.ARROW_CIRCLE_LEFT, welcome);
Add the welcome button to the menu.
mainMenu.addItem("Input", FontAwesome.WEIXIN, input);
Add the input button to the menu.
mainMenu.addItem("Data", FontAwesome.LIST, data);
Add the data button to the menu.
5.3.5 Navigator initial page
We redirect the navigator to the page we want to show when the application is started.
Navigator initial page
layout.addComponent(mainMenu); layout.addComponent(contentPanel); getNavigator().navigateTo(WelcomePage.NAME);
layout.addComponent(mainMenu);
Adds the menu to the layout.
layout.addComponent(contentPanel);
Adds the content panel to the layout.
getNavigator().navigateTo(WelcomePage.NAME);
Navigates to the welcome page when the application is loaded.
5.3.6 Welcome page
The welcome page is used as the initial page for the navigator.
Welcome page
public class WelcomePage extends VerticalLayout implements View { private static final long serialVersionUID = 1L; public static final String NAME = "welcomepage"; public WelcomePage() { setMargin(true); setSpacing(true); Label welcome = new Label("Welcome"); welcome.addStyleName("h1"); addComponent(welcome); } }
public class WelcomePage extends VerticalLayout implements View
The welcome page used by the navigator must implements the view interface.
public static final String NAME = "welcomepage";
The id of the welcome page to use with the navigator.
setMargin(true);
Sets the margin of the layout.
setSpacing(true);
Sets the spacing of the layout.
Label welcome = new Label("Welcome");
Creates a label.
welcome.addStyleName("h1");
Adds a predefined style to the label.
addComponent(welcome);
Adds the label to the layout.
5.4 Validate user input
The data entered by a user is prone to errors and mistakes and is sane to have a validation process in the input of the data.
We have a view with three input fields to show the validation process.
To validate our input fields we use the Vaadin validator.
5.4.1 Input form
Input form
FormLayout inputForm = new FormLayout(); inputForm.setMargin(true); inputForm.setSpacing(true); inputPanel.setContent(inputForm);
FormLayout inputForm = new FormLayout();
Creates the input form.
inputForm.setMargin(true);
Sets the margin of the input form.
inputForm.setSpacing(true);
Sets the spacing of the input form.
inputPanel.setContent(inputForm);
Sets the input form as the content of the input panel.
5.4.2 Name field validator
Name Field
TextField name = new TextField("Name"); name.setNullSettingAllowed(true); name.setNullRepresentation(""); name.addValidator(new StringLengthValidator("Name must have 3-15 characters lenght", 3, 15, true)); name.setValidationVisible(true); inputForm.addComponent(name);
TextField name = new TextField("Name");
Creates a name text field.
name.setNullSettingAllowed(true);
Allows null in the text field.
name.setNullRepresentation("");
Sets the null representation to a empty string.
name.addValidator(new StringLengthValidator("Name must have 3-15 characters lenght", 3, 15, true));
Adds the validator to the text field.
The validator checks that the string entered into the text field has a length greater than 3 and less than 15.
name.setValidationVisible(true);
Sets the validator visible.
inputForm.addComponent(name);
Add the text field to the form.
5.4.3 Surname field validator
Surname Field
TextField surname = new TextField("Surname"); surname.setNullSettingAllowed(true); surname.setNullRepresentation(""); surname.addValidator(new StringLengthValidator("Surname must have 3-15 characters lenght", 3, 15, true)); surname.setValidationVisible(true); inputForm.addComponent(surname);
TextField surname = new TextField("Surname");
Creates a text field to the surname.
surname.setNullSettingAllowed(true);
Alows null in the text field.
surname.setNullRepresentation("");
Sets the null representation to a empty string.
surname.addValidator(new StringLengthValidator("Surname must have 3-15 characters lenght", 3, 15, true));
Adds the validator to the text field.
The validator checks that the string entered into the text field has a length greater than 3 and less than 15.
surname.setValidationVisible(true);
Sets the validator visible.
inputForm.addComponent(surname);
Add the text field to the form.
5.4.4 Age field validator
Age field
TextField age = new TextField("Age"); age.setNullRepresentation("0"); age.addValidator(new IntegerRangeValidator("Age must be between 1 and 110", 1, 110)); inputForm.addComponent(age);
TextField age = new TextField("Age");
Creates a text field for the age.
age.setNullRepresentation("0");
Sets the null representation to the “0” string.
age.addValidator(new IntegerRangeValidator("Age must be between 1 and 110", 1, 110));
Adds the validator to the field.
The value of the input must be an integer between 1 and 110.
inputForm.addComponent(age);
Adds the text fiel to the form.
5.4.5 Age field validator
Validation buttons
HorizontalLayout btLayout = new HorizontalLayout(); Button btSave = new Button("Save"); btLayout.addComponent(btSave); Button btClear = new Button("Clear"); btLayout.addComponent(btClear); inputForm.addComponent(btLayout);
HorizontalLayout btLayout = new HorizontalLayout();
Creates a horizontal layout for the buttons.
Button btSave = new Button("Save");
Creates a button for save the form data.
btLayout.addComponent(btSave);
Adds the button to the layout.
Button btClear = new Button("Clear");
Creates a new button to clear the fields.
btLayout.addComponent(btClear);
Adds the clear button to the layout.
inputForm.addComponent(btLayout);
Adds the button layout to the form.
5.4.6 Validation process
Checks if the fields are empty
btSave.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { if(!name.isEmpty() && !surname.isEmpty() && !age.isEmpty()){ }else{ Notification.show("All fields must be filled"); }
Checks that all fields have a value otherwise it shows a notification.
Try to validate
Boolean save = true; try{ name.validate(); }catch(InvalidValueException e){ save = false; } try{ surname.validate(); }catch(InvalidValueException e){ save = false; } try{ age.validate(); }catch(InvalidValueException e){ save = false; }
Tries to validate the fields. A boolean is used to keep the status of the validation process. If any validation fails we set save to false.
Save click listener
if(save){ VaadinbestpracticesUI.dataBean.addBean( new DataBean(name.getValue(), surname.getValue(), Integer.valueOf(age.getValue()))); Notification.show("Data saved!"); name.setValue(""); surname.setValue(""); age.setValue("0"); btSave.setComponentError(null); }
if(save)
We check the boolean to save.
VaadinbestpracticesUI.dataBean.addBean(new DataBean(name.getValue(), surname.getValue(), Integer.valueOf(age.getValue())));
We create a bean with the new data.
Notification.show("Data saved!");
Notifies that the data is saved.
name.setValue("");
Clears the name field.
surname.setValue("");
Clears the surname field.
age.setValue("0");
Clears the age field with the null value.
5.4.7 Clear fields
java
btClear.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { name.clear(); surname.clear(); age.clear(); } });
name.clear();
Clears the name.
surname.clear();
Clears the surname.
age.clear();
Clears the age.
5.5 Use containers in fields
The container allow us to bind our input fields to a data type and help in the validation process.
5.5.1 Property sets
java
PropertysetItem fProperties = new PropertysetItem(); fProperties.addItemProperty("nameValidator", new ObjectProperty("")); fProperties.addItemProperty("surnameValidator", new ObjectProperty("")); fProperties.addItemProperty("integerValidator", new ObjectProperty(0));
PropertysetItem fProperties = new PropertysetItem();
Creates a property set.
fProperties.addItemProperty("nameValidator", new ObjectProperty(""));
Adds the name property.
fProperties.addItemProperty("surnameValidator", new ObjectProperty(""));
Adds the surname property.
fProperties.addItemProperty("integerValidator", new ObjectProperty(0)
Adds the age property.
5.5.2 Field groups
java
FieldGroup fGroup = new FieldGroup(fProperties); fGroup.bind(name, "nameValidator"); fGroup.bind(surname, "surnameValidator"); fGroup.bind(age, "integerValidator");
FieldGroup fGroup = new FieldGroup(fProperties);
Creates a field group.
fGroup.bind(name, "nameValidator");
Binds the name text field to the name property.
fGroup.bind(surname, "surnameValidator");
Binds the surname text field to the surname property.
fGroup.bind(age, "integerValidator");
Binds the age text field to the age property.
5.6 Separate the UI from the data
Separation of the data from the UI allow us to change the UI or the data store without affecting each other.
In the image the UI, the datasets and the database are in different layers.
If you change any of these three pieces you only have to define the same interfaces to communicate each other.
The change of one layer doesn’t have to affect any other layer. If you want to change the database from MySQL to PostgreSQL for example, this change is transparent to the UI code.
Data
public class DataBean implements Serializable { private static final long serialVersionUID = 1L; private String name; private String surname; private Integer age; public DataBean(String pName, String pSurname, Integer pAge) { this.name = pName; this.surname = pSurname; this.age = pAge; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
This is a standard java class that extends serializable.
This class has three fields to store the name, the surname and the age with its getters and setters.
Data View
public static final String NAME = "datapage"; public DataPage() { Table dataTable = new Table("Data Table", VaadinbestpracticesUI.dataBean); dataTable.setVisibleColumns(new Object[]{"name", "surname", "age"}); dataTable.setHeight("200px"); addComponent(dataTable); }
public static final String NAME = "datapage";
Creates the id of the data view.
Table dataTable = new Table("Data Table", VaadinbestpracticesUI.dataBean);
Creates a table to show all the records we have loaded.
The table is using the container as a data source.
dataTable.setVisibleColumns(new Object[]{"name", "surname", "age"});
Sets the visible columns.
dataTable.setHeight("200px");
Sets the height of the table.
addComponent(dataTable);
Adds the table to the layout.
5.7 Deploy on https
If our application is going to be on a public domain is better to deploy it using http secure protocol.
Https encrypts our connection protecting us for some kind of attacks that could compromise our data.
6. The complete source code
6.1 VaadinbestpracticesUI.java
VaadinbestpracticesUI.java
package com.example.vaadinbestpractices; import javax.servlet.annotation.WebServlet; import com.example.vaadinbestpractices.data.DataBean; import com.vaadin.annotations.Theme; import com.vaadin.annotations.VaadinServletConfiguration; import com.vaadin.data.util.BeanContainer; import com.vaadin.navigator.Navigator; import com.vaadin.server.FontAwesome; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinServlet; import com.vaadin.ui.MenuBar; import com.vaadin.ui.MenuBar.Command; import com.vaadin.ui.MenuBar.MenuItem; import com.vaadin.ui.Panel; import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; @SuppressWarnings("serial") @Theme("vaadinbestpractices") public class VaadinbestpracticesUI extends UI { public static BeanContainer dataBean; @WebServlet(value = "/*", asyncSupported = true) @VaadinServletConfiguration(productionMode = false, ui = VaadinbestpracticesUI.class, widgetset = "com.example.vaadinbestpractices.widgetset.VaadinbestpracticesWidgetset") public static class Servlet extends VaadinServlet { } @Override protected void init(VaadinRequest request) { final VerticalLayout layout = new VerticalLayout(); layout.setMargin(true); setContent(layout); Panel contentPanel = new Panel("Main Panel"); contentPanel.setSizeUndefined(); dataBean = new BeanContainer(DataBean.class); dataBean.setBeanIdProperty("name"); new Navigator(this, contentPanel); getNavigator().addView(InputPage.NAME, InputPage.class); getNavigator().addView(WelcomePage.NAME, WelcomePage.class); getNavigator().addView(DataPage.NAME, DataPage.class); MenuBar.Command welcome = new Command() { @Override public void menuSelected(MenuItem selectedItem) { getNavigator().navigateTo(WelcomePage.NAME); } }; MenuBar.Command input = new Command() { @Override public void menuSelected(MenuItem selectedItem) { getNavigator().navigateTo(InputPage.NAME); } }; MenuBar.Command data = new Command() { @Override public void menuSelected(MenuItem selectedItem) { getNavigator().navigateTo(DataPage.NAME); } }; MenuBar mainMenu = new MenuBar(); mainMenu.addItem("Welcome", FontAwesome.ARROW_CIRCLE_LEFT, welcome); mainMenu.addItem("Input", FontAwesome.WEIXIN, input); mainMenu.addItem("Data", FontAwesome.LIST, data); layout.addComponent(mainMenu); layout.addComponent(contentPanel); getNavigator().navigateTo(WelcomePage.NAME); } }
6.2 WelcomePage.java
WelcomePage.java
package com.example.vaadinbestpractices; import com.vaadin.navigator.View; import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; import com.vaadin.ui.Label; import com.vaadin.ui.VerticalLayout; public class WelcomePage extends VerticalLayout implements View { private static final long serialVersionUID = 1L; public static final String NAME = "welcomepage"; public WelcomePage() { setMargin(true); setSpacing(true); Label welcome = new Label("Welcome"); welcome.addStyleName("h1"); addComponent(welcome); } @Override public void enter(ViewChangeEvent event) { } }
6.3 InputPage.java
InputPage.java
package com.example.vaadinbestpractices; import com.example.vaadinbestpractices.data.DataBean; import com.google.appengine.api.memcache.InvalidValueException; import com.vaadin.data.fieldgroup.FieldGroup; import com.vaadin.data.util.ObjectProperty; import com.vaadin.data.util.PropertysetItem; import com.vaadin.data.validator.IntegerRangeValidator; import com.vaadin.data.validator.StringLengthValidator; import com.vaadin.navigator.View; import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.FormLayout; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Notification; import com.vaadin.ui.Panel; import com.vaadin.ui.TextField; import com.vaadin.ui.VerticalLayout; @SuppressWarnings("serial") public class InputPage extends VerticalLayout implements View { public static final String NAME = "inputpage"; public InputPage() { Panel inputPanel = new Panel("Input data"); inputPanel.setSizeUndefined(); addComponent(inputPanel); PropertysetItem fProperties = new PropertysetItem(); fProperties.addItemProperty("nameValidator", new ObjectProperty("")); fProperties.addItemProperty("surnameValidator", new ObjectProperty("")); fProperties.addItemProperty("integerValidator", new ObjectProperty(0)); FormLayout inputForm = new FormLayout(); TextField name = new TextField("Name"); name.setNullSettingAllowed(true); name.setNullRepresentation(""); name.addValidator(new StringLengthValidator("Name must have 3-15 characters lenght", 3, 15, true)); name.setValidationVisible(true); inputForm.addComponent(name); TextField surname = new TextField("Surname"); surname.setNullSettingAllowed(true); surname.setNullRepresentation(""); surname.addValidator(new StringLengthValidator("Surname must have 3-15 characters lenght", 3, 15, true)); surname.setValidationVisible(true); inputForm.addComponent(surname); TextField age = new TextField("Age"); age.setNullRepresentation("0"); age.addValidator(new IntegerRangeValidator("Age must be between 1 and 110", 1, 110)); inputForm.addComponent(age); HorizontalLayout btLayout = new HorizontalLayout(); Button btSave = new Button("Save"); btLayout.addComponent(btSave); Button btClear = new Button("Clear"); btLayout.addComponent(btClear); inputForm.addComponent(btLayout); btSave.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { if(!name.isEmpty() && !surname.isEmpty() && !age.isEmpty()){ Boolean save = true; try{ name.validate(); }catch(InvalidValueException e){ save = false; } try{ surname.validate(); }catch(InvalidValueException e){ save = false; } try{ age.validate(); }catch(InvalidValueException e){ save = false; } if(save){ VaadinbestpracticesUI.dataBean.addBean( new DataBean(name.getValue(), surname.getValue(), Integer.valueOf(age.getValue()))); Notification.show("Data saved!"); name.setValue(""); surname.setValue(""); age.setValue("0"); btSave.setComponentError(null); } }else{ Notification.show("All fields must be filled"); } } }); btClear.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { name.clear(); surname.clear(); age.clear(); } }); FieldGroup fGroup = new FieldGroup(fProperties); fGroup.bind(name, "nameValidator"); fGroup.bind(surname, "surnameValidator"); fGroup.bind(age, "integerValidator"); inputForm.setMargin(true); inputForm.setSpacing(true); inputPanel.setContent(inputForm); } @Override public void enter(ViewChangeEvent event) { } }
6.4 DataPage.java
DataPage.java
package com.example.vaadinbestpractices; import com.vaadin.navigator.View; import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; import com.vaadin.ui.Table; import com.vaadin.ui.VerticalLayout; public class DataPage extends VerticalLayout implements View { private static final long serialVersionUID = 1L; public static final String NAME = "datapage"; public DataPage() { Table dataTable = new Table("Data Table", VaadinbestpracticesUI.dataBean); dataTable.setVisibleColumns(new Object[]{"name", "surname", "age"}); dataTable.setHeight("200px"); addComponent(dataTable); } @Override public void enter(ViewChangeEvent event) { } }
6.5 DataBean.java
DataBean.java
package com.example.vaadinbestpractices.data; import java.io.Serializable; public class DataBean implements Serializable { private static final long serialVersionUID = 1L; private String name; private String surname; private Integer age; public DataBean(String pName, String pSurname, Integer pAge) { this.name = pName; this.surname = pSurname; this.age = pAge; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
7. Running the example
Right click on the project folder and choose Run as -> Run on server choose Tomcat 8 server and click finish.
8. Results
8.1 Welcome view
This is the start page. Every time you open the application page, this page is shown.
8.2 Input view
In this page we can add records to our example. Here we validate the fields and then store the data into a container.
8.3 Input view
In this view, we retrieve all the records from the container and shown them into a table.
9. Download the Source Code
This was an example of: Vaadin Best Practices.