Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- by [[http://vaadin.com/petter|Petter Holmström]]
- == Introduction ==
- The purpose of this article is to briefly demonstrate how a Vaadin user interface can be added to a Grails application. Prerequisites:
- * Knowledge of [[http://groovy.codehaus.org|Groovy]], [[http://grails.org|Grails]] and Vaadin
- * A working installation of Grails 1.3.0.RC1 or later
- * A Groovy code editor, or an IDE with Grails support
- == Setting up the demo application ==
- In this article, we are going to add a Vaadin user interface to the application described in [[https://www.ibm.com/developerworks/java/library/j-grails01158/|this tutorial]].
- Start by creating a new Grails project:
- {{{$ grails create-app tripplanner}}}
- Change into the //tripplanner// directory and create a domain class:
- {{{$ grails create-domain-class Trip}}}
- Open //grails-app/domain/tripplanner/Trip.groovy// and add some fields:
- {{{
- package tripplanner
- class Trip {
- static constraints = {
- }
- String name
- String city
- Date startDate
- Date endDate
- String purpose
- String notes
- }
- }}}
- Then generate a controller and corresponding views:
- {{{$ grails generate-all tripplanner.Trip}}}
- You can now start the application and try it out:
- {{{$ grails run-app}}}
- == Creating the Vaadin UI ==
- Now, [[http://vaadin.com/download|download]] the latest Vaadin jar and place it in the //lib// directory. Next, create a new class named {{{tripplanner.VaadinApp}}} and place it in the //src/groovy// directory. The complete implementation of the class is as follows:
- {{{
- package tripplanner
- import com.vaadin.ui.*
- import com.vaadin.data.*
- import com.vaadin.data.util.*
- class VaadinApp extends com.vaadin.Application {
- void init() {
- def window = new Window("Hello Vaadin!", new SplitPanel(SplitPanel.ORIENTATION_HORIZONTAL))
- setMainWindow window
- // Trip editor
- def tripEditor = new Form()
- tripEditor.setSizeFull()
- tripEditor.layout.setMargin true
- tripEditor.immediate = true
- tripEditor.visible = false
- def saveButton = new Button("Save", new Button.ClickListener() {
- void buttonClick(Button.ClickEvent event) {
- Trip.withTransaction { status ->
- def tripInstance = tripEditor.itemDataSource.bean
- if (!tripInstance.save(flush:true)) {
- window.showNotification "Could not save changes"
- } else {
- window.showNotification "Changes saved"
- }
- }
- }
- })
- tripEditor.footer = new HorizontalLayout()
- tripEditor.footer.addComponent saveButton
- // Trip browser
- def container = new BeanItemContainer<Trip>(Trip.class)
- Trip.list().each {
- container.addBean it
- }
- def browser = new VerticalLayout()
- browser.setSizeFull()
- def table = new Table()
- table.containerDataSource = container
- table.selectable = true
- table.setSizeFull()
- table.visibleColumns = ["name", "purpose", "startDate", "endDate", "city", "notes"]
- table.immediate = true
- table.addListener new Property.ValueChangeListener() {
- void valueChange(Property.ValueChangeEvent event) {
- if (table.value) {
- tripEditor.itemDataSource = table.getItem(table.value)
- tripEditor.visibleItemProperties = ["name", "purpose", "startDate", "endDate", "city", "notes"]
- } else {
- tripEditor.itemDataSource = null
- }
- tripEditor.visible = tripEditor.itemDataSource != null
- }
- }
- browser.addComponent table
- def toolbar = new HorizontalLayout()
- def addButton = new Button("+", new Button.ClickListener() {
- void buttonClick(Button.ClickEvent event) {
- Trip.withTransaction { status ->
- def tripInstance = new Trip()
- tripInstance.name = "A new trip"
- tripInstance.endDate = new Date()
- tripInstance.startDate = new Date()
- tripInstance.purpose = ""
- tripInstance.notes = ""
- tripInstance.city = ""
- if (tripInstance.save(flush:true)) {
- container.addBean tripInstance
- } else {
- window.showInstance "Could not add trip"
- }
- }
- }
- })
- toolbar.addComponent addButton
- def removeButton = new Button("-", new Button.ClickListener() {
- void buttonClick(Button.ClickEvent event) {
- if (table.value) {
- Trip.withTransaction { status ->
- def tripInstance = table.value
- tripInstance.delete(flush:true)
- container.removeItem tripInstance
- table.value = null
- }
- }
- }
- })
- toolbar.addComponent(removeButton)
- browser.addComponent toolbar
- browser.setExpandRatio table, 1.0f
- mainWindow.addComponent browser
- mainWindow.addComponent tripEditor
- }
- }
- }}}
- Most of the code is ordinary Vaadin UI creation code, which we will not discuss in this article. However, the interesting parts are those dealing with domain objects.
- We start with the code that fetches the Trips from the database and puts them in a Vaadin container:
- {{{
- def container = new BeanItemContainer<Trip>(Trip.class)
- Trip.list().each {
- container.addBean it
- }
- }}}
- This code snippet creates a standard Vaadin {{{BeanItemContainer}}}, fetches a list of all the Trips and adds each Trip to the container.
- Next, we move on to the code for adding new Trips:
- {{{
- Trip.withTransaction { status ->
- def tripInstance = new Trip()
- tripInstance.name = "A new trip"
- tripInstance.endDate = new Date()
- tripInstance.startDate = new Date()
- tripInstance.purpose = ""
- tripInstance.notes = ""
- tripInstance.city = ""
- if (tripInstance.save(flush:true)) {
- container.addBean tripInstance
- } else {
- window.showInstance "Could not add trip"
- }
- }
- }}}
- As Vaadin is not controlled by Grails, we have to handle transactions ourselves. Fortunately, each Grails domain class has a method named {{{withTransaction}}} that makes this easy (see the Grails [[http://grails.org/doc/latest/ref/Domain%20Classes/withTransaction.html|documentation]] for details).
- While inside the transaction, we create a new {{{Trip}}} instance, populate it with default values (otherwise the database will not accept it), save it to the database and add it to the container so that it shows up in the UI. Please note, that a real-world application might want to ask the user for data before saving the domain object instead of using default data. Also, the validation errors could probably be handled in a more user friendly way.
- Finally, we take a look at the code snippet for deleting Trips:
- {{{
- Trip.withTransaction { status ->
- def tripInstance = table.value
- tripInstance.delete(flush:true)
- container.removeItem tripInstance
- table.value = null
- }
- }}}
- We fetch the currently selected Trip from the table, delete it from both the database and the container and clear the selection of the table. Again, we have to handle the transactions ourselves.
- == Deploying the Vaadin UI ==
- Now when the Vaadin UI has been implemented, we have to deploy it. We start by creating a new servlet in the {{{tripplanner}}} package (found in the //src/groovy// directory):
- {{{
- package tripplanner
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServletRequest;
- import com.vaadin.Application;
- class VaadinAppServlet extends com.vaadin.terminal.gwt.server.AbstractApplicationServlet {
- @Override
- protected Class<? extends Application> getApplicationClass()
- throws ClassNotFoundException {
- return VaadinApp.class
- }
- @Override
- protected Application getNewApplication(HttpServletRequest request)
- throws ServletException {
- return new VaadinApp()
- }
- }
- }}}
- We have to implement the class ourselves, as the default {{{ApplicationServlet}}} class cannot find the {{{VaadinApp}}} class for some reason.
- Finally, we have to configure the //web.xml// file. As Grails normally uses a default file based on a template, we have to start by installing the template in order to be able to change it:
- {{{$ grails install-templates}}}
- Now open the //web.xml// file found in the //src/templates/war// directory and add the following elements:
- {{{
- <servlet>
- <servlet-name>VaadinServlet</servlet-name>
- <servlet-class>tripplanner.VaadinAppServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>VaadinServlet</servlet-name>
- <url-pattern>/VAADIN/*</url-pattern>
- </servlet-mapping>
- }}}
- Now you can start the application again:
- {{{$ grails run-app}}}
- If everything went as expected, you should now be able to access the new Vaadin UI at [[http://localhost:8080/tripplanner/VAADIN]]. If you like, you can also open the UI generated by Grails and verify that the Trips show up in both user interfaces (please remember, though, that you have to restart the Vaadin application in order to refresh the list of Trips).
- == Summary ==
- Using Vaadin and Groovy is really no different than using Vaadin and Java, especially now when Groovy supports anonymous inner classes. Although I had never used Groovy before I wrote this article, its syntax seems to be more efficient than Java's, making it possible to do more with less lines of code. Therefore, I think Groovy and Vaadin may turn out to be a very nice combination.
- As for Grails and Vaadin, I am not that convinced. //I had never used Grails before I wrote this article so I may be wrong//, but to me, it seems like Grails is a Groovy-based MVC framework with a built-in object persistence solution (built on top of Hibernate). As Vaadin and the Grails MVC are very different, the only reason why one would want to use Vaadin and Grails is the persistence solution.
- So in conclusion, it is possible to write a Vaadin user interface for a Grails application. However, if you are starting a new project, you should ask yourself what you are winning by basing the application on Grails. If you can't answer that question, it might be a good idea to look at some alternatives (such as plain Groovy) before the coding begins.
- **Attention all Grails experts out there:** If you can find a better way of incorporating Vaadin into grails, or better arguments for why it is a good idea to base Vaadin applications on Grails, please let me know!
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement