Git Product home page Git Product logo

vaadin-on-kotlin's Introduction

Powered By Vaadin on Kotlin Join the chat at https://gitter.im/vaadin/vaadin-on-kotlin GitHub tag Maven Central Build Status

Welcome to Vaadin-On-Kotlin

Warning: VoK 0.17 is a complete overhaul and the documentation is out-of-date. While the documentation is updated, please consult example apps for more details.

Vaadin-on-Kotlin is a web-application framework that includes everything needed to create database-backed web applications. Please see the official documentation at www.vaadinonkotlin.eu.

Vaadin-on-Kotlin does not enforce you to use Model-View-Controller (MVC), Dependency Injection (DI) nor Service-Oriented Architecture (SOA). It by default does not use Spring nor JavaEE. Instead, Vaadin-on-Kotlin focuses on simplicity.

The View layer leverages component-oriented programming as offered by the Vaadin framework. Vaadin offers powerful components which are built on AJAX; programming in Vaadin resembles programming in a traditional client-side framework such as JavaFX or Swing.

The database access layer is covered by the vok-orm library. vok-orm allows you to present the data from database rows as objects and embellish these data objects with business logic methods. Using vok-orm is the recommended approach to access SQL databases. Of course, you may decide not to use vok-orm and integrate with NoSQL instead, or use JPA and/or Hibernate.

Everything is combined with the conciseness of the Kotlin programming language, which makes Vaadin-on-Kotlin a perfect starting point for beginner programmers. And Kotlin is statically-typed, so you can always Ctrl+Click on a code and learn how it works under the hood!

For a Getting Started guide please see the official documentation at www.vaadinonkotlin.eu/.

Getting Started

  1. Please install Java 17 JDK and git client if you haven't yet.

  2. Then, at the command prompt, just type in:

    git clone https://github.com/mvysny/vok-helloworld-app
    cd vok-helloworld-app
    ./gradlew clean build web:appRun
  3. Using a browser, go to http://localhost:8080 and you'll see: "Yay! You're on Vaadin-on-Kotlin!"

  4. Follow the guidelines to start developing your application. You may find the following resources handy:

  5. For easy development, we encourage you to edit the project sources in Intellij IDEA; the Community Edition is enough.

Example project

A more polished example application which you can inspire from. Just type this into your terminal:

git clone https://github.com/mvysny/vaadin-on-kotlin
cd vaadin-on-kotlin
./gradlew vok-example-crud:run

The web app will be running at http://localhost:8080.

For more information check out the vok-example-crud module.

Vaadin Example project

Head to Beverage Buddy VoK for the standalone example project.

Run the example application from Intellij IDEA Community

  1. In Intellij IDEA, open the project simply by opening the build.gradle file, and then selecting "Open as Project".
  2. To run the application from IDEA, just open Gradle tab, select vok-example-crud-vokdb / Tasks / gretty / appRun, right-click and select Debug. The web app will be running at http://localhost:8080.

If you have the Intellij IDEA Ultimate version, we recommend you to use Tomcat for development, since it offers better code hot-redeployment:

  1. Open the project in IDEA
  2. Launch the vok-example-crud-vokdb WAR in Tomcat as described here: https://kotlinlang.org/docs/tutorials/httpservlets.html

Contributing

We encourage you to contribute to Vaadin-on-Kotlin! Join us and discuss at Vaadin Forums: Miscellaneous.

Trying to report a possible security vulnerability in Vaadin-on-Kotlin? Please use Vaadin Bug Tracker.

For general Vaadin-on-Kotlin bugs, please use the Vaadin-on-Kotlin Github Issue Tracker.

Modules

Vaadin-on-Kotlin consists of several modules which provides you with handy functionality. To include the modules into your project, you simply add appropriate Gradle jar dependencies to your build.gradle.

Every module contains a description of what exactly the module does, when you should use it and when it might be better to use something else.

The list of modules:

  • vok-framework - the very core of Vaadin-on-Kotlin which contains machinery for developing VoK plugins, and also the means to bootstrap/teardown the VoK runtime. Always included in your project when you build your app with VoK.
  • vok-util-vaadin - when you want to have additional support for Vaadin. You typically include this module when you build your Vaadin-based app with VoK.
  • vok-framework-vokdb - when you want to have additional support for Vaadin and the support for the database using the recommended approach. Includes vok-util-vaadin and vok-db.
  • vok-rest - when you want to expose data from your VoK app to other REST-consuming clients.
  • vok-rest-client - when you want to consume data in your VoK app from other REST servers.
  • vok-db - Provides access to the database; uses VoK-ORM
  • vok-security - provides basic security support. The documentation there explains the basics and provides links to sample projects.

Code Examples

Easy database transactions:

vok-orm:

button("Save", { db { person.save() } })

See vok-orm for an explanation on how this works.

Prepare your database

Simply use Flyway: write Flyway scripts, add a Gradle dependency:

compile 'org.flywaydb:flyway-core:7.1.1'

and introduce a context listener, to auto-update your database to the newest version before your app starts:

@WebListener
class Bootstrap: ServletContextListener {
    override fun contextInitialized(sce: ServletContextEvent?) {
        VaadinOnKotlin.init()
        val flyway = Flyway()
        flyway.dataSource = VaadinOnKotlin.getDataSource()
        flyway.migrate()
    }
}

Please scroll below for more details.

Defining UI DSL-style

verticalLayout {
  formLayout {
    isSpacing = true
    textField("Name:") {
      focus()
    }
    textField("Age:")
  }
  horizontalLayout {
    w = 100.perc
    isSpacing = true
    button("Save") {
      onClick { okPressed() }
      setPrimary()
    }
  }
}

Simple popups

popupView("Details") {
  verticalLayout {
    formLayout { ... }
    button("Close", { isPopupVisible = false })
  }
}

vok-orm-based grid is a breeze

Support for sorting and filtering out-of-the-box:

grid<User>(dataProvider = Person.dataProvider) {
  isExpand = true
  
  val filterBar = appendHeaderRow().asFilterBar(this)

  columnFor(User::id) {
      filterBar.forField(NumberRangePopup(), this).inRange()
  }
  columnFor(User::username) {
      filterBar.forField(TextField(), this).ilike()
  }
  columnFor(User::roles) {
      filterBar.forField(TextField(), this).ilike()
  }
  columnFor(User::hashedPassword)
  addButtonColumn(VaadinIcon.EDIT, "edit", { createOrEditUser(it) }) {}
  addButtonColumn(VaadinIcon.TRASH, "delete", { it.delete(); refresh() }) {}
}

Advanced syntax

Keyboard shortcuts via operator overloading

import com.github.mvysny.karibudsl.v8.ModifierKey.Alt
import com.github.mvysny.karibudsl.v8.ModifierKey.Ctrl
import com.vaadin.event.ShortcutAction.KeyCode.C

button("Create New Person (Ctrl+Alt+C)") {
  onClick { ... }
  clickShortcut = Ctrl + Alt + C
}

Width/height

button {
  icon = ...
  w = 48.px
  h = 50.perc
}
if (button.w.isFillParent) { ... }

Further Links

License

Licensed under the MIT License.

Copyright (c) 2017-2018 Martin Vysny

All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

vaadin-on-kotlin's People

Contributors

dependabot[bot] avatar jhult avatar mstahv avatar mvysny avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vaadin-on-kotlin's Issues

When editing an updating Grid, I get exception..

I've been trying to figure out, what is wrong in my code. When I go to edit some of the data in grid, I get this exception..

10:03:03.833 [qtp279680875-909] ERROR c.vaadin.server.DefaultErrorHandler - 
java.lang.IllegalStateException: Duplicate key Person(firstName=matti222, lastName=meikalainen22, sotu=111111)
        at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
        at java.util.HashMap.merge(HashMap.java:1245)
        at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
        at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
        at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
        at java.util.HashMap$KeySpliterator.forEachRemaining(HashMap.java:1540)
        at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
        at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
        at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
        at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
        at com.vaadin.data.provider.DataCommunicator$ActiveDataHandler.getActiveData(DataCommunicator.java:165)
        at com.vaadin.data.provider.DataCommunicator.refresh(DataCommunicator.java:521)
        at com.vaadin.ui.AbstractListing$AbstractListingExtension.refresh(AbstractListing.java:122)
        at com.vaadin.ui.components.grid.EditorImpl.save(EditorImpl.java:250)
        at com.vaadin.ui.components.grid.EditorImpl$1.save(EditorImpl.java:133)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:155)
        at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:116)
        at com.vaadin.server.communication.ServerRpcHandler.handleInvocation(ServerRpcHandler.java:445)
        at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:410)
        at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:274)
        at com.vaadin.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:90)
        at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:41)
        at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1577)
        at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:381)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
        at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:812)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1669)
        at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:224)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
        at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:585)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
        at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:577)
        at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223)
        at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1127)
        at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515)
        at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
        at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
        at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:215)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
        at org.eclipse.jetty.server.Server.handle(Server.java:499)
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:311)
        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:257)
        at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:544)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
        at java.lang.Thread.run(Thread.java:745)

And here is the code I'm using:

data class Person(var firstName: String, var lastName: String, var sotu: String)


/**
 * Person View
 */


@AutoView("")
class TestView: VerticalLayout(), View {


    private lateinit  var personGrid: Grid<Person>
    private lateinit  var firstNameFilter: TextField
    var persons = listOf<Person>(Person("matti", "meikalainen", "111111"), Person("teppo", "testaajaa", "2222222"))

    companion object {
        fun navigateTo() = navigateToView<WelcomeView>()
    }

    init {

        var personsListProvider = ListDataProvider<Person>(persons).withConfigurableFilter()


        isMargin = false
        label("Vaadin On Kotlin testview") {
            w = fillParent
            addStyleNames(ValoTheme.LABEL_H1, ValoTheme.LABEL_COLORED)
        }


        personGrid = grid(Person::class, "List of Persons", dataProvider = personsListProvider) {
            expandRatio = 1f;
            setSizeFull()

            val binder: Binder<Person> = getEditor().getBinder()

            column(Person::firstName) {
                caption="firstname"
                setEditorComponent(TextField("firstname"), Person::firstName.setter)
               isEditable = true
                isHidden = false
            }




            column(Person::lastName) {
                setEditorComponent(TextField("lastname"), Person::lastName.setter)
                isEditable = true
            }

            column(Person::sotu) {
                setEditorComponent(TextField("sotu"), Person::sotu.setter)
                isEditable = true

            }

            editor.addCancelListener( { event -> Notification.show("Editing Cancelled...")
            } )
            editor.addSaveListener({ event ->
                Notification.show("Saving ${event.bean.firstName} - ${event.bean.lastName} - ${event.bean.sotu} -...")
                binder.writeBean(event.bean)

                personsListProvider.refreshAll()

                // just for debugging to see what's in the provider now..
                personsListProvider.getAll().forEach(
                        {
                            println("${it.firstName} - ${it.lastName} - ${it.sotu}")
                        }
                )

                refresh()

            })

            editor.setSaveCaption("Tallenna")
            editor.setCancelCaption("Peruuta")

            editor.setEnabled(true)

            addColumn({ "Show" }, ButtonRenderer<Person>({ event -> personGrid.refresh() }))
            addColumn({ "Edit" }, ButtonRenderer<Person>({ event -> null }))
            addColumn({ "Delete" }, ButtonRenderer<Person>({ event -> null }))

            // automatically create filters, based on the types of values present in particular columns.
            appendHeaderRow().generateFilterComponents(this, Person::class)


        }

        personGrid.addItemClickListener({ event -> Notification.show("Value: " + event.getItem()) })

    }


    override fun enter(event: ViewChangeListener.ViewChangeEvent?) {
        personGrid.dataProvider.refreshAll()
    }

}

Even if I leave the whole ListDataProvider out of it, I still get the same duplicate key error.
So I'm starting to wonder, is this a bug in Vaadin, or am I just doing something wrong? Is the problem with ListDataProvider, or with the Binder? I'm just not getting it how to use this properly through the docs..

vok-example-flow-sql2o - Build failed

Hello,

It's not really a bug, I tried to build and run vok-example-flow-sql2o. It's not working.

I did this:

git clone https://github.com/mvysny/vaadin-on-kotlin
cd vaadin-on-kotlin
./gradlew vok-example-crud-sql2o:appRun

--> vok-example-crud-sql2o is working

then this:

./gradlew vok-example-flow-sql2o:appRun

And I got this:

18:24:13.895 ERROR [Bower] Install failed: ENOTEMPTY: Error: ENOTEMPTY:/home/jgueriaud/.cache/bower/packages/57f7f0864b724db288d6fd3200db7f1d/2.0.0
at /home/jgueriaud/dev_kotlin/vaadin-on-kotlin/vok-example-flow-sql2o/build/webResource/build.js:1347
at _fulfilled (/home/jgueriaud/dev_kotlin/vaadin-on-kotlin/vok-example-flow-sql2o/build/webResource/build.js:1346)
at /home/jgueriaud/dev_kotlin/vaadin-on-kotlin/vok-example-flow-sql2o/build/webResource/build.js:1346
at /home/jgueriaud/dev_kotlin/vaadin-on-kotlin/vok-example-flow-sql2o/build/webResource/build.js:1346
at /home/jgueriaud/dev_kotlin/vaadin-on-kotlin/vok-example-flow-sql2o/build/webResource/build.js:1346
at runSingle (/home/jgueriaud/dev_kotlin/vaadin-on-kotlin/vok-example-flow-sql2o/build/webResource/build.js:1346)
at flush (/home/jgueriaud/dev_kotlin/vaadin-on-kotlin/vok-example-flow-sql2o/build/webResource/build.js:1346)
at processImmediate (timers.js:345)

There is no kotlin problem but there is a problem with bower

  • Exception is:
    org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':vok-example-flow-sql2o:webResourceInstallBowerDependencies'.

Do you have the same error ? (and resolve it ? )

Thanks,

How to Define the grid label

@mvysny ,hi, how to add custom grid column label?
in your example ,i can define a column by addColumnFor(Person::name),and this column label is Name.
if i want to redefine the label to "Person Name",how to do this,thank you very much !

Question / Enhancement

Could you suggest a path to take to implement 'expanding' menu on left nav ?
I don't have tight requirements, the use case is that I'm getting to many top level menu entries so would like to implement the 'sections' as a kind of accordion or 'expando' or something similar.
I dont want to write it from scratch as I suspect theres 'hidden magic' attempting to get the styles right.

Breadcrumbs as a stretch request :)

navigateTo + ViewChangeEvent.parameterList may double URL decode

If I use parameters that require URL encoding in Navigator.navigateToView(), a subsequecent ViewChangeEvent.parameterList may double -URL decode depending on if this is a direct in-app navigation or if it is the result of browser-side request (refresh, bookmark, manual enter).

Example:

navigateToView( DocstoreTreeView::class.java , item.url )

where item.url is a string in URL format such as https://somewhere/over/the/rainbow

the corresponding enter events parameterList may be either

[ "0" -> "https://somewhere/over/the/rainbow"]
Or
[ "0" -> "https" , "1" -> "somewhere/over/the/rainbow" ]

I tracked this down to largely correct (*) code that encodes on navigate and decodes on enter,
the cause seems to be Vaadin code which in the direct case supplies the ENCODED string "parameters" but in the refresh/manual case supplies the DECODED string. Which then vok decodes again.

In many cases this doesn't matter, in the above case there is 1 bug from the extra decode and 1 bug from vok implementation

  1. the double-decode causes the "/" to be interpreted as a seperator so you get a split string
  2. intentional code in parameterList removes the empty argument caused by ("//")
  3. an encoded % escape would be interpreted differently

#1 might be able to work around if #2 were not an issue. (#2 cannot determine between "/" or "//" being stripped)

Suggest that at minimal the code in parameterList be changed to NOT remove empty parameters, this would allow a workaround of the vaadin issue (not able to determine core cause yet)

Remove JPA

This talk is hilarious: https://vimeo.com/28885655 and helped to soothe my anger a lot while fighting with Hibernate's

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.vok.Article.comments, could not initialize proxy - no Session
	at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:582)
	at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:201)
	at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:561)
	at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:132)
	at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:277)
	at kotlin.collections.CollectionsKt___CollectionsKt.joinTo(_Collections.kt:2013)
	at kotlin.collections.CollectionsKt___CollectionsKt.joinToString(_Collections.kt:2031)
	at kotlin.collections.CollectionsKt___CollectionsKt.joinToString$default(_Collections.kt:2030)
	at com.example.vok.ArticleView$refreshComments$1.invoke(ArticleView.kt:71)
	at com.example.vok.ArticleView$refreshComments$1.invoke(ArticleView.kt:16)
	at com.github.vok.framework.DBKt.db(DB.kt:146)

As it turns out, I had a detached article and there is no way of reattaching it back to the session! How dumb is that? https://stackoverflow.com/questions/912659/what-is-the-proper-way-to-re-attach-detached-objects-in-hibernate/4438358#4438358

So instead of article.comments.forEach {...} you need to write Article.find(article.id!!)!!.comments.forEach {...} to force Hibernate to reload the entity and make the bloody comments collection attached. Smooth ๐Ÿ‘Ž

So there goes Hibernate. I kinda liked Ebean until I realized it needs to do some compile-time class enhancement with Maven Tiles - ๐Ÿ‘Ž , sorry, no, I'm not going to install a plugin into my IDE just to develop with Ebean. There goes Ebean.

I remember EclipseLink having troubles with sequences as primary keys generators: I believe EclipseLink started with 51 instead of 1, and then EclipseLink wondered where the hell is record number 1: http://stackoverflow.com/questions/18474046/eclipselink-and-sequence-generator-preallocation ๐Ÿ‘Ž

Anyway, it seems that JPA is hated by the interwebs with glaring passion: https://virgo47.wordpress.com/2014/10/09/jpa-is-it-worth-it-horror-stories-with-eclipselink-and-hibernate/
and https://www.reddit.com/r/java/comments/ln2st/jpa_the_mother_of_all_leaky_abstractions/

JPA - the mother of all leaky abstractions :-D Kinda says it all. There's no way I'm going to touch Spring Data (because it has Spring in it ๐Ÿ‘Ž ), which leaves us either with (yet) another JDBC wrapper library, or https://github.com/JetBrains/Exposed . It looks quite foreign to me, but I'll evaluate.

More blogs from people pissed by Hibernate:

DataProvider and() is confusing since it produces a NEW DataProvider

It's not clear from the and() method name that a new DP is produced by the call. That is also the case of configurableFilter().

Perhaps the data providers offered from the entities should be configurable right from the start. That makes sense since then filter components can directly set filters to those data providers, without having to call the confusing configurableFilter() method. That would also allow us to call setFilter() directly which makes sense.

When a function creates a new instance, it is typically prefixed with with. Hence the configurableFilter() should be renamed to withConfigurableFilter(). Also, the and() should be renamed to withFilter().

With these changes, current use-cases of creating a grid with filtered items are still valid, for example:

personGrid = grid(Person::class, dataProvider = Person.dataProvider) {
  // since dataProvider is configurable, the generateFilterComponents() function will work properly
}

also

personGrid = grid(Person::class, dataProvider = Person.dataProvider.withFilter { Person::age between 20..60 })

or

personGrid = grid(Person::class, dataProvider = Person.dataProvider.apply { setFilter { Person::age between 20..60 }})

However, there is now a problem that the generated filter bar will silently overwrite the initial filter set by the programmer ...

Grid generateFilterComponents() does not reflect later changes of DP

The generateFilterComponents() immediately takes Grid's DP and ignores any further assignments. This is confusing:

  1. If the DP has not been configured yet, the default ListDataProvider is used which will then reject any filter because it's not Vaadin's SerializablePredicate
  2. If the DP is changed afterwards, the filter bar stops working for no apparent reason.

Instead, the generateFilterComponents() should take a Grid instance and read it lazily, when the filters are to be set.

Make FilterFactory class(es) open

It would be nice for any FilterFactory classes to be open so they can be overridden.

Having the actual Filter classes open would also be nice, but they are data classes (for good reason) and thus can't be open.

Originally discussed in #45.

REST CRUD: Add support for filtering, pagination and sorting

Remove Vaadin 8 support

Vaadin 8 is old and barely maintained; moreover there are no reports of VoK being used with Vaadin 8. Since Vaadin 14 is stable and the next LTS is around the corner, I propose to remove the support for Vaadin 8. That means:

  1. Any specific VoK bits for Vaadin 8 will be removed.
  2. Example project will be removed from VoK
  3. Other example projects will cease being maintained.
    However, Karibu-DSL will still support Vaadin 8. Therefore it will still be possible to use Vaadin 8 with Kotlin; only the vok-db+Vaadin8 integration code will be removed.

Generated Filters: immediate

For Vaadin 8 the filters are immediate; for Vaadin 10 the filters are lazy and only applied when the filter component loses focus.

Please unify this behavior and set reasonable defaults:

  • Slow data sources would require lazy filters;
  • Fast data sources are fine with eager filters.

I'd say that let's use eager filters by default since that's the good default for smaller projects; in larger projects the filters can simply be turned lazy (by providing such configuration option in the generateFilterComponents() function).

Add REST-Client module

We should provide an easy way for vok apps to consume REST endpoints published elsewhere. For that I propose to create a vok-rest-client module with the following properties:

  • Avoid annotations if possible.
  • Maximum client simplicity
  • The client should be able to provide CRUD+Gson client API to easy access CRUD endpoints
  • We can even provide vok-orm DataLoader API? But that depends on #31

How to create new Project using VoK framework

Documentation/steps to import the VoK framework in Intenllij for new Project.

Documentation must show the steps to be used to create a new project from the scratch and importing all the files required to start a new project right from the scratch

vok-example-flow-sql2o failing with appRun - Could not find ... org.webjars.bower:vaadin-valo-theme:[2.0.0,3)

Hello. Following the instructions on the README for the project, it's failing on

./gradlew vok-example-flow-sql2o:appRun

Error:

Execution failed for task ':vok-example-flow-sql2o:appRun'.
> Could not resolve all dependencies for configuration ':vok-example-flow-sql2o:compile'.
> Could not find any version that matches org.webjars.bower:vaadin-valo-theme:[2.0.0,3).
 Versions that do not match:
     2.0.0-alpha5
     2.0.0-alpha4
     2.0.0-alpha3
     0.3.2
 Required by:
     project :vok-example-flow-sql2o > project :vok-framework-v10-sql2o > project :vok-util-vaadin10 > com.github.vok.karibudsl:karibu-dsl-v10:0.2.16 > com.vaadin:vaadin:10.0.0.alpha11 > com.vaadin:vaadin-dialog-flow:1.0.0.alpha4 > org.webjars.bower:vaadin-dialog:2.0.0-alpha1
     project :vok-example-flow-sql2o > project :vok-framework-v10-sql2o > project :vok-util-vaadin10 > com.github.vok.karibudsl:karibu-dsl-v10:0.2.16 > com.vaadin:vaadin:10.0.0.alpha11 > com.vaadin:vaadin-combo-box-flow:1.0.0.alpha5 > org.webjars.bower:vaadin-combo-box:3.0.1 > org.webjars.bower:vaadin-overlay:2.0.1

Update to support new DataProvider enhancements in Flow 4.0

There are updates needed to support the new DataProvider improvements that are being done in Vaadin #8054.

Example:

java.lang.IllegalStateException: Grid uses Vaadin built-in ListDataProvider however the filter is VoK's Filter, which is incompatible with Vaadin's ListDataProvider. Please use `grid.setDataLoaderItems()` to use the DataLoader API instead (which is much simpler anyway).
	at eu.vaadinonkotlin.vaadin10.FilterBar.applyFilterToGrid(FilterBar.kt:116) ~[vok-util-vaadin10-master-22926d5677-1.jar:?]
	at eu.vaadinonkotlin.vaadin10.FilterBar.updateFilter(FilterBar.kt:169) ~[vok-util-vaadin10-master-22926d5677-1.jar:?]
	at eu.vaadinonkotlin.vaadin10.FilterBar.access$updateFilter(FilterBar.kt:91) ~[vok-util-vaadin10-master-22926d5677-1.jar:?]
	at eu.vaadinonkotlin.vaadin10.FilterBar$finalizeBinding$reg$1.invoke(FilterBar.kt:348) ~[vok-util-vaadin10-master-22926d5677-1.jar:?]
	at eu.vaadinonkotlin.vaadin10.FilterBar$finalizeBinding$reg$1.invoke(FilterBar.kt:91) ~[vok-util-vaadin10-master-22926d5677-1.jar:?]
	at eu.vaadinonkotlin.vaadin10.FilterBar$Binding$addFilterChangeListener$1.valueChanged(FilterBar.kt:222) ~[vok-util-vaadin10-master-22926d5677-1.jar:?]
	at com.vaadin.flow.component.internal.AbstractFieldSupport.lambda$addValueChangeListener$828eca10$1(AbstractFieldSupport.java:96) ~[flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.component.internal.AbstractFieldSupport$$Lambda$874/0000000000000000.onComponentEvent(Unknown Source) ~[?:?]
	at com.vaadin.flow.component.ComponentEventBus.fireEventForListener(ComponentEventBus.java:205) ~[flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.component.ComponentEventBus.fireEvent(ComponentEventBus.java:194) ~[flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.component.Component.fireEvent(Component.java:378) ~[flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.component.ComponentUtil.fireEvent(ComponentUtil.java:385) ~[flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.component.internal.AbstractFieldSupport.setValue(AbstractFieldSupport.java:207) ~[flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.component.internal.AbstractFieldSupport.setModelValue(AbstractFieldSupport.java:167) ~[flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.component.AbstractField.setModelValue(AbstractField.java:225) ~[flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.component.AbstractSinglePropertyField.handlePropertyChange(AbstractSinglePropertyField.java:352) ~[flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.component.AbstractSinglePropertyField.access$200(AbstractSinglePropertyField.java:48) ~[flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.component.AbstractSinglePropertyField$1.propertyChange(AbstractSinglePropertyField.java:325) ~[flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.internal.nodefeature.ElementPropertyMap.lambda$fireEvent$2(ElementPropertyMap.java:462) ~[flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.internal.nodefeature.ElementPropertyMap$$Lambda$812/0000000000000000.accept(Unknown Source) ~[?:?]
	at java.util.ArrayList.forEach(ArrayList.java:1507) ~[?:?]
	at com.vaadin.flow.internal.nodefeature.ElementPropertyMap.fireEvent(ElementPropertyMap.java:462) ~[flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.internal.nodefeature.ElementPropertyMap.access$100(ElementPropertyMap.java:48) ~[flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.internal.nodefeature.ElementPropertyMap$PutResult.run(ElementPropertyMap.java:169) ~[flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.server.communication.ServerRpcHandler.runMapSyncTask(ServerRpcHandler.java:395) [flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.server.communication.ServerRpcHandler.lambda$handleInvocations$0(ServerRpcHandler.java:389) [flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at com.vaadin.flow.server.communication.ServerRpcHandler$$Lambda$776/0000000000000000.accept(Unknown Source) [flow-server-4.0-SNAPSHOT.jar:4.0-SNAPSHOT]
	at java.util.ArrayList.forEach(ArrayList.java:1507) [?:?]

Extract Vaadin-related bindings to a Vaadin Add-on

  1. Use this opportunity to steal good ideas from Kaadin
  2. Remove Grid filters and use the Grid Filters Add-on as stated in #1 - however don't introduce that dependency into the Add-on unless absolutely necessary
  3. ? other ideas? Maybe look at Anko on how to do a DSL properly?

Use DatePicker or TimePicker as FilterBar component

I want to use a Vaadin Date Picker component as a FilterBar component. An example is below:

image

For me, the goal is to let a user pick a date and the Grid results are then filtered from midnight to 11:59 PM.

Here is code that I am currently using to accomplish this:

fun <BEAN : Any, FILTER : Filter<BEAN>> FilterBar<BEAN, FILTER>.datePicker(column: Grid.Column<BEAN>): FilterBar.Binding<BEAN, FILTER> {
    require(!column.key.isNullOrBlank()) { "The column needs to have the property name as its key" }
    val component = DatePicker().apply {
        max = LocalDate.now()
        isClearButtonVisible = true
        setWidthFull()
    }
    return FilterBar.Binding.Builder(filterFactory, this, column, component) {
        // value getter
        component.value
    }.withConverter {
        DateInterval(it, it).toFilter(column.key, filterFactory, LocalDateTime::class.java)
    }.bind()
}

I was unable to use the following:

  • LocalDate.toFilter() - because it was private
  • DateInterval.toFilter() - because I didn't have a DateInterval but rather a LocalDate

A similar request would be for Vaadin Time Picker.

Relation to Vaadin

Hi @mvysny,

I'm wondering why this project doesn't reside at https://github.com/vaadin/, as this project is referenced in the official documentation

Vaadin supports the Kotlin programming language through the Karibu-DSL library, [...]

(I'm unsure if Vaadin means "Vaadin, the framework" or "Vaadin, the company" in this sentence.)

I can see in your GitHub profile you somehow belong to Vaadin Ltd., so I guess this started as a side project at some time. But what is the current state of relation between this project and Vaadin?

Grid + editor.setEnabled(true) == Editing items inside a Grid.

In vaadin 7 it was much easier to make grid columns editable. We just called .setEditorEnabled(true) and just did override preComit() and PostCommit methods for Cancel and Save functionality. In Vaadin 8(I'm just migrating it from Vaadin 7) we have to use Binder Could it be possible to develop an auto binding for Grid components editor.setEnabled(true) method? I mean for example if I have a grid like:

personGrid = grid(Person::class, "List of Persons", dataProvider = personsListProvider) {
            expandRatio = 1f;
            setSizeFull()
            appendHeaderRow().generateFilterComponents(this, Person::class)
        }

and then inside that personGrid I would just call: editor.setEnabled(true) so then it would just automatically create editing components for selected Person object? Then if we wan't / need easily override setEnabled(true) by using our custom bindings (for example if we don't want to edit every field, or some field need Calendar etc)..... Any idea what I'm talking about? Or is there a really easy way to do this in vaadin-on-kotlin and Vaadin 8, that I'm not aware about?

SqlDataProvider with PostgreSQL DB

First, after 2 weeks of working with VoK, I am still fascinated by its elegancy. Very nice work! Converting my few small Vaadin projects to VoK, i ran into this problem:

Using SqlDataProvider to map the result of a Postgresql table valued function, I got a Database error: Operation requires a scrollable ResultSet, but this ResultSet is FORWARD_ONLY. I believe it is a known problem as I could see plenty of post suggesting to manually set ResultSet.TYPE_SCROLL_INSENSITIVE. I could track down the issue to:

override fun sizeInBackEnd(query: Query<T, Filter<T>?>?): Int
(...)
val counts: List<Int> = q.executeAndFetch({ rs: ResultSet -> if (rs.last()) rs.row else 0 })

Unfortunately I do not quiet understand how to set this up correctly, my (SQL guy) solution was to wrap the query into a SELECT count(*) FROM ({$s}) AS Tab in computeSQL and do the counting on the Server, but I realize that this could be DB dependent.

One footnote: the statement ... ORDER BY NULL{{Where}} leads to an error on PostgreSQL, I used ... ORDER BY 1=1{{Where}} which amazingly does work (you can not use ORDER BY 1 as this uses the first row for sorting).

Thanx again,

v-bump to vok-orm 1.0

vok-orm is now based on JDBI and jdbi-orm. Steps needed:

[x] Release VoK 0.8 with the new vok-orm
[x] Update all VoK guides

`PredicateFilterFactory` not compatible with `buildFilter{}`

PredicateFilterFactory is not compatible with buildFilter{}.

The PredicateFilterFactory was meant to be a way to stay compatible with Vaadin's in-memory data providers (which accept Predicate<T> as the filters).

However, the Vaadin In-memory data provider model is utterly broken: in order for it to work properly, it requires support from classes using the API:

  1. You need to specify Grid.Column.comparator in certain cases instead of just calling Grid.Column.setSortable(true)
  2. Grid then needs to pass in Query.inMemorySorting.
  3. There's specialized InMemoryDataProvider interface just for in-memory stuff, even though BackEndDataProvider can also be used for fetching data from in-memory list.
  4. DataProvider.isInMemory(): it's highly unclear what that value is used for.

Question issue: Kotlin DSL

I see that there is two ways to design a kotlin DSL :

1st way:

class Body { }

fun Body.button (init: Button.() -> Unit) { }

class Button {
  var text: String? = null
}

2nd way: The advantage here is that you dont need to import button.

class Body {
  fun button (init: Button.() -> Unit) { }
}

class Button {
  var text: String? = null
}

My question is why functional programming is prefered in Kotlin DSL while you can use the second way to get rid of the imports?

Assistance Required

Can you please help us in using the thru gradle

  1. grid-renderers-collection-addon-2.2.10

Vaadin Grid and Grid editor

This is more like a feature request, in order to make your awseome project even easier to use. The one feature that brought me into the Vaadin world was Grids and specially the inline editing provided by Grids. Unfortunately documentation on this aspect is not very extensive, this beeing true for original Vaadin as well as for VoK. Key things I had to learn so far where:

  • setting up simple Editor Components (Textfield, Combobox inside grid, ...)
  • setting up more complex Editor Bindings (in order to do type conversion with grid editor)
  • formating values consistently between grid and grid editor
  • using row value based StyleGenerator
  • programmatically filtering DataSources inside the Grid
  • setting a validation Binder for the grid Editor

Things that I still struggle with but could be interesting for others too:

  • finding the best way of adding and editing new lines purely in grid editor
  • using unbuffered grid editor (where is that save event?)
  • using the GridFastNavigationAddon by Tatu Lund

I understand that making such samples that are easy enough to be understood yet show all the complex features is very time consuming, but I think that adding such into the grid (or even a new grid editor) section of karibudsl would greatly help future newbie VoK users.

All the best

Button examples incorrect

Several places in the docs are samples like:

        button("Back", { navigateToView<ArticlesView>() }) {
            styleName = ValoTheme.BUTTON_LINK
        }

that doesnt work for me -- perhaps nativeButton -- but for button I need to do:

        button("Back"){
             onLeftCLick { navigateToView<ArticlesView>() } 
            styleName = ValoTheme.BUTTON_LINK
        }

Switch to different REST server endpoint provider

The problem with RESTEasy is that it is:

  • Magic Annotations. Since there are inherent issues with annotations we should move forward to builders.
  • Integrated into the servlet container too deeply, so that it's hard to bootstrap for testing purposes.

The abovementioned also disqualifies Jersey since it too needs javax.ws.rs.core.Application to work properly; on top of that it has countless dependencies. I'll evaluate eventual replacements.

Security

Add a very simple support for security.

  1. Check the level of support in Ruby on Rails
  2. Evaluate the simplest framework for Java.
  3. Write a guide

Feature requests for Beverage Buddy example app

I think it's a useful enough starting app that it's worth making a ToDo list of features... for anyone who wants to take them on. This isn't intended as a complaint.

  1. I can not find a way to list all reviews by Category in the UI
  2. The categories page should be refactored to use the same right-column "Edit" that the Reviews uses.
  3. On Category page, default clicking of a category name should list all the reviews in that category.
    3a. On Reviews page, clicking of category should list reviews in that category.
  4. Reviews (and Categories?) list should have paging options when many rows available
  5. Does the framework have any language localization for strings? Such as the headings of "Search" "Reviews", button "New Review"? It would be nice to have a way to test/change languages to say Spanish for just these hand-full of words to demonstrate the coding technique best practices.
  6. Reviews page have a sort pick: Drink name, times tasted, date
  7. More complex: I notice this app is entirely JavaScript on the client side, the body tag is empty except for a noscript message. It would be perhaps useful to show a fallback mode of this simple app as a best-practice? Just to show coding best practices for a noscript app?

Thank you.

Getting Started Tutorial: Listing All Articles

Hi @mvysny.

I'm following the tutorial and when I arrive to section 5.8 Listing All Articles I get stuck:

vok-helloworld-app-v10/web/src/main/kotlin/com/example/vok/ArticlesView.kt: (6, 41): Unresolved reference: dataProvider

package com.example.vok

import com.github.mvysny.karibudsl.v10.*
import com.vaadin.flow.component.grid.Grid
import com.vaadin.flow.router.*
import eu.vaadinonkotlin.vaadin10.vokdb.dataProvider

@Route("articles")
class ArticlesView: KComposite(), AfterNavigationObserver {
   private lateinit var grid: Grid<Article>
   private val root = ui {
       verticalLayout {
           setSizeFull()
           h1("Listing Articles")
           grid = grid(dataProvider = Article.dataProvider) {
               isExpand = true; setSizeFull()
               addColumnFor(Article::id)
               addColumnFor(Article::title)
               addColumnFor(Article::text)
           }
       }
   }

   override fun afterNavigation(event: AfterNavigationEvent) {
       grid.refresh()
   }
}

Auto set tooltip for FilterBar headers?

Should forField attempt to auto add the tooltip/title?

This is tricky because forField accepts HasValue but Component.tooltip is an extension on Component.

Here is an example of how I am currently solving it:

fun Component.tooltip(property: KProperty1<*, *>): Component = tooltip(property.name)
fun Component.tooltip(column: Grid.Column<*>): Component = tooltip(column.key)
private fun Component.tooltip(key: String): Component {
    tooltip = SharedUtil.camelCaseToHumanFriendly(key)
    return this
}

/**
 * Create a [TextField] with a [TextField.valueChangeTimeout] of 600.
 */
fun <BEAN : Any, FILTER : Filter<BEAN>> FilterBar<BEAN, FILTER>.textField(column: Grid.Column<BEAN>): FilterBar.Binding.Builder<BEAN, String, FILTER> {
    val component = TextField().apply {
        // increase filter timeout from default 400 to 600
        // 400 seems a little fast for when someone is typing in a value (in case they pause)
        valueChangeTimeout = 600

        addThemeVariants(TextFieldVariant.LUMO_SMALL)

        tooltip(column)
    }
    return forField(component, column)
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.