Here is a small widgetset-sample-project as a reference (Grails 3.2.11).
Download and unzip the archive, then:
cd sample-project gradle vaadin-compile-widgetset grails run-app
Hope it helps!
cheers, weHe
Here is a small widgetset-sample-project as a reference (Grails 3.2.11).
Download and unzip the archive, then:
cd sample-project gradle vaadin-compile-widgetset grails run-app
Hope it helps!
cheers, weHe
[see my update for Vaadin 8 at the bottom!]
Over the last ten years I’ve been doing quite a lot of work with Grails and ZK plugged-in and got great results – and I got quite used to using a component based UI toolkit in Grails. Unfortunately the ZK plugin for Grails only supports the Grails 2.x branch (with it’s latest update in Sept 2014) so that ceased to be an option for new projects.
Vaadin on the other hand also is a vibrant and powerful component toolkit for Java, so I’d fancy it as a replacement… but so far, Ondrej Kvasnovsky’s Vaadin-Plugin was also only available for the Grails 2.x branch which is at the time being (April 2017) over 2 years behind… so unfortunately, Vaadin wasn’t really an option for a component based UI toolkit in Grails either lately – quite a dire situation for a Grails addict.
But now Ondrej has finally finished his version of the Vaadin-Plugin for the current Grails 3.x branch. It plugs Vaadin 7.7.8 into Grails 3.2.8 (or later) – while Vaadin 8 is already supported by a later version of the same plugin right now (see note at the bottom).
The Vaadin-Website has a great Getting-Started Tutorial at vaadin.com/tutorial which
walks you step by step through the creation of a customer list and form and even comes with a set of videos – and which I recommend as a good first introduction to Vaadin with Java. I’ve created a Groovy-vied version of this tutorial using Gorm against H2-DB in order to test the old version of the Vaadin plugin against Grails 2.x and documented the result in this blog post.
For the new Grails3- Vaadin-Plugin I’ve adapted this tutorial project – and the plugin seems to work just well – YEAH! Here is how to put this togehter:
As of now, the new plugin’s documentation / tutorial is only on the plugins issues-pages on github – you’ll need to follow the steps therein, but note:
That should do the job!
All the best & cheers, weHe
Update: since version 2 the plugin uses Vaadin 8
(as opposed to 7.7.8 in the earlier version used here):
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,
Unterstützung der Aufgaben zur Verrechnung in der Dienststelle
und aller Abläufe vor Ort beim Betrieb in den Kindergärten.
[ Videos und Diaschau sind am Ende der Seite!]
[Videos & Slideshow at the bottim of the page!]
vereinfachte Schittstelle für die Gruppe zur Bewegung im Raum
Die externen Abhängigkeiten auf Benutzer-Verzeichnis, Buchhaltung und die eAkte sind im Code gekapselt und könnten durch vergleichbare andere ersetzt werden.
Auf Anfrage kann ich ein Docker-Image zur Verfügung stellen, das ohne weitere Abhängigkeiten lauffähig ist: es enthält eine Linux-VM mit Java, Tomcat und die KiGa-Anwendung, MySql mit anonymisierten KiGa-Daten und mit den Daten einer Ersatz-Buchhaltung, ein Fake-LDAP-DropIn mit CSV-Daten.
Bei Interesse senden sie mir bitte eine Nachricht und ich nehme mit Ihnen Kontakt auf.
[podlovevideo webm=“http://wehe.autom.at/media/KiGaDemo-Gruppe.webm“ mp4=“http://wehe.autom.at/media/KiGaDemo-Gruppe.mp4″ duration=“00:12:38″ title=“Rundgang KiGa/Gruppe“]
[podlovevideo webm=“http://wehe.autom.at/media/KiGaDemo-KiGa.webm“ mp4=“http://wehe.autom.at/media/KiGaDemo-KiGa.mp4″ duration=“00:08:35″ title=“Rundgang KiGa/Leitung“]
[podlovevideo webm=“http://wehe.autom.at/media/KiGaDemo-DS.webm“ mp4=“http://wehe.autom.at/media/KiGaDemo-DS.mp4″ duration=“00:16:34″ title=“Rundgang KiGa/Dienststelle“]
Benützen sie die Tastatur, Pfeile, oder die Punktleiste zum Blättern,
ggfs auch den Vollbildmodus.