Vaadin on Grails 2.x

I’ve been doing quite a lot of work with Grails and Zkoss over the last ten years and got great results.

Recently I came across Vaadin several times and got quite curious to see how Grails and Vaadin would go together. So I sat down and read Vaadin’s great Java Tutorial (highly recommended!) and used  Ondrej Kvasnovsky’s Vaadin-Plugin  to run the code in Grails after simplifying it for Grails / Groovy.

As the Plugin right now just works on Grails 2.x, I used the most recent release of the Grails 2.x branch, Grails 2.5.5 (a release of the plugin for Grails 3.x is promised).

Here’s my entire project as a ZIP archive!

Other than the pure Java version, my Grails version does in fact persist the Customer data! BootStrap.groovy checks if it runs in development mode and creates the test customers. The data source defiition uses H2-Db with a ‚create-drop‘ policy, so the DB is created anew and populated on each start of the app. Both the domain and the service classes are much smaller due to Grail’s instrumentation like e.g. for the hibernate persistence, automatic getters & setters, and all the other magic.

Have a look at the code – thanks to Grails and Groovy, it is much cleaner and leaner than the original Java version, at least IMHO.

src/groovy/my/app/CustomerStatus.groovy:

package my.app

public enum CustomerStatus {
    ImportedLead, NotContacted, Contacted, Customer, ClosedLost
}

grails-app/domain/my/app/Customer.grrovy:

package my.app

public class Customer implements Comparable {

    String firstName = ""
    String lastName = ""
    Date birthDate
    CustomerStatus status = CustomerStatus.ImportedLead;
    String email = ""


    static constraints = {
        firstName blank: false, nullable: false, maxSize: 50
        lastName blank: false, nullable: false, maxSize: 50
        email blank: false, nullable: false, maxSize: 50, email: true
    }

    static mapping = {
        table 'customer'
        version false
        firstName column: 'firstname'
        lastName column: 'lastname'
        email column: 'email'
    }

    public String toString() {
        return firstName + " " + lastName;
    }

    int compareTo(Object other) {
        this.toString().compareTo(other.toString())
    }

}

grails-app/services/my/app/CustomerService.groovy:

package my.app

class CustomerService {

    private static TEST_DATA = [
            "Gabrielle Patel", "Brian Robinson", "Eduardo Haugen",
            "Koen Johansen", "Alejandro Macdonald", "Angel Karlsson", "Yahir Gustavsson", "Haiden Svensson",
            "Emily Stewart", "Corinne Davis", "Ryann Davis", "Yurem Jackson", "Kelly Gustavsson",
            "Eileen Walker", "Katelyn Martin", "Israel Carlsson", "Quinn Hansson", "Makena Smith",
            "Danielle Watson", "Leland Harris", "Gunner Karlsen", "Jamar Olsson", "Lara Martin",
            "Ann Andersson", "Remington Andersson", "Rene Carlsson", "Elvis Olsen", "Solomon Olsen",
            "Jaydan Jackson", "Bernard Nilsen"
    ]

    /**
     * retrieves customers and filters
     * @param filter optional, to be contained in customer.toString(), case ignored
     * @return customers matched - or all
     */
    List<Customer> findAll(String filter=null) {
        List<Customer> rtn = Customer.findAll()
        if (filter) {
            filter = filter.toLowerCase()
            rtn = rtn.findAll {Customer cust -> cust.toString().toLowerCase().contains(filter)}
        }
        rtn.sort()
    }

    /**
     * called by BootStrap
     * @param log
     */
    public static void createTestCustomers(log) {
        log.trace "storing customer data:"
        listTestCustomers().each { Customer c ->
            log.trace " '${c}'"
            c.save()
        }
    }
         private static List<Customer> listTestCustomers() {
            List<Customer> rtn = []
            Random r = new Random(0);
            for (String name : TEST_DATA) {
                String[] split = name.split(" ")
                Customer c = new Customer()
                c.firstName = split[0]
                c.lastName = split[1]
                c.email = split[0].toLowerCase() + "@" + split[1].toLowerCase() + ".com"
                c.status = CustomerStatus.values()[r.nextInt(CustomerStatus.values().length)]
                Calendar cal = Calendar.getInstance()
                int daysOld = 0 - r.nextInt(365 * 15 + 365 * 60)
                cal.add(Calendar.DAY_OF_MONTH, daysOld)
                c.birthDate = cal.getTime()
                rtn << c
            }
            rtn
        }


}

src/groovy/my/app/MyGui.groovy:

package my.app

import com.vaadin.annotations.Theme
import com.vaadin.data.util.BeanItemContainer
import com.vaadin.server.FontAwesome
import com.vaadin.ui.Button
import com.vaadin.ui.CssLayout
import com.vaadin.ui.Grid
import com.vaadin.ui.HorizontalLayout
import com.vaadin.ui.TextField
import com.vaadin.ui.UI
import com.vaadin.ui.VerticalLayout
import com.vaadin.server.VaadinRequest
import com.vaadin.grails.Grails
import com.vaadin.ui.themes.ValoTheme

