Использование компонентов Vaadin в проекте kotlin js - PullRequest
1 голос
/ 26 апреля 2019

Этот вопрос касается проекта Kotlin JS, в котором используется плагин внешнего интерфейса Kotlin.

Я хочу использовать некоторые компоненты пользовательского интерфейса из библиотеки Vaadin Components .

IУ меня есть два вопроса по этому поводу:

(1) Как лучше всего включить веб-компоненты в Kotlin JS

=> для моего полного кода, см. ссылку на источник ниже.Таким образом, соответствующие данные:

build.gradle.kts

kotlinFrontend {
    npm {
        dependency("@vaadin/vaadin-grid")
    }
}

vaadin.grid.Imports.kt

@file:JsModule("@vaadin/vaadin-grid")
@file:JsNonModule
package vaadin.grid
external class GridElement {
    companion object
}

Почему companion object?Мне нужно это для обходного пути (см. Ниже).

foo.kt

fun main() {

    document.getElementById("container")!!.append {
        vaadin_grid {
            attributes["id"] = "grid"
        }
    }

    initUI()

}

fun initUI() {
    // Force the side-effects of the vaadin modules. Is there a better way?
    console.log(GridElement)

    val grid = document.querySelector("#grid") /* ?? as GridElement ?? */
}

console.log - это уродливый обходной прием, которого я хочу избежать.Если я ничего не делаю с GridElement, то он просто не включается в мой пакет.

DSL vaadin_grid определяется как пользовательский тег kotlinx.html, который не связан с кодом.

(2) Я хочу сохранить мой код как можно более типизированным, чтобы избежать asDynamic, но когда я приводил HTMLElement к элементу Vaadin, я получаю ClassCastExceptions (потому что GridElement равно undefined).

Например, я хочу написать что-то вроде этого:

val grid : GridElement = document.querySelector("#grid") as GridElement
grid.items = ... // vs grid.asDynamic().items which does work

Вот как я определяю внешний GridElement

vaadin / button / Imports.kt

@file:JsModule("@vaadin/vaadin-grid")
@file:JsNonModule

package vaadin.grid

import org.w3c.dom.HTMLElement

abstract external class GridElement : HTMLElement {
    var items: Array<*> = definedExternally
}

build/node_modules/@vaadin/vaadin-grid/src/vaadin-grid.js

...
customElements.define(GridElement.is, GridElement);
export { GridElement };

Исходный пример

Для запуска:

Из корня репозитория git:

./gradlew 05-kt-frontend-vaadin:build && open 05-kt-frontend-vaadin/frontend.html

1 Ответ

1 голос
/ 14 мая 2019

Я нашел ответ (ы)

По первому вопросу

(1) Как лучше всего включить веб-компоненты в Kotlin JS

* 1007?*

Вместо console.log для запуска побочных эффектов я использую require(...)

external fun require(module: String): dynamic

fun main() {

    require("@vaadin/vaadin-button")
    require("@vaadin/vaadin-text-field")
    require("@vaadin/vaadin-grid")
    ...
}

(кредит на чей-то ответ в списке kotlin-frontend-plugin )

(2) Я хочу сохранить мой код как можно более типизированным, чтобы избежать asDynamic

Вместо импорта @vaadin/vaadin-grid Мне нужно импортировать файл, который фактически предоставляет элемент,Тогда это, кажется, работает, и я даже могу добавить дженерики к своему GridElement:

@file:JsModule("@vaadin/vaadin-grid/src/vaadin-grid")
@file:JsNonModule

package vaadin.grid

import org.w3c.dom.HTMLElement

abstract external class GridElement<T> : HTMLElement {
    var items: Array<out T> = definedExternally
}

Таким образом, я смог избавиться от всех asDynamic s

    val firstNameField = document.querySelector("#firstName") as TextFieldElement?
    val lastNameField = document.querySelector("#lastName") as TextFieldElement?
    val addButton = document.querySelector("#addButton") as ButtonElement?
    val grid = document.querySelector("#grid") as GridElement<Person>?

    val initialPeople: Array<out Person> = emptyArray()
    grid?.items = initialPeople

    addButton?.addEventListener("click", {
        // Read the new person's data
        val person = Person(firstNameField?.value, lastNameField?.value)

        // Add it to the items
        if(grid != null){
            val people = grid.items
            grid.items = people.plus(person)
        }

        // Reset the form fields
        firstNameField?.value = ""
        lastNameField?.value = ""
    })
...