1. Two-minutes tutorial

An example is worth a thousand words:

import ch.frankel.kaadin.* (1)
import com.vaadin.server.*
import com.vaadin.ui.*

class FirstStepUI(): UI() {
    override fun init(request: VaadinRequest) {
        verticalLayout(margin = true, spacing = true) { (2)
            label("Welcome to Vaadin") (3)
        }
    }
}
1 Import the namespace ch.frankel.kaadin.*
2 The topmost component will automatically be added as the UI content. There’s one such function per common Vaadin component.
3 Nested blocks allow to add children components and configure them

2. Usage

2.1. Requirements

Kaadin dependencies include:

  • Java 8

  • Vaadin 7.7.0

  • Kotlin 1.0.4

While the Java dependency is a strict requirement, other Vaadin and Kotlin versions might work successfully.

2.2. Build and use

Building Kaadin only requires Java and Maven:

mvn install

To use in one’s project, just add the following dependency to its POM:

<dependency>
    <groupId>ch.frankel.kaadin</groupId>
    <artifactId>kaadin-core</artifactId>
    <version>0.1-SNAPSHOT</version>
</dependency>

2.3. User manual

The best user manual is a sampler demoing usage of each function.

It’s a full-fledged Vaadin application so it can be run either inside your own servlet-container (3.0+) or directly from the command-line:

mvn install
cd kaadin-sampler
mvn package org.apache.tomcat.maven:tomcat7-maven-plugin:run

2.4. Available components

Please refer to the components list.

2.5. Dokka

Please refer to the generated Dokka.

3.1. Setting the theme

Theme setting is common to all Vaadin applications.

Beside the @Theme annotation, Kotlin’s syntax allows for a straightforward code:

class SetThemeUI() : UI() {
    override fun init(request: VaadinRequest) {
        theme = "valo" (1)
    }
}
1 Use Kotlin’s properties syntactic sugar, equivalent of setTheme("valo")

In addition, Kaadin provides a theme(theme: String) function to set the theme. This allows for dynamic behavior:

import ch.frankel.kaadin.horizontalLayout
import ch.frankel.kaadin.optionGroup
import com.vaadin.data.*
import com.vaadin.server.*
import com.vaadin.ui.*

val changeTheme: (Property.ValueChangeEvent) -> Unit = { (1)
    ch.frankel.kaadin.theme(it.property.value.toString()) (2)
}

class SetThemeUI() : UI() {
    override fun init(request: VaadinRequest) {
        val themes = arrayListOf("valo", "reindeer", "runo", "chameleon", "liferay")
        horizontalLayout(spacing = true, margin = true) {
            optionGroup("Select theme", themes, changeTheme) { (3)
                select("valo")
            }
        }
    }
}
1 Define the listener function as a lambda function
2 Set the theme, taken from the radio button value
3 Create the option group, passing along the lambda

3.2. Navigator API

Vaadin Navigator’s API requires components added as views to implement the View interface. Most of the time, this is only boilerplate since the enter() method is empty.

Kaadin allows to use any of the existing layout as views - provided one of Kaadin’s method to create them is used.

import ch.frankel.kaadin.*
import com.vaadin.server.*
import com.vaadin.ui.*

class NavigatorUI() : UI() {
    private val firstView = firstView()
    private val secondView = secondView()
    override fun init(request: VaadinRequest) {
        navigator(verticalLayout(margin = true)) { (1)
            view("", firstView) (2)
            view("second", secondView) (3)
        }
    }
}

private fun firstView() = verticalLayout {
    html("<h1>Kaadin</h1>")
    label("Kaadin is a library to create Vaadin Graphical User Interface using a Kotlin DSL.")
    button("Navigate to second view", onClick = { ui().navigator.navigateTo("second") }) (4)
}

private fun secondView() = verticalLayout {
    label("This is the second view, as an example.")
    button("Navigate back to first view", onClick = { ui().navigator.navigateTo("") }) (5)
}
1 Configure the navigator with a placeholder layout. This also sets the layout as the content of the UI
2 Configure the default view in the navigator
3 Configure an additional view in the navigator
4 Navigate to the second view using the navigator’s API on the click of the button
5 Navigate to the default view

3.3. Grid

3.3.1. Data source

There are several ways to declare a grid with a data source.

  • The most direct way is to use the grid() function with the dataSource parameter:

    import ch.frankel.kaadin.*
    import com.vaadin.data.util.*
    import com.vaadin.server.*
    import com.vaadin.ui.*
    
    class SimpleGridUI() : UI() {
        val data = BeanItemContainer<Person>(Person::class.java, Person.all)
        override fun init(request: VaadinRequest) {
            grid(dataSource = data)
        }
    }
  • Alternatively, the container can be declared within a nested beanItemContainer() function:

    import ch.frankel.kaadin.*
    import com.vaadin.data.util.*
    import com.vaadin.server.*
    import com.vaadin.ui.*
    
    class GridNestedContainerUI() : UI() {
        override fun init(request: VaadinRequest) {
            grid {
                beanItemContainer(Person::class.java, Person.all)
            }
        }
    }
  • Finally, beans can be added either individually or from a collection:

    import ch.frankel.kaadin.*
    import com.vaadin.data.util.*
    import com.vaadin.server.*
    import com.vaadin.ui.*
    
    class GridWitBeansUI() : UI() {
        override fun init(request: VaadinRequest) {
            grid {
                beanItemContainer(Person::class.java) {
                    Person.single (1)
                    Person.all (2)
                }
            }
        }
    }
1 Add a single element to the container
2 Add a collection to the container

3.3.2. Columns

To get a hold of a column, use the column() function with the desired bean property name as the parameter. Once a column has been referenced, it can be managed in the lambda block:

class GridColumnLambdaUI() : UI() {
    val data = BeanItemContainer<Person>(Person::class.java, Person.all)
    override fun init(request: VaadinRequest) {
        grid(dataSource = data) {
            column("firstName") {
                expandRatio = 20
                setLastFrozenColumn()
            }
        }
    }
}

Shortcut are availabe for existing renderers:

grid(dataSource = data) {
    column("firstName").buttonRenderer { println(it) }
}

And for existing converters as well:

grid(dataSource = data) {
    column("birthDate").dateConverter()
}

If no existing converter meets the requirements, Kaadin allows to pass the conversion function as a lambda:

val dateFormat = "dd.MM.yyyy"
grid(dataSource = data) {
    column("birthDate").converter(
            Date::class.java, (1)
            String::class.java, (2)
            { date -> SimpleDateFormat(dateFormat).format(date) } (3)
    )
}
1 Source type
2 Target type
3 Conversion function

4. Contributing

Kaadin is very much a work in progress. Want to help? Here’s how you can contribute:

  • Use it!

  • Open issues

  • Open pull requests, to improve either the code or the documentation (or both!)

  • Spread the word about #Kaadin

5. License

Kaadin is distributed under the friendly Apache 2.0 License.