@Theme('mytheme')
class MyUI extends UI {

    private Grid grid = new Grid()
    private CustomerService customerService = Grails.get(CustomerService)
    private TextField filterText = new TextField()
    private CustomerForm form = new CustomerForm(this)

    protected void init(VaadinRequest vaadinRequest) {
        VerticalLayout layout = new VerticalLayout()

        filterText.setInputPrompt("filter by name...")
        filterText.addTextChangeListener { evt -> grid.containerDataSource =
                    new BeanItemContainer<Customer>(Customer.class, customerService.findAll(evt.text) )
        }

        Button clearFilterTextBtn = new Button(FontAwesome.TIMES)
        clearFilterTextBtn.description = "clear the current filter"
        clearFilterTextBtn.addClickListener { evt ->
            filterText.clear()
            updateList()
        }

        CssLayout filtering = new CssLayout()
        filtering.addComponents(filterText, clearFilterTextBtn)
        filtering.styleName = ValoTheme.LAYOUT_COMPONENT_GROUP

        Button addCustomerBtn = new Button("Add new customer");
        addCustomerBtn.addClickListener { evt ->
            grid.select(null)
            form.customer = new Customer()
        }

        HorizontalLayout toolbar = new HorizontalLayout(filtering, addCustomerBtn)
        toolbar.spacing = true
        grid.setColumns('firstName', 'lastName', 'email')

        HorizontalLayout main = new HorizontalLayout(grid, form)
        main.spacing = true
        main.setSizeFull()
        grid.setSizeFull()
        main.setExpandRatio(grid,1)
        layout.addComponents(toolbar, main)

        updateList()
        layout.margin = true
        content = layout
        grid.addSelectionListener { evt ->
            if (evt.selected.empty) {
                form.visible = false
            } else {
                Customer customer = (Customer) evt.selected.iterator().next()
                form.customer = customer
            }
        }
        form.visible = false
    }

    public void updateList() {
        List<Customer> customers = customerService.findAll(filterText.value)
        grid.setContainerDataSource(new BeanItemContainer<>(Customer.class, customers))
    }

}

src/groovy/my/app/CustomerForm.groovy:
(localize birthdate to your own locale and date format in the constructor)

package my.app

import com.vaadin.data.fieldgroup.BeanFieldGroup
import com.vaadin.event.ShortcutAction.KeyCode
import com.vaadin.ui.Button
import com.vaadin.ui.FormLayout
import com.vaadin.ui.HorizontalLayout
import com.vaadin.ui.NativeSelect
import com.vaadin.ui.PopupDateField
import com.vaadin.ui.TextField
import com.vaadin.grails.Grails
import com.vaadin.ui.themes.ValoTheme

class CustomerForm extends FormLayout {

    private CustomerService service = Grails.get(CustomerService)
    private Customer customer
    private MyUI myUI

    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")

    public CustomerForm(MyUI myUI) {
        this.myUI = myUI
        status.addItems(CustomerStatus.values())
        // birthdate.locale = Locale.GERMAN
        // birthdate.dateFormat = 'dd.MM.yyyy'
        save.styleName = ValoTheme.BUTTON_PRIMARY
        save.setClickShortcut(KeyCode.ENTER)    // property assignment (=) breaks code!
        delete.styleName = ValoTheme.BUTTON_DANGER
        setSizeUndefined()
        HorizontalLayout buttons = new HorizontalLayout(save, delete)
        buttons.spacing = true
        addComponents(firstName, lastName, email, status, birthdate, buttons)
        save.addClickListener { evt -> this.save() }
        delete.addClickListener { evt -> this.delete() }
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
        BeanFieldGroup.bindFieldsUnbuffered(customer, this);
        delete.visible = customer.id>0  // Show delete button for only customers already in the database
        this.visible = true
        firstName.selectAll()
    }

    private void delete() {
        customer.delete(flush:true)
        myUI.updateList()
        this.visible = false
    }

    private void save() {
        customer.save()
        myUI.updateList()
        this.visible = false
    }
    
}

grails-app/conf/BootStrap.groovy:

import my.app.CustomerService
import grails.util.Environment

class BootStrap {
    CustomerService customerService	// spring dependency injection!

    def init = { servletContext ->

        if (Environment.current.name == 'development') {
            customerService.createTestCustomers(log)
        }
    }

    def destroy = {
    }
}

You will also need these – they are in the archive:

grails-app/conf/BuidConfig.groovy
grails-app/conf/Config.groovy
grails-app/conf/DataSource.groovy
grails-app/conf/UrlMapping.groovy
grails-app/conf/VaadinConfig.groovy

Hope it helps!
cheers, weHe

— end,

 

 

 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert