|
|
@@ -0,0 +1,884 @@ |
|
|
|
:experimental: |
|
|
|
:sectnums: |
|
|
|
:imagesdir: img |
|
|
|
|
|
|
|
= Vaadin tutorial |
|
|
|
|
|
|
|
This tutorial gives you an overview of how you can use |
|
|
|
https://vaadin.com/framework[Vaadin Framework] to build single-page web UI's for your Java |
|
|
|
application. All you need to start with it is JDK 8 and an https://en.wikipedia.org/wiki/Integrated_development_environment[IDE] like Eclipse. |
|
|
|
No extensive knowledge of Java is even needed to get started, only the basic programming skills are required. Although the tutorial is built for |
|
|
|
Eclipse users, you can use your IDE of choice. |
|
|
|
|
|
|
|
We'll build a simple customer management system. It is not a real app, but we'll |
|
|
|
use an in-memory "backend" so that you can understand how to hook it to an |
|
|
|
existing Java based backend. We'll cover the basic Vaadin development and you |
|
|
|
can use the result as a basis for more experiments with Vaadin, like using |
|
|
|
add-ons, your own custom look and feel (aka. theme), adding new views or optimizing the result for |
|
|
|
mobile support. |
|
|
|
|
|
|
|
You'll need about 20 - 60 minutes to complete the tutorial, depending on your |
|
|
|
existing experience, but naturally you can just do the beginning of |
|
|
|
the exercise or pick any of the steps you want. To start from a specific step, |
|
|
|
we have prepared the example code after each step to be |
|
|
|
downloaded as a zip file. If don't want to do the exercise at all, you can also |
|
|
|
just https://github.com/vaadin/tutorial/[downlood the final application] and |
|
|
|
play around with it. |
|
|
|
|
|
|
|
The tutorial uses Java 8, so ensure you have an up-to-date JDK 8 installed. Most |
|
|
|
linux distributions can use package managers to install JDK8, Windows and Mac users should |
|
|
|
download it from |
|
|
|
http://www.oracle.com/technetwork/java/javase/downloads/index.html[Oracle's Java |
|
|
|
SE site]. |
|
|
|
|
|
|
|
Also make sure you have the latest version of your IDE. Eclipse is available in |
|
|
|
various packages, be sure to download the *Eclipse IDE for Java EE Developers* |
|
|
|
from http://www.eclipse.org/downloads/[eclipse.org]. |
|
|
|
|
|
|
|
== Creating a project using an archetype |
|
|
|
|
|
|
|
As a starting point for the application, we'll use a Maven archetype called |
|
|
|
vaadin-archetype-application. Maven archetypes are project stubs with basic |
|
|
|
build script and some example code. |
|
|
|
|
|
|
|
Start by choosing menu:File[New > Maven Project] from the menu. |
|
|
|
|
|
|
|
image::createMavenProject.jpg[Create a new Maven built Vaadin project.] |
|
|
|
|
|
|
|
TIP: If the "Maven Project" is not visible in the menu, you should switch to the |
|
|
|
_Java EE_ perspective. Use the shortcut button in the toolbar or |
|
|
|
menu:Window[Perspective] to do this. |
|
|
|
|
|
|
|
The first page in the wizard is ok with its defaults, but on the second page |
|
|
|
you'll need to choose _vaadin-archetype-application_ archetype. You can first |
|
|
|
try to find it using the filtering function, but in some cases Eclipse hasn't |
|
|
|
yet indexed the archetype catalog, so you might need to manually add the |
|
|
|
archetype details with the _Add Archetype_ button. The Group Id is _com.vaadin_, |
|
|
|
the Artifact Id is _vaadin-archetype-application_ and as a version you can use |
|
|
|
the latest Vaadin version, at the time of writing _7.6.2_. The _Repository URL_ |
|
|
|
can be left blank. |
|
|
|
|
|
|
|
CAUTION: Eclipse has a bug in its project wizard. If |
|
|
|
_vaadin-archetype-application_ doesn't appear in the listing, even though you |
|
|
|
added it using the _Add Archetype_ button, close the whole new project wizard |
|
|
|
and re-open it by selecting menu:File[New > Maven Project] again. The archetype |
|
|
|
then typically appears in the listing and can be found by the filtering |
|
|
|
functionality. |
|
|
|
|
|
|
|
On the next wizard page, type in _my.vaadin_ as the Group Id, _app_ as the |
|
|
|
Artifact Id and click _Finish_. If this is your first Vaadin app, creating |
|
|
|
a project might take a while, depending on the speed of your network, as Vaadin |
|
|
|
libraries and other dependencies are being downloaded. Maven caches these on |
|
|
|
your local file system and creating your next Maven based Vaadin project will be much |
|
|
|
faster. |
|
|
|
|
|
|
|
Right click on the newly created project and choose menu:Run as[Maven Install]. |
|
|
|
This will initiate a full build of your application and will finally create a |
|
|
|
https://en.wikipedia.org/wiki/WAR_(file_format)[war] file to the "target" directory that you can deploy to your application |
|
|
|
server. As Maven might again need to download some new modules and the project |
|
|
|
has a setup for add-on usage and contains a project specific theme, the first |
|
|
|
build will take a while. |
|
|
|
|
|
|
|
TIP: For the Maven compilation to work you need a JDK to be configured in your |
|
|
|
Eclipse in menu:Window[Preferences > Java > Installed JREs > Add...]. This step |
|
|
|
is necessary at least on Windows, if you are using a fresh installation of |
|
|
|
Eclipse or for some other reason haven't configured a JDK to your Eclipse. The |
|
|
|
JDK by default installs to _\Program Files\Java_ on Windows. You |
|
|
|
can make JDK the default JRE for your Eclipse. |
|
|
|
|
|
|
|
While the build is running, let's have a look at what the archetype created for |
|
|
|
you. You can browse your project resources from the tree structure in the |
|
|
|
_Project Explorer_. Maven's _pom.xml_ on top level contains settings for your |
|
|
|
projects build and declares the used dependencies. Open _Java Resources_ and |
|
|
|
below it _src/main/java_, the main source directory, and _my.vaadin.app_, the |
|
|
|
main Java package that will contain your Vaadin UI code. |
|
|
|
|
|
|
|
TIP: Eclipse shows all project files in the Project Explorer. In this case you |
|
|
|
can also find your _.java_ files via top level _src_ node, but the suggested |
|
|
|
method is to access them via the _Java Resources_ node, which is optimized for |
|
|
|
editing Java source code. |
|
|
|
|
|
|
|
The UI code (and the Servlet declaration) used by the application stub can be |
|
|
|
found in the _MyUI.java_ file. Let's read it through to see how it works. The |
|
|
|
_init_ method of a UI class is triggered when a user enters your web |
|
|
|
application. The VerticalLayout is one of the most used layout objects, which |
|
|
|
are used to position and display other Vaadin components in your UI classes. The |
|
|
|
example code creates one TextField to allow the user to input her name and a |
|
|
|
Button whose click listener dynamically adds a new Label component to the main |
|
|
|
layout. In the end of the init method we just configure the main layout and |
|
|
|
place components into it and set it to be the content of MyUI. |
|
|
|
|
|
|
|
To test your first Vaadin application, right click on the project and choose |
|
|
|
menu:Debug as[Maven build...]. The debug mode is slightly slower than the basic run |
|
|
|
mode, but it often helps you to figure out what is happening in your |
|
|
|
application. |
|
|
|
|
|
|
|
image::debugAsMavenBuild.jpg[Starting the server using a Maven target] |
|
|
|
|
|
|
|
In the dialog, type _Run in jetty_ to the _Name_ input and _jetty:run_ to the |
|
|
|
_Goals_ input. |
|
|
|
|
|
|
|
image::debugAsMavenBuild2.jpg[Generating a Maven launch for jetty:run target] |
|
|
|
|
|
|
|
Before clicking debug, to make sure debugging works properly, add your Java |
|
|
|
project to the source lookup path from the _Source_ tab, like it is being done |
|
|
|
in the image below. |
|
|
|
|
|
|
|
image::debugAsMavenBuildAddSources.jpg[Adding sources for debugging] |
|
|
|
|
|
|
|
Now click _Debug_ to continue. This will download a small Java web server |
|
|
|
(if not cached to your local Maven repository), and use it to host |
|
|
|
your application. Once the server has started, point your browser to the URL |
|
|
|
http://localhost:8080/[http://localhost:8080/] to see the running application. |
|
|
|
|
|
|
|
If you make changes to the code, the jetty server will notice the changes and in |
|
|
|
a couple of seconds most changes are automatically deployed. Reloading the page |
|
|
|
in your browser will show the changes. |
|
|
|
|
|
|
|
TIP: In some cases your JVM might not allow injecting changes on the fly. In |
|
|
|
these cases, Eclipse will complain about "Hot code replacement error". Just |
|
|
|
choose to restart the server to get the latest changes. Many Java developers use |
|
|
|
a commercial tool called http://zeroturnaround.com/software/jrebel/[JRebel] to make code |
|
|
|
replacement work better. |
|
|
|
|
|
|
|
Mastering the usage of the java debugger is also handy to better understand how your |
|
|
|
application actually works and fixing bugs that all developers write at some |
|
|
|
point. As Vaadin is "only" Java code, you can use all of Java's debugging tools, which cannot be done with other UI frameworks where the UI is written (partly) in HTML and/or JavaScript. Double click on the line number in the Java editor, for example of the |
|
|
|
following line in the click listener: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
layout.addComponent(new Label("Thanks " + name.getValue() |
|
|
|
---- |
|
|
|
|
|
|
|
This will add a breakpoint to the selected line. If you then click the button in |
|
|
|
your browser, the execution of the application will stop on that line. Eclipse |
|
|
|
will ask you to enter to _Debugging perspective_ and you can inspect its |
|
|
|
variables and step through the execution. Clicking on the _play_ icon in the |
|
|
|
toolbar will continue the execution. Double click the same line again to remove |
|
|
|
the breakpoint. |
|
|
|
|
|
|
|
image::debugInBreakPointVariable.jpg[Execution in a break point in the button click listener] |
|
|
|
|
|
|
|
Clicking the red square in the Console view will terminate the server process. |
|
|
|
You can restart it easily form the run/debug history. You can find that from the |
|
|
|
small down arrow next to the green play button or bug button (for the debug |
|
|
|
mode) in the toolbar. Alternatively you can use the main menu menu:Run[Run |
|
|
|
history/Debug history > Run in Jetty]. |
|
|
|
|
|
|
|
To get back to the _Java EE Perspective_, an Eclipse mode designed for editing |
|
|
|
Java web app code, click the _Java EE_ button in the toolbar. |
|
|
|
|
|
|
|
== Adding a demo "backend" |
|
|
|
|
|
|
|
Before getting more into real Vaadin development, let's introduce some domain |
|
|
|
objects and a "fake backend". In a real world application, you'll most likely |
|
|
|
have something similar, implemented with, for example, JPA and EJB or a Spring based |
|
|
|
service. |
|
|
|
|
|
|
|
Copy the following three classes from github to your project. Class names |
|
|
|
point to the classes hosted in Github. Copying classes can be done in many ways. |
|
|
|
|
|
|
|
TIP: The fastest way to copy classes using Eclipse is to use your good old |
|
|
|
clipboard. Select the text content of the whole class from your browser, choose |
|
|
|
menu:Edit[Copy], focus the node representing the _my.vaadin.app_ Java package in |
|
|
|
Eclipse's Java Resources view and choose menu:Edit[Paste]. Eclipse is smart |
|
|
|
enough to automatically create a properly named Java file for the class. |
|
|
|
|
|
|
|
* https://raw.githubusercontent.com/vaadin/tutorial/master/src/main/java/my/vaadin/app/CustomerStatus.java[CustomerStatus] - this is a simple enum class |
|
|
|
* https://raw.githubusercontent.com/vaadin/tutorial/master/src/main/java/my/vaadin/app/Customer.java[Customer] - this is the main domain object, a basic Java bean that we'll be |
|
|
|
using in our example |
|
|
|
* https://raw.githubusercontent.com/vaadin/tutorial/master/src/main/java/my/vaadin/app/CustomerService.java[CustomerService] - this is a simple facade via which you can request and modify Customer instances. You can think of this as your entry point to your fake database. |
|
|
|
|
|
|
|
In the next steps, we'll be using these classes and build a UI around them. The |
|
|
|
actual implementation of these classes is not relevant for this tutorial, but |
|
|
|
feel free to have a look around. |
|
|
|
|
|
|
|
== Listing entities in a Grid |
|
|
|
|
|
|
|
TIP: Starting from this step directly? https://github.com/vaadin/tutorial/archive/step2.zip[Download the project] for this step, extract the zip file and choose menu:Import...[Maven>Existing Maven project]. |
|
|
|
|
|
|
|
Often when you start building a UI for a data centric application, the first |
|
|
|
thing you want to do is to list your data from your backend. There are several |
|
|
|
components and methods in Vaadin to do this. In this example, we'll use the Grid |
|
|
|
component for tabular presentation of our customers. |
|
|
|
|
|
|
|
We'll start by introducing a Grid field to the MyUI class. We could of course |
|
|
|
just introduce the Grid as a variable in the init method, but we'll most likely |
|
|
|
want to refer to it later. Also, let's get a reference to the CustomerService. |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
public class MyUI extends UI { |
|
|
|
|
|
|
|
// Add next two lines: |
|
|
|
private CustomerService service = CustomerService.getInstance(); |
|
|
|
private Grid grid = new Grid(); |
|
|
|
|
|
|
|
// the rest is already there... |
|
|
|
@Override |
|
|
|
protected void init(VaadinRequest vaadinRequest) { |
|
|
|
---- |
|
|
|
|
|
|
|
TIP: If you are new to Java development, you probably don't feel comfortable |
|
|
|
with the red compilation error for the line where the Grid got introduced, due |
|
|
|
to a missing import. This is easily fixed in Eclipse by using the |
|
|
|
menu:Source[Organize Imports] command. Learn its shortcut (kbd:[Ctrl-Shift-O] or |
|
|
|
kbd:[CMD-Shift-O] on Macs), you'll be using it a lot in Java development. In |
|
|
|
possible class name collisions, always choose the appropriate class from the |
|
|
|
_com.vaadin.ui_ package if you want to import core Vaadin UI classes like the |
|
|
|
Grid. |
|
|
|
|
|
|
|
To simply list all properties of all Customer objects from the backend service, |
|
|
|
replace the init method with the following snippet: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
@Override |
|
|
|
protected void init(VaadinRequest vaadinRequest) { |
|
|
|
final VerticalLayout layout = new VerticalLayout(); |
|
|
|
|
|
|
|
// add Grid to the layout |
|
|
|
layout.addComponents(grid); |
|
|
|
|
|
|
|
// fetch list of Customers from service and assign it to Grid |
|
|
|
List<Customer> customers = service.findAll(); |
|
|
|
grid.setContainerDataSource(new BeanItemContainer<>(Customer.class, customers)); |
|
|
|
|
|
|
|
layout.setMargin(true); |
|
|
|
setContent(layout); |
|
|
|
} |
|
|
|
---- |
|
|
|
|
|
|
|
TIP: Again, use the organize imports feature. The List object we use here is |
|
|
|
_java.util.List_. |
|
|
|
|
|
|
|
As we'll want to refresh the listing from various places in our application, |
|
|
|
extract the customer listing part into its own "updateList" method with the |
|
|
|
*public* modifier. The public modifier is handy later when we want to update the |
|
|
|
listing from other classes. You can let Eclipse help here by selecting the |
|
|
|
relevant lines and using the "quick fix" feature (kbd:[Ctrl+1] or kbd:[Cmd+1] on |
|
|
|
Macs). The extracted method call looks like this: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
public void updateList() { |
|
|
|
List<Customer> customers = service.findAll(); |
|
|
|
grid.setContainerDataSource(new BeanItemContainer<>(Customer.class, customers)); |
|
|
|
} |
|
|
|
---- |
|
|
|
|
|
|
|
If you try the application now, you'll see that quite many properties of the |
|
|
|
customers are shown in the grid. To limit the visible properties, configure |
|
|
|
the Grid using the _setColumns_ method to only show "firstName", "lastName" and |
|
|
|
"email" properties. |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
grid.setColumns("firstName", "lastName", "email"); |
|
|
|
---- |
|
|
|
|
|
|
|
At this point the body of the MyUI class should look like this (servlet declaration |
|
|
|
omitted): |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
private CustomerService service = CustomerService.getInstance(); |
|
|
|
private Grid grid = new Grid(); |
|
|
|
|
|
|
|
@Override |
|
|
|
protected void init(VaadinRequest vaadinRequest) { |
|
|
|
final VerticalLayout layout = new VerticalLayout(); |
|
|
|
|
|
|
|
grid.setColumns("firstName", "lastName", "email"); |
|
|
|
// add Grid to the layout |
|
|
|
layout.addComponent(grid); |
|
|
|
|
|
|
|
updateList(); |
|
|
|
|
|
|
|
layout.setMargin(true); |
|
|
|
setContent(layout); |
|
|
|
} |
|
|
|
|
|
|
|
public void updateList() { |
|
|
|
// fetch list of Customers from service and assign it to Grid |
|
|
|
List<Customer> customers = service.findAll(); |
|
|
|
grid.setContainerDataSource(new BeanItemContainer<>(Customer.class, customers)); |
|
|
|
} |
|
|
|
---- |
|
|
|
|
|
|
|
You can now save your changes to the file and verify the changes from your browser. |
|
|
|
You can do this at any point during the rest of the tutorial as well. |
|
|
|
|
|
|
|
== Creating live filtering for entities |
|
|
|
|
|
|
|
TIP: Starting from this step directly? https://github.com/vaadin/tutorial/archive/step3.zip[Download the project] for this step, extract the zip file and choose menu:Import...[Maven>Existing Maven project]. |
|
|
|
|
|
|
|
A search functionality is expected in every modern application and it is |
|
|
|
also a nice Vaadin development exercise. Let's add a filtering functionality to |
|
|
|
the Customer listing we created in the previous step. |
|
|
|
|
|
|
|
We'll start by introducing a TextField component as a field to our UI class: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
private TextField filterText = new TextField(); |
|
|
|
---- |
|
|
|
|
|
|
|
In the _init_ method, configure the text field to contain a helpful input prompt |
|
|
|
and add a text change listener to the field. The exact place of these lines is |
|
|
|
not important, but add them, for example, after you have introduced the _layout_ |
|
|
|
object. |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
filterText.setInputPrompt("filter by name..."); |
|
|
|
filterText.addTextChangeListener(e -> { |
|
|
|
grid.setContainerDataSource(new BeanItemContainer<>(Customer.class, |
|
|
|
service.findAll(e.getText()))); |
|
|
|
}); |
|
|
|
---- |
|
|
|
|
|
|
|
TIP: To keep your code more readable, you can use autoformat after you write or |
|
|
|
copy paste code snippets. The default keyboard shortcut in Eclipse is |
|
|
|
kbd:[Ctrl+Shift+F] or kbd:[Cmd+Shift+F] |
|
|
|
|
|
|
|
The text change listener is another listener (in addition to the more commonly |
|
|
|
used ValueChangeListener) you can use with text fields in Vaadin. It is fired |
|
|
|
lazily while the user is typing, but only when there is a small pause in the |
|
|
|
typing. This makes it perfect for this kind of automatic filtering. When the |
|
|
|
user has changed the text, we'll just update the listing like in the updateList |
|
|
|
method, but use the current text as a filter for entries. |
|
|
|
|
|
|
|
To keep the _updateList_ method functional, it should also take into |
|
|
|
consideration the possible value in the filterText field. Change the line for |
|
|
|
fetching the customers into this: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
List<Customer> customers = service.findAll(filterText.getValue()); |
|
|
|
---- |
|
|
|
|
|
|
|
Before adding the text field to the UI, let's improve the usability a bit |
|
|
|
and make a short exercise to compose better components from lower level UI |
|
|
|
components. The search field can naturally be cleared with the keyboard, but let's |
|
|
|
add a clear button next to the text field. Start by adding the following lines |
|
|
|
to the init method, for example right after your filterText configuration: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
Button clearFilterTextBtn = new Button(FontAwesome.TIMES); |
|
|
|
clearFilterTextBtn.setDescription("Clear the current filter"); |
|
|
|
clearFilterTextBtn.addClickListener(e -> { |
|
|
|
filterText.clear(); |
|
|
|
updateList(); |
|
|
|
}); |
|
|
|
---- |
|
|
|
|
|
|
|
Vaadin contains a set of built in icons, from which we use the "X" icon, |
|
|
|
_FontAwesome.TIMES_, here, which most users will recognise as a functionality to clear |
|
|
|
the value. If we set the description to a component, it will be shown as a |
|
|
|
tooltip for those users who hover the cursor over the button and wonder what to |
|
|
|
do with it. In the click listener, we simply clear the text from the field and |
|
|
|
refresh the content of the listing. |
|
|
|
|
|
|
|
Vaadin contains lots of different kinds of layouts. The simplest way to align |
|
|
|
the text field and the button next to each other would be to use a |
|
|
|
HorizontalLayout. An alternative way we use here is using a CssLayout, which is |
|
|
|
a lightweight layout that is easy to customize with css. Even if you wouldn't |
|
|
|
want to play with CSS yourself, you can often use one of the existing style |
|
|
|
rules in the default _Valo_ theme. The following snippet will create a nice |
|
|
|
compact "composition" of both the TextField and the clear button. Add these |
|
|
|
lines to the init method right after you configured the _clearFilterTextBtn_: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
CssLayout filtering = new CssLayout(); |
|
|
|
filtering.addComponents(filterText, clearFilterTextBtn); |
|
|
|
filtering.setStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP); |
|
|
|
---- |
|
|
|
|
|
|
|
Finally, *change* the row in the init method that currently adds only the grid, |
|
|
|
to add both _filtering_ composition and the _grid_ to the main _layout_ of the |
|
|
|
application. |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
layout.addComponents(filtering, grid); |
|
|
|
---- |
|
|
|
|
|
|
|
Now it is a good place to save your changes and try them in your browser. |
|
|
|
|
|
|
|
== Creating a form to edit Customer objects |
|
|
|
|
|
|
|
To edit and add Customer objects we need to create a form, that edits the |
|
|
|
values in our domain objects. This tutorial has two alternative methods to do |
|
|
|
that. Pick either of them. |
|
|
|
|
|
|
|
=== Creating a form using Vaadin Designer |
|
|
|
|
|
|
|
TIP: Starting from this step directly? https://github.com/vaadin/tutorial/archive/step4.zip[Download the project] for this step, extract the zip file and choose menu:Import...[Maven>Existing Maven project]. |
|
|
|
|
|
|
|
The form to edit Customer objects can be built using several methods of which |
|
|
|
the visual composition by drag 'n' drop is the most intuitive. Vaadin |
|
|
|
Designer is an Eclipse plugin that you can install and do WYSIWYG editing of |
|
|
|
your view code. We'll use it to create the form and then hook the editing logic |
|
|
|
to it with Java. |
|
|
|
|
|
|
|
TIP: If you are using another IDE or just prefer to compose your user interface |
|
|
|
with code, take the alternative step, <<Creating a form using plain Java>>, |
|
|
|
where the CustomerForm is composed using plain Java code. |
|
|
|
|
|
|
|
==== Installing Vaadin Designer |
|
|
|
|
|
|
|
Vaadin Designer comes as an integrated part of Vaadin Plugin for Eclipse. It can |
|
|
|
be installed easily via Eclipse Marketplace. Choose menu:Help[Eclipse Marketplace] |
|
|
|
|
|
|
|
In the dialog, just search for Vaadin and click _install_ to mark it for |
|
|
|
installation. Clicking _Install Now_ will take you to choose the modules you want |
|
|
|
and accept the license agreement. |
|
|
|
|
|
|
|
image::pluginEclipseMarketPlace2.jpg[Selecting Vaadin Plugin for Eclipse for installation in Eclipse Marketplace] |
|
|
|
|
|
|
|
If you get a security warning about the software containing unsigned content, |
|
|
|
just accept the warning by clicking OK. After installation, Eclipse asks if you |
|
|
|
want to restart. Click Yes. |
|
|
|
|
|
|
|
TIP: When you use Vaadin Designer for the first time in the next |
|
|
|
step, it will ask for a license key. Get a key from |
|
|
|
https://vaadin.com/designer. If you are not willing to buy a license now, just |
|
|
|
acquire a trial license. |
|
|
|
|
|
|
|
==== Creating the form design |
|
|
|
|
|
|
|
The following screencast will show you how to produce the |
|
|
|
_CustomerFormDesign.html_, a design file we need in this tutorial. Use pause and |
|
|
|
slow motion to follow better what is being done in the video. Feel free to get creative! |
|
|
|
|
|
|
|
video::B5dN69NSS78[youtube, width="640", height="400"] |
|
|
|
|
|
|
|
TIP: At any point of the process, you can also switch to the markup mode where |
|
|
|
you can edit the raw content of the .html file. If you wish to take a shortcut |
|
|
|
or think you did something wrong when using the designer, you can just |
|
|
|
copy-paste the content of https://github.com/vaadin/tutorial/blob/master/src/main/resources/my/vaadin/app/CustomerFormDesign.html[the final state] to your own .html file. |
|
|
|
|
|
|
|
At this point we only have a static mockup of the actual UI. To implement a |
|
|
|
functional form component, we need some Java code as well. Vaadin Designer |
|
|
|
automatically creates a similarly named Java class, but a good habit is to never touch the auto-generated file, in this case the |
|
|
|
CustomerFormDesign.java file. If you'd introduce a new field to your form, |
|
|
|
your changes to CustomerFormDesign would be overridden by the tooling. Instead, |
|
|
|
we'll create a class called CustomerForm which inherits from the auto-generated |
|
|
|
CustomerFormDesign class. |
|
|
|
|
|
|
|
Start by creating a new Java class with the name CustomerForm. In Eclipse, right |
|
|
|
click on the "my.vaadin.app" package and choose menu:New[Class]. Type in the |
|
|
|
name _CustomerForm_, define the superclass as |
|
|
|
_my.vaadin.app.CustomerFormDesign_ and click _finish_. |
|
|
|
|
|
|
|
From the superclass, we inherit all the UI elements that we named when using |
|
|
|
the designer. E.g. by simply referencing to "save" field in the CustomerForm, |
|
|
|
we'll have access to the save button we previously created. |
|
|
|
|
|
|
|
We will later need a reference to the currently edited Customer object, |
|
|
|
CustomerService and the MyUI that uses this class. Add these fields and a |
|
|
|
basic constructor that accepts MyUI as a parameter to the CustomerForm class: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
CustomerService service = CustomerService.getInstance(); |
|
|
|
private Customer customer; |
|
|
|
private MyUI myUI; |
|
|
|
|
|
|
|
public CustomerForm(MyUI myUI) { |
|
|
|
this.myUI = myUI; |
|
|
|
} |
|
|
|
---- |
|
|
|
|
|
|
|
Although the form is not yet fully functional, you might want to see what it |
|
|
|
looks like at this point. Add it as a field to the _MyUI_ class: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
CustomerForm form = new CustomerForm(this); |
|
|
|
---- |
|
|
|
|
|
|
|
Now let's modify the init method in MyUI to show the form. Let's wrap both the |
|
|
|
Grid and the CustomerForm in a horizontal layout and configure the Grid to use |
|
|
|
all of the available space more efficiently. Replace the line |
|
|
|
*layout.addComponents(filtering, grid);* with the following: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
HorizontalLayout main = new HorizontalLayout(grid, form); |
|
|
|
main.setSpacing(true); |
|
|
|
main.setSizeFull(); |
|
|
|
grid.setSizeFull(); |
|
|
|
main.setExpandRatio(grid, 1); |
|
|
|
|
|
|
|
layout.addComponents(filtering, main); |
|
|
|
---- |
|
|
|
|
|
|
|
If you now save your changes and reload your application page in a browser, |
|
|
|
you should see your CustomerForm next to the grid that lists your |
|
|
|
existing entities. |
|
|
|
|
|
|
|
Let's get back to the CustomerForm. The first thing we'll need is to populate |
|
|
|
the options for the select. To add all enum values as valid selections, add the |
|
|
|
following line to the constructor: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
status.addItems(CustomerStatus.values()); |
|
|
|
---- |
|
|
|
|
|
|
|
Let's also improve the UX a bit. When building the design, we already |
|
|
|
emphasized the save button with a ValoTheme.BUTTON_PRIMARY style name. Thus, it |
|
|
|
would be natural if the enter-key would do the same action as clicking the |
|
|
|
save button. Assign a keyboard shortcut to the save button with this line in the |
|
|
|
constructor: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
save.setClickShortcut(KeyCode.ENTER); |
|
|
|
---- |
|
|
|
|
|
|
|
To finish our form, we need to create a public API that we will use in the next |
|
|
|
part from MyUI, to pass in a Customer object that the form should edit. We |
|
|
|
will also add some logic to actually save the changes. We'll start by creating a |
|
|
|
setter method to the Customer field. Just type _setCus_ in the body of the |
|
|
|
class and hit autocomplete (kbd:[Ctrl+Space]) and Eclipse will create a method |
|
|
|
stub for you. Complete it with the following implementation: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
public void setCustomer(Customer customer) { |
|
|
|
this.customer = customer; |
|
|
|
BeanFieldGroup.bindFieldsUnbuffered(customer, this); |
|
|
|
|
|
|
|
// Show delete button for only customers already in the database |
|
|
|
delete.setVisible(customer.isPersisted()); |
|
|
|
setVisible(true); |
|
|
|
firstName.selectAll(); |
|
|
|
} |
|
|
|
---- |
|
|
|
|
|
|
|
In addition to saving the reference of the currently edited Customer object, we are |
|
|
|
calling the _BeanFieldGroup.bindFieldsUnbuffered_ method. It will initialize all |
|
|
|
similarly named editor fields in this form with the values from their |
|
|
|
counterpart in the given Customer object. Also, it will automatically update the |
|
|
|
values in the domain objects as the corresponding field value changes in the |
|
|
|
user interface. |
|
|
|
|
|
|
|
TIP: If the naming convention based databinding doesn't fit your needs, you |
|
|
|
can use |
|
|
|
https://www.vaadin.com/api/com/vaadin/data/fieldgroup/PropertyId.html[PropertyId] |
|
|
|
annotation on fields to explicitly declare the edited property. |
|
|
|
|
|
|
|
We'll also want to ensure the form is visible and that focus goes to the |
|
|
|
firstName field to improve user experience. As we will be using the form to |
|
|
|
edit both new non-persisted objects and existing customers, we will also show |
|
|
|
the delete button only for customers that are already persisted in the backend. |
|
|
|
|
|
|
|
The last thing we need to do is to handle save and delete button clicks. Add |
|
|
|
the following methods to the CustomerForm class: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
private void delete() { |
|
|
|
service.delete(customer); |
|
|
|
myUI.updateList(); |
|
|
|
setVisible(false); |
|
|
|
} |
|
|
|
|
|
|
|
private void save() { |
|
|
|
service.save(customer); |
|
|
|
myUI.updateList(); |
|
|
|
setVisible(false); |
|
|
|
} |
|
|
|
---- |
|
|
|
|
|
|
|
Finally, we'll add listeners to the buttons to call these methods. Adding these |
|
|
|
simple lambda expressions to the constructor will take care of that: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
save.addClickListener(e->this.save()); |
|
|
|
delete.addClickListener(e->this.delete()); |
|
|
|
---- |
|
|
|
|
|
|
|
TIP: For a truly re-usable form component in a real life project, you'd want to |
|
|
|
introduce an interface to replace the myUI field or, even better, use an event |
|
|
|
system like https://vaadin.com/wiki/-/wiki/main/Events+and+contexts[CDI events] |
|
|
|
to completely decouple the components. We'll leave that out of this tutorial for |
|
|
|
simplicity. |
|
|
|
|
|
|
|
=== Creating a form using plain Java |
|
|
|
|
|
|
|
This is an alternative step to the <<Creating a form using Vaadin Designer>>, |
|
|
|
where you'll build the form UI programmatically in plain Java. If you already |
|
|
|
completed the step using Vaadin Designer, you can proceed to |
|
|
|
<<Connecting the form to the application>>. |
|
|
|
|
|
|
|
Start by creating a new Java class with the name CustomerForm. In Eclipse right |
|
|
|
click on the "my.vaadin.app" package and choose menu:New[Class]. Type in the |
|
|
|
name _CustomerForm_, define the superclass as _com.vaadin.ui.FormLayout_ and |
|
|
|
click _finish_. |
|
|
|
|
|
|
|
In the form, we'll need editor fields for each property in our Customer domain |
|
|
|
class. There are different kinds of fields in Vaadin for editing different kinds |
|
|
|
of properties. In this example, we'll use a TextField, a PopupDateField and a |
|
|
|
NativeSelect. Add the following field declarations and action buttons as Java |
|
|
|
fields to the CustomerForm: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
private TextField firstName = new TextField("First name"); |
|
|
|
private TextField lastName = new TextField("Last name"); |
|
|
|
private TextField email = new TextField("Email"); |
|
|
|
private NativeSelect status = new NativeSelect("Status"); |
|
|
|
private PopupDateField birthdate = new PopupDateField("Birthday"); |
|
|
|
private Button save = new Button("Save"); |
|
|
|
private Button delete = new Button("Delete"); |
|
|
|
---- |
|
|
|
|
|
|
|
Also, we will laterneed a reference to the currently edited Customer object, |
|
|
|
CustomerService and the MyUI that uses this class. Add these fields and a |
|
|
|
basic constructor that accepts MyUI as a parameter to the CustomerForm class: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
private CustomerService service = CustomerService.getInstance(); |
|
|
|
private Customer customer; |
|
|
|
private MyUI myUI; |
|
|
|
|
|
|
|
public CustomerForm(MyUI myUI) { |
|
|
|
this.myUI = myUI; |
|
|
|
|
|
|
|
setSizeUndefined(); |
|
|
|
HorizontalLayout buttons = new HorizontalLayout(save, delete); |
|
|
|
buttons.setSpacing(true); |
|
|
|
addComponents(firstName, lastName, email, status, birthdate, buttons); |
|
|
|
} |
|
|
|
---- |
|
|
|
|
|
|
|
In the constructor we make the form size undefined, which practically means it |
|
|
|
will consume the minimum space defined by its content. Then we'll just add all |
|
|
|
the fields to the CustomerForm and add action buttons to the bottom - side-by-side |
|
|
|
using a HorizontalLayout. Although the form is not yet fully functional, you |
|
|
|
might want to see what it looks like at this point. Add it as a field to the MyUI |
|
|
|
class: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
CustomerForm form = new CustomerForm(this); |
|
|
|
---- |
|
|
|
|
|
|
|
Now let's modify the init method in MyUI to show the form. Let's wrap both the |
|
|
|
Grid and the CustomerForm in a horizontal layout and configure the Grid to use |
|
|
|
all of the available space more efficiently. Replace the line |
|
|
|
*layout.addComponents(filtering, grid);* with the following: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
HorizontalLayout main = new HorizontalLayout(grid, form); |
|
|
|
main.setSpacing(true); |
|
|
|
main.setSizeFull(); |
|
|
|
grid.setSizeFull(); |
|
|
|
main.setExpandRatio(grid, 1); |
|
|
|
|
|
|
|
layout.addComponents(filtering, main); |
|
|
|
---- |
|
|
|
|
|
|
|
When you now save your changes and reload your application page in your browser, |
|
|
|
you should see your CustomerForm next to the grid that lists your |
|
|
|
existing entities. |
|
|
|
|
|
|
|
Let's get back to the CustomerForm. The first thing we'll need is to populate |
|
|
|
the options for the select. To add all enum values as valid selections, add the |
|
|
|
following line to the constructor: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
status.addItems(CustomerStatus.values()); |
|
|
|
---- |
|
|
|
|
|
|
|
Let's also improve the UX a bit. The most common thing your users will want to |
|
|
|
do with this kind of form is to save it. Let's decorate the button with a style |
|
|
|
name that makes it more prominent in the UI and give it a keyboard shortcut - |
|
|
|
simply an enter hit in this case: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
save.setStyleName(ValoTheme.BUTTON_PRIMARY); |
|
|
|
save.setClickShortcut(KeyCode.ENTER); |
|
|
|
---- |
|
|
|
|
|
|
|
To finish our form, we need to create a public API that we will use in the next |
|
|
|
part from the MyUI, to pass in a Customer object that the form should edit. We |
|
|
|
will also add some logic to actually save the changes. We'll start by creating a |
|
|
|
setter method for the Customer field. Just type _setCus_ in the body of the |
|
|
|
class and hit autocomplete (kbd:[Ctrl+Space]) and Eclipse will create a method |
|
|
|
stub for you. Complete it with the following implementation: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
public void setCustomer(Customer customer) { |
|
|
|
this.customer = customer; |
|
|
|
BeanFieldGroup.bindFieldsUnbuffered(customer, this); |
|
|
|
|
|
|
|
// Show delete button for only customers already in the database |
|
|
|
delete.setVisible(customer.isPersisted()); |
|
|
|
setVisible(true); |
|
|
|
firstName.selectAll(); |
|
|
|
} |
|
|
|
---- |
|
|
|
|
|
|
|
In addition to saving the reference of the currently edited Customer object, we are |
|
|
|
calling _BeanFieldGroup.bindFieldsUnbuffered_ method. It will initialise all |
|
|
|
similarly named editor fields in this form with the values from their |
|
|
|
counterpart in the given Customer object. Also, it will automatically update the |
|
|
|
values in the domain objects as the corresponding field value changes in the |
|
|
|
user interface. |
|
|
|
|
|
|
|
TIP: If the naming convention based databinding doesn't fit your needs, you |
|
|
|
can use |
|
|
|
https://www.vaadin.com/api/com/vaadin/data/fieldgroup/PropertyId.html[PropertyId] |
|
|
|
annotation on fields to explicitly declare the edited property. |
|
|
|
|
|
|
|
We'll also want to ensure the form is visible and that the focus goes to the |
|
|
|
firstName field to improve the user experience. As we will be using the form to |
|
|
|
edit both new non-persisted objects and existing customers, we will also show |
|
|
|
the delete button only for customers that are already persisted in the backend. |
|
|
|
|
|
|
|
The last thing we need to do is to handle save and delete button clicks. Add |
|
|
|
the following methods to the CustomerForm class: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
private void delete() { |
|
|
|
service.delete(customer); |
|
|
|
myUI.updateList(); |
|
|
|
setVisible(false); |
|
|
|
} |
|
|
|
|
|
|
|
private void save() { |
|
|
|
service.save(customer); |
|
|
|
myUI.updateList(); |
|
|
|
setVisible(false); |
|
|
|
} |
|
|
|
---- |
|
|
|
|
|
|
|
Finally, we'll add listeners to the buttons to call these methods. Adding these |
|
|
|
simple lambda expressions to the constructor will take care of that: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
save.addClickListener(e->this.save()); |
|
|
|
delete.addClickListener(e->this.delete()); |
|
|
|
---- |
|
|
|
|
|
|
|
TIP: For a truly re-usable form component in a real life project, you'd want to |
|
|
|
introduce an interface to replace the myUI field or, event better, use an event |
|
|
|
system like https://vaadin.com/wiki/-/wiki/main/Events+and+contexts[CDI events] |
|
|
|
to completely decouple the components. We'll leave that out of this tutorial for |
|
|
|
simplicity. |
|
|
|
|
|
|
|
== Connecting the form to the application |
|
|
|
|
|
|
|
TIP: Starting from this step directly? https://github.com/vaadin/tutorial/archive/step5.zip[Download the project] for this step, extract the zip file and choose menu:Import...[Maven>Existing Maven project]. |
|
|
|
|
|
|
|
In this part, we'll use the CustomerForm class, which we created in the |
|
|
|
previous step, from the MyUI class. We will use it for both editing the existing |
|
|
|
customers and creating new ones. |
|
|
|
|
|
|
|
In the previous part, we already added the form to the _MyUI_ to see what it looks |
|
|
|
like. By default, we want it to be invisible, so let's first hide it |
|
|
|
by adding this line to the _init_ method of MyUI class: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
form.setVisible(false); |
|
|
|
---- |
|
|
|
|
|
|
|
To edit the customer chosen from the Grid, add the following selection listener to |
|
|
|
the end of the _init_ method: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
grid.addSelectionListener(event -> { |
|
|
|
if (event.getSelected().isEmpty()) { |
|
|
|
form.setVisible(false); |
|
|
|
} else { |
|
|
|
Customer customer = (Customer) event.getSelected().iterator().next(); |
|
|
|
form.setCustomer(customer); |
|
|
|
} |
|
|
|
}); |
|
|
|
---- |
|
|
|
|
|
|
|
In the listener, we simply take the Customer object of the selected row and pass it to |
|
|
|
the CustomerForm for editing. In the previous step, we added a side effect to the |
|
|
|
_setCustomer_ method that will bind the domain object to the corresponding fields |
|
|
|
and make it visible. If the selection is empty, we'll hide the form. |
|
|
|
|
|
|
|
To allow users to also create new customer records, we'll create a simple "Add |
|
|
|
customer button" to the top of the UI, right next to the _filtering_ composition |
|
|
|
we have already built from a CssLayout, a TextField and a Button. Introduce the new |
|
|
|
Button with a click listener, by adding the following lines to the _init_ method, |
|
|
|
right after where you introduced the _filtering_ composition: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
Button addCustomerBtn = new Button("Add new customer"); |
|
|
|
addCustomerBtn.addClickListener(e -> { |
|
|
|
grid.select(null); |
|
|
|
form.setCustomer(new Customer()); |
|
|
|
}); |
|
|
|
---- |
|
|
|
|
|
|
|
In the click listener, we first clear a possible selection from the grid and then |
|
|
|
instantiate a new Customer object and pass that to the form for editing. |
|
|
|
|
|
|
|
To add it beside our _filtering_ composition, we can use a HorizontalLayout to |
|
|
|
create a toolbar where we place both components. First, introduce a toolbar like |
|
|
|
this after the previously created _addCustomerBtn_: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
HorizontalLayout toolbar = new HorizontalLayout(filtering, addCustomerBtn); |
|
|
|
toolbar.setSpacing(true); |
|
|
|
---- |
|
|
|
|
|
|
|
And, again, *replace* the line that populates your main layout to add the |
|
|
|
toolbar instead of just the filtering composition, which we just moved to the |
|
|
|
_toolbar_ layout. |
|
|
|
|
|
|
|
[source,java] |
|
|
|
---- |
|
|
|
layout.addComponents(toolbar, main); |
|
|
|
---- |
|
|
|
|
|
|
|
All planned features are now done. You can save the changes and play around with |
|
|
|
the application. If something went wrong, you can also download an example of |
|
|
|
https://github.com/vaadin/tutorial[the final application] and see what went wrong. |
|
|
|
|
|
|
|
== It works! What next? |
|
|
|
|
|
|
|
Congratulations! Users can now create, read, update and delete customer records |
|
|
|
stored in the demo backend and you have completed creating your first CRUD UI |
|
|
|
with Vaadin. |
|
|
|
|
|
|
|
If you are an experienced Java developer, you are probably already full of ideas of |
|
|
|
how you can use your existing skills and create new shiny web UIs for your |
|
|
|
existing Java apps. If you want more ideas of how to create full stack |
|
|
|
applications, you might, for example, go through the |
|
|
|
http://spring.io/guides/gs/crud-with-vaadin/[Creating CRUD UI with Vaadin] guide |
|
|
|
and create a bit similar UI with a real database backend implemented with Spring |
|
|
|
Data JPA. We have also collected a couple of other resources for an easy |
|
|
|
start in your Vaadin developer career. |
|
|
|
|
|
|
|
* https://vaadin.com/docs/-/part/framework/introduction/intro-overview.html[Vaadin online documentation] |
|
|
|
* http://spring.io/guides/gs/crud-with-vaadin/[Creating CRUD UI with Vaadin] - the tutorial for your first Vaadin application using a Spring based backend. |
|
|
|
* https://github.com/mstahv/jpa-invoicer[Jave EE example app] - a Vaadin app example for creating invoices that uses Java EE backend, Apache DeltaSpike Data for simple JPA layer, OAuth2 based login, PDF generation etc. |
|
|
|
* http://vaadin.com/directory[Directory] - a vast source of awesome Vaadin add-ons |
|
|
|
|
|
|
|
|