### Initial Welcome View Source: https://www.vaadinonkotlin.eu/tutorial The starting view of the application before adding interactive elements. It sets up the basic layout and a static welcome heading. ```kotlin package com.example.vok import com.github.mvysny.karibudsl.v10.* import com.vaadin.flow.router.Route @Route("") class WelcomeView : KComposite() { private val root = ui { verticalLayout { setSizeFull(); content { align(center, middle) }; isMargin = false; isSpacing = true h1("Welcome to BoltShop") } } } ``` -------------------------------- ### Basic Form Layout with Vaadin Components (Java Example) Source: https://www.vaadinonkotlin.eu/creating_ui This example demonstrates creating a `FormLayout` and adding standard Vaadin form components like `TextField`, `Checkbox`, and `DatePicker` in plain Java. This serves as a reference for the Kotlin DSL equivalent. ```java FormLayout layout = new FormLayout(); TextField nameField = new TextField("Name:"); nameField.setValue("Donald Knuth"); layout.add(nameField); layout.add(new Checkbox("Internal employee")); layout.add(new DatePicker("Date of birth:")); ``` -------------------------------- ### GET /api/products Endpoint Source: https://www.vaadinonkotlin.eu/tutorial This snippet defines the GET endpoint for retrieving all products. It uses ktorm-vaadin's findAll and maps entities to DTOs. ```kotlin get("/api/products") { ctx -> ctx.json(Products.findAll().map { it.toDto() }) } ``` -------------------------------- ### ButtonBar extending KComposite Source: https://www.vaadinonkotlin.eu/creating_ui This example demonstrates the KComposite pattern where the UI structure is explicitly defined within the `ui {}` block. The root layout is clearly visible, making the component's hierarchy easy to understand at a glance. ```kotlin class ButtonBar : KComposite() { val root = ui { horizontalLayout { button("ok") } } } ``` -------------------------------- ### Implement Vaadin Service Init Listener for Access Control Source: https://www.vaadinonkotlin.eu/security Install a `SimpleNavigationAccessControl` as a Vaadin `BeforeEnterListener` by creating a `VaadinServiceInitListener`. This listener intercepts navigation events and enforces route annotations. ```kotlin class AppServiceInitListener : VaadinServiceInitListener { private val accessControl = SimpleNavigationAccessControl.usingService { Session.loginService } init { accessControl.setLoginView(LoginRoute::class.java) } override fun serviceInit(e: ServiceInitEvent) { e.source.addUIInitListener { uiInitEvent -> uiInitEvent.ui.addBeforeEnterListener(accessControl) } } } ``` -------------------------------- ### Embedded Jetty Startup Message Source: https://www.vaadinonkotlin.eu/tutorial This message indicates that the embedded Jetty server has started successfully and is ready to serve the application. It shows the URL where the application is accessible. ```text ================================================= Embedded Jetty started successfully on http://localhost:8080 ================================================= ``` -------------------------------- ### GET /api/products/{sku} Endpoint Source: https://www.vaadinonkotlin.eu/tutorial This snippet defines the GET endpoint for retrieving a single product by SKU. It uses path parameters and ktorm's find operation within a db block. ```kotlin get("/api/products/{sku}") { ctx -> val sku = ctx.pathParam("sku") val product = db { database.sequenceOf(Products).find { it.sku eq sku } } ?: throw NotFoundResponse("No product with SKU '$sku'") ctx.json(product.toDto()) } ``` -------------------------------- ### Minimal Vaadin Grid with ktorm-vaadin DataProvider Source: https://www.vaadinonkotlin.eu/databases A basic example of a Vaadin Grid displaying Person entities. It utilizes ktorm-vaadin's `dataProvider` for lazy loading and SQL sorting. Includes custom column renderers and actions. ```kotlin import com.vaadin.flow.component.button.NativeButton import com.vaadin.flow.component.grid.Grid import com.vaadin.flow.component.orderedlayout.VerticalLayout import com.vaadin.flow.router.Route import org.ktorm.entity.delete import org.ktorm.entity.forEach import org.ktorm.entity.sequence @Route("") class PersonListView : VerticalLayout() { init { setSizeFull() grid(Persons.dataProvider) { setSizeFull() columnFor(Person::id) columnFor(Person::name) columnFor(Person::age) columnFor(Person::dateOfBirth, converter = { it?.toString() }) columnFor(Person::maritalStatus) columnFor(Person::alive) columnFor(Person::created, converter = { it?.toString() }) addColumn(NativeButtonRenderer("Delete") { person -> person.delete() this@grid.refresh() }) } } } ``` -------------------------------- ### Japanese Localization Bundle Source: https://www.vaadinonkotlin.eu/i18n Create language-specific resource bundles, like `VokMessages_ja.properties`, to provide translations for particular locales. This example shows Japanese translations. ```properties newuser.name=名 newuser.surname=姓 ``` -------------------------------- ### Get or Create JVM Singleton Service Source: https://www.vaadinonkotlin.eu/services Use this code to access or create a single instance of a service throughout the application's runtime. This approach allows for providing different implementations, especially for testing. ```kotlin val Services.yourService: YourService get() = singletons.getOrCreate(YourService::class) { YourService() } ``` -------------------------------- ### Get Product by SKU Source: https://www.vaadinonkotlin.eu/tutorial Retrieves a single product by its Stock Keeping Unit (SKU). ```APIDOC ## GET /api/products/{sku} ### Description Retrieves one product by its natural key (SKU). ### Method GET ### Endpoint /api/products/{sku} ### Parameters #### Path Parameters - **sku** (string) - Required - The SKU of the product to retrieve. ### Response #### Success Response (200) - product (object) - The product object matching the provided SKU. ``` -------------------------------- ### Define an Empty Services Object Source: https://www.vaadinonkotlin.eu/services This is a simple singleton object that acts as a container for service extensions. It requires no setup beyond its declaration. ```kotlin object Services ``` -------------------------------- ### Referencing TextFields in a View Source: https://www.vaadinonkotlin.eu/creating_ui Capture component references to access their values, for example, within a button click handler. This example demonstrates creating a simple view with text fields for name and age. ```kotlin package com.example.vok import com.github.mvysny.karibudsl.v10.* import com.vaadin.flow.component.notification.Notification import com.vaadin.flow.component.orderedlayout.VerticalLayout import com.vaadin.flow.component.textfield.TextField import com.vaadin.flow.router.Route @Route("") class WelcomeView : VerticalLayout() { private lateinit var nameField: TextField private lateinit var ageField: TextField init { formLayout { nameField = textField("Name:") ageField = textField("Age:") } button("Click me") { onClick { Notification.show("Hello, ${nameField.value} of age ${ageField.value}") } } } } ``` -------------------------------- ### Get All Products Source: https://www.vaadinonkotlin.eu/tutorial Retrieves a list of all products. It fetches all records from the Product table and returns them as a JSON array of ProductDto objects. ```APIDOC ## GET /api/products ### Description Retrieves a list of all products. It fetches all records from the Product table and returns them as a JSON array of ProductDto objects. ### Method GET ### Endpoint /api/products ### Response #### Success Response (200) - **Array** - A JSON array of product objects. #### Response Example ```json [ { "sku": "string", "name": "string", "price": 0.0 } ] ``` ``` -------------------------------- ### Using Session Login Service in UI Event Listener Source: https://www.vaadinonkotlin.eu/services An example of how to use the `Session.loginService` extension property within a Vaadin `LoginForm`'s login listener to authenticate a user and log them in. ```kotlin loginForm.addLoginListener { e -> val user = User.findByUsername(e.username) if (user == null || !user.passwordMatches(e.password)) { loginForm.setErrorMessage("Login failed", "Invalid username or password") } else { Session.loginService.login(user) } } ``` -------------------------------- ### ButtonBar extending HorizontalLayout Source: https://www.vaadinonkotlin.eu/creating_ui This example shows a component directly extending a layout class. The UI structure is less explicit, requiring inspection of the constructor or init block to understand the component's layout hierarchy. ```kotlin class ButtonBar : HorizontalLayout() { init { button("ok") } } ``` -------------------------------- ### Get Product by SKU Source: https://www.vaadinonkotlin.eu/tutorial Retrieves a single product by its Stock Keeping Unit (SKU). It queries the database for a product matching the provided SKU and returns its DTO. If no product is found, it returns a 404 error. ```APIDOC ## GET /api/products/{sku} ### Description Retrieves a single product by its Stock Keeping Unit (SKU). It queries the database for a product matching the provided SKU and returns its DTO. If no product is found, it returns a 404 error. ### Method GET ### Endpoint /api/products/{sku} ### Parameters #### Path Parameters - **sku** (string) - Required - The Stock Keeping Unit of the product to retrieve. ### Response #### Success Response (200) - **object** - A JSON object representing the product. #### Response Example ```json { "sku": "string", "name": "string", "price": 0.0 } ``` #### Error Response (404) - **message** (string) - Description of the error (e.g., "No product with SKU '...'") ``` -------------------------------- ### Creating a Reusable Form Component Source: https://www.vaadinonkotlin.eu/creating_ui Define a class extending a layout to create a reusable component. This example shows a `NameAgeForm` class with text fields and a greeting property. ```kotlin class NameAgeForm : FormLayout() { private val nameField = textField("Name:") private val ageField = textField("Age:") val greeting: String get() = "Hello, ${nameField.value} of age ${ageField.value}" } ``` -------------------------------- ### Annotating Product Entity Properties Source: https://www.vaadinonkotlin.eu/tutorial This snippet shows the Product interface with various Jakarta Bean Validation annotations applied to its properties. Ensure you use the `@get:` site target for these annotations to be recognized by the validation framework. ```kotlin package com.example.vok import com.github.mvysny.ktormvaadin.ActiveEntity import jakarta.validation.constraints.NotNull import jakarta.validation.constraints.Pattern import jakarta.validation.constraints.Positive import jakarta.validation.constraints.PositiveOrZero import jakarta.validation.constraints.Size import org.ktorm.entity.Entity import org.ktorm.schema.Column import org.ktorm.schema.Table import org.ktorm.schema.decimal import org.ktorm.schema.enum import org.ktorm.schema.int import org.ktorm.schema.long import org.ktorm.schema.varchar import java.math.BigDecimal enum class Category { Tools, Fasteners, Plumbing, Electrical, Paint, Garden } enum class UnitOfMeasure { Each, Box, Meter, Kilogram } interface Product : ActiveEntity { var id: Long? @get:NotNull @get:Size(min = 1, max = 40) @get:Pattern( regexp = "[A-Z0-9-]+", message = "SKU may only contain uppercase letters, digits and hyphens" ) var sku: String? @get:NotNull @get:Size(min = 1, max = 200) var name: String? @get:NotNull var category: Category? @get:NotNull @get:Positive(message = "Price must be greater than zero") var price: BigDecimal? @get:NotNull @get:PositiveOrZero(message = "Stock cannot be negative") var stock: Int? @get:NotNull var unit: UnitOfMeasure? override val table: Table get() = Products companion object : Entity.Factory() } // Products table object unchanged from Chapter 3 ``` -------------------------------- ### Application Startup Log Source: https://www.vaadinonkotlin.eu/tutorial Observe the application startup log to confirm initialization, database migrations, and server status. ```text Hikari ... Start completed. Vaadin On Kotlin initialized Running DB migrations Flyway ... Migrating schema "PUBLIC" to version "1 - create product" Flyway ... Successfully applied 1 migration to schema "PUBLIC", now at version v1 Initialization complete Embedded Jetty started successfully on http://localhost:8080 ``` -------------------------------- ### Create a Welcome View with a Button Source: https://www.vaadinonkotlin.eu/creating_ui This snippet demonstrates creating a basic view with a single button that shows a notification when clicked. It uses the `VerticalLayout` as the root and a DSL builder for the button. ```kotlin package com.example.vok import com.github.mvysny.karibudsl.v10.* import com.vaadin.flow.component.notification.Notification import com.vaadin.flow.component.orderedlayout.VerticalLayout import com.vaadin.flow.router.Route @Route("") class WelcomeView : VerticalLayout() { init { button("Click me") { onClick { Notification.show("Clicked") } } } } ``` -------------------------------- ### Run Gradle Application Source: https://www.vaadinonkotlin.eu/tutorial Execute the Gradle wrapper to run the application. Ensure the server is stopped before restarting. ```bash $ ./gradlew run ``` -------------------------------- ### Clone Vaadin on Kotlin Starter Project Source: https://www.vaadinonkotlin.eu/tutorial Clone the official starter repository to begin your Vaadin on Kotlin project. This command fetches the project files and sets up the basic structure. ```bash $ git clone https://github.com/mvysny/vok-helloworld-app $ cd vok-helloworld-app ``` -------------------------------- ### Retrieve a product by SKU via API Source: https://www.vaadinonkotlin.eu/tutorial Fetch a specific product using its SKU. A `404 Not Found` response is returned if the SKU does not exist. ```bash $ curl -s http://localhost:8080/api/products/HX-M6-40 {"id":1,"sku":"HX-M6-40","name":"Hex bolt M6×40mm, zinc-plated","category":"Fasteners","price":0.35,"stock":120,"unit":"Each"} $ curl -i http://localhost:8080/api/products/NOPE HTTP/1.1 404 Not Found Content-Type: application/json ... No product with SKU 'NOPE' ``` -------------------------------- ### List all products via API Source: https://www.vaadinonkotlin.eu/tutorial Use `curl` to fetch a list of all products from the API. The output is pretty-printed using `python3 -m json.tool` for readability. ```bash $ curl -s http://localhost:8080/api/products | python3 -m json.tool [ { "id": 1, "sku": "HX-M6-40", "name": "Hex bolt M6×40mm, zinc-plated", "category": "Fasteners", "price": 0.35, "stock": 120, "unit": "Each" }, { "id": 2, "sku": "HX-M8-50", ... }, ... ] ``` -------------------------------- ### Creating RouterLinks with Different Overloads Source: https://www.vaadinonkotlin.eu/navigating Demonstrates the various ways to create `RouterLink` instances using the `routerLink` DSL extension. Use these for declarative in-app links that avoid full page reloads. ```kotlin import com.github.mvysny.karibudsl.v10.routerLink import com.vaadin.flow.component.icon.VaadinIcon import com.vaadin.flow.router.HighlightConditions // no parameter routerLink(VaadinIcon.LIST, "Reviews", ReviewsList::class) { addClassName("main-layout__nav-item") highlightCondition = HighlightConditions.sameLocation() } // single typed URL parameter (HasUrlParameter) routerLink(text = "Edit", viewType = PersonView::class, parameter = person.id!!) // named RouteParameters routerLink(text = "Open", viewType = AdminView::class, parameters = mapOf("id" to "25")) // no target — useful when the destination is set later, e.g. in HasUrlParameter.setParameter() routerLink(text = "Loading…") { /* set route later via setRoute(...) */ } ``` -------------------------------- ### Bean Validation Annotations on Entity Getters Source: https://www.vaadinonkotlin.eu/forms Annotate entity getters with Jakarta Bean Validation annotations. The `@get:` site target is crucial for interfaces. ```kotlin import com.github.mvysny.ktormvaadin.ActiveEntity import jakarta.validation.constraints.Max import jakarta.validation.constraints.Min import jakarta.validation.constraints.NotNull import jakarta.validation.constraints.Size import org.ktorm.entity.Entity import org.ktorm.schema.Table interface Person : ActiveEntity { var id: Long? @get:NotNull @get:Size(min = 1, max = 200) var name: String? @get:NotNull @get:Min(15) @get:Max(100) var age: Int? // ... override val table: Table get() = Persons companion object : Entity.Factory() } ``` -------------------------------- ### Vaadin on Kotlin Project Structure Overview Source: https://www.vaadinonkotlin.eu/tutorial This file structure outlines the core components of a minimal Vaadin on Kotlin application, including startup scripts, main application file, utilities, and the initial view. ```text src/main/kotlin/com/example/vok/ ├── Bootstrap.kt — startup: datasource, VoK init, Flyway, REST scaffold ├── Main.kt — the main() that boots Vaadin Boot ├── Utils.kt — small helpers └── WelcomeView.kt — the @Route("") landing page ``` -------------------------------- ### Responsive Form Layout Steps Source: https://www.vaadinonkotlin.eu/forms Configures responsive behavior for a FormLayout using karibu-dsl's condensed DSL for responsive steps. Allows defining column counts and label positions based on viewport width. ```kotlin formLayout { responsiveSteps { responsiveSteps {"0px"(1, top); "30em"(2, aside)} textField("Name") textField("Age") } } ``` -------------------------------- ### Create a new product via API Source: https://www.vaadinonkotlin.eu/tutorial Use a `POST` request with JSON data to create a new product. A `201 Created` status is returned on success. ```bash $ curl -i -X POST http://localhost:8080/api/products \ -H 'Content-Type: application/json' \ -d '{ "sku": "HX-M10-60", "name": "Hex bolt M10x60mm", "category": "Fasteners", "price": 1.20, "stock": 50, "unit": "Each" }' HTTP/1.1 201 Created Content-Type: application/json ... {"id":11,"sku":"HX-M10-60","name":"Hex bolt M10x60mm","category":"Fasteners","price":1.20,"stock":50,"unit":"Each"} ``` -------------------------------- ### Initialize VaadinOnKotlin and Flyway Migration Source: https://www.vaadinonkotlin.eu/tutorial This snippet initializes the VaadinOnKotlin framework and its data source, then configures and runs Flyway migrations. Ensure Flyway is configured with the correct data source to apply SQL migration scripts. ```kotlin VaadinOnKotlin.dataSource = HikariDataSource(cfg) VaadinOnKotlin.init() val flyway = Flyway.configure() .dataSource(VaadinOnKotlin.dataSource) .load() flyway.migrate() ``` -------------------------------- ### Add New Product via Dialog Source: https://www.vaadinonkotlin.eu/tutorial Opens a dialog to add a new product, fills in the form fields, and creates the product. Verifies the notification and the increased row count in the grid. ```kotlin _get