https://kotlinlang.org logo
#kvision
Title
# kvision
t

Tomas Kormanak

03/02/2021, 5:06 PM
We are few versions back(3.16.1) and I am trying to upgrade and I found an issue with Tabulator. We use tabulator with observable list, like this(simplified):
Copy code
data class Voice(var _id: Id? = null, var name: String? = null) 

val options = TabulatorOptions(
        layout = Layout.FITCOLUMNS,
        columns = listOf(ColumnDefinition(tr("Name"), Voice::name.name))
    ),

val entities: ObservableList<E> = observableListOf()
tabulator = Tabulator(entities, options = options)
It used to work well, but after upgrade to 3.17.0 we can see only empty rows. I think it's because of this commit: https://github.com/rjaros/kvision/commit/5406e9e7b4cf8b516e9070179bb702038e2af46a since then data are converted to plain object in
Tabulator::replaceData
Also
getData()
now returns plain object instead of original object:
Copy code
tabulatorRowClick = { e ->
    val entity = (e.detail as JsTabulator.RowComponent).getData() as E
   // .... do something with entity
}
Am I missing something or is there a new way how to use Tabulator we should use?
r

Robert Jaros

03/02/2021, 5:08 PM
have you also migrated from legacy to IR?
t

Tomas Kormanak

03/02/2021, 5:11 PM
I didnt
Is it required?
r

Robert Jaros

03/02/2021, 5:16 PM
Let me check this ...
This is a bit complicated, but indeed I've made some changes in tabulator internals.
It was necessary to make tabulator work with IR.
With legacy compiler a data class instance can be used as JS plain object.
But with IR it can't. Even when you annotate a class with
@JsExport
it still won't work with tabulator.
So before 3.17 we were "cheating". Kotlin data classes where being sent directly as tabulator data. And most of the time, we could get kotlin data class back (eg. with an event).
t

Tomas Kormanak

03/02/2021, 5:35 PM
Ok, I see. But we can't even see data.
r

Robert Jaros

03/02/2021, 5:36 PM
And that's strange. Your example works fine for me.
t

Tomas Kormanak

03/02/2021, 5:36 PM
Actually when I declare a property in data class constructor it is not shown, when I declare it as a getter I see the value in the table.
r

Robert Jaros

03/02/2021, 5:38 PM
This is my test code:
Copy code
data class Voice(var _id: Int? = null, var name: String? = null)

// ...

        val options = TabulatorOptions(
            layout = Layout.FITCOLUMNS,
            columns = listOf(ColumnDefinition(tr("Name"), Voice::name.name)),
        )
        val entities: ObservableList<Voice> = observableListOf()

        var counter = 0
        root("kvapp") {
            tabulator(entities, options = options) {
                onEvent {
                    tabulatorRowClick = { e ->
                        val entity = (e.detail as io.kvision.tabulator.js.Tabulator.RowComponent).getData()
                        console.log(entity)
                    }
                }
            }
            button("add").onClick {
                entities.add(Voice(counter, "Name $counter"))
                counter++
            }
        }
Is the
_id
type the only difference?
I've tested with
data class Id(val id: Int)
and it works fine, too
t

Tomas Kormanak

03/02/2021, 5:43 PM
I simplified it, we have it implemented using generics to have one implementation for all entities.
but yes, I can't see significant difference
I'll try to isolate it.
r

Robert Jaros

03/02/2021, 5:49 PM
There is a function which should convert a data class to a plain object, which works with both legacy and IR: https://github.com/rjaros/kvision/blob/master/src/main/kotlin/io/kvision/utils/Snabbdom.kt#L99
And it's "hacky" 😉 Perhaps it's not working correctly with your data type. I haven't tested it with generics.
It doesn't use any Kotlin reflection - it uses JS internals.
Please try to run this on a single data object from your app, and check the js object you get.
t

Tomas Kormanak

03/02/2021, 6:02 PM
I'll try it tomorrow and let you know. Thank you.
I found the difference which breaks it, our data classes implement interface:
Copy code
data class Voice(override val id: Id?, var name: String? = null) : Entity

interface Entity {
    val id: Id?
}
@Robert Jaros I found it the problem is that method
getAllPropertyNames
returns only properties from the parent (in our case implemented interface) For the example above it returns only one property
id
r

Robert Jaros

03/04/2021, 10:06 AM
Thanks. I'll take a look.
👍 1
Any plans to migrate to IR?
I've checked the current problem is IR detection, which fails for your use case. I have some ideas how to fix this, but I've also checked it seems to work fine with IR. Just have to add
@JsExport
to your data class and interface.
t

Tomas Kormanak

03/04/2021, 1:32 PM
We didn't think about it much. Docs says it's still in aplha.
I tried IR, but it failed. It seems we are depending on lib which does not support it yet.
r

Robert Jaros

03/04/2021, 5:18 PM
Ok, I've made some tests and experiments.
First of all, I don't think I can fully support using composite kotlin objects as tabulator data model.
When using flat data classes it's possible to pass kotlin data to tabulator and get it back as kotlin data (with a small fix it works with both IR and legacy even when data class is inheriting fields from an interface)
But with a composite
Id
like yours I've managed to pass the data only from kotlin to js but I can't get it back (e.g. with
getData()
)
There are two options to consider: 1. You can just use it if you don't need to use
Tabulator.getData()
. I'll just fix the current legacy detection problem and your data should be fully visible in your tabulator tables. 2. I could add a new parameter to the
Tabulator
component, which will completely skip current conversions (basicaly revert to 3.16 behavior). Of course it will be usable with legacy backend only.
Please let me know what do you think.
t

Tomas Kormanak

03/05/2021, 12:52 PM
1. I think I can easily refactor the app to use getData() only to retrieve id of the entity and load it independently. 2. We probably switch to IR when it becomes stable. So it would just postpone the problem.
But I am not sure about the problem with composite objects you mentioned. We use them quite often not even for Id. e.g:
Copy code
data class User(..... val device:Device)

ColumnDefinition(tr("Device"), "device.name")
We also use properties with getters (to combine 2 properties into one column), but it seems it works well.
r

Robert Jaros

03/05/2021, 1:04 PM
but in 3.16?
t

Tomas Kormanak

03/05/2021, 1:05 PM
We have 3.16 now, but I am trying to upgrade it
r

Robert Jaros

03/05/2021, 1:07 PM
in 3.16 there is no conversion - kotlin data structures are just passed to and from tabulator. And it works with composite objects, nested structures, custom getters etc, because most of the time tabulator doesn't change the data.
but it's impossible with IR, because with IR kotlin data structures are different and not compatible with tabulator so I've added the conversions
and the conversions are problematic for composite objects
I can convert from kotlin to js but not from js to kotlin
that's why I said I can't support such composite objects
t

Tomas Kormanak

03/05/2021, 1:12 PM
Is the other way (js->kotlin) needed elsewhere than
getdata
function?
r

Robert Jaros

03/05/2021, 1:13 PM
And I'm not sure we are talking about the same
getData
:-)
t

Tomas Kormanak

03/05/2021, 1:14 PM
I am talking about
Tabulator.RowComponent.getData()
r

Robert Jaros

03/05/2021, 1:14 PM
yes, I though so
I was referring to
Tabulator.getData()
The conversion is there.
RowComponent.getData()
is tabulator's internal function.
and it just returns the data tabulator works with
without conversion, tabulator works with kotlin data so
RowComponent.getData()
returns kotlin
with conversion (>= 3.17) tabulator works with js data, so it returns plain js object
but you are right - it's easy to just take ID from it and retrieve kotlin data by ID
it will work fine for you
but I was asking about
Tabulator.getData()
- in >= 3.17 it will not work with composite structures
if you need this to work - the only way is to add an option and revert to 3.16 behavior in legacy mode
t

Tomas Kormanak

03/05/2021, 1:22 PM
I see, I don't need it
r

Robert Jaros

03/05/2021, 1:23 PM
ok
I assume it will work fine for you with the fix I've already pushed to master
I will make a new release this weekend
let me know after you test the new release
t

Tomas Kormanak

03/05/2021, 1:27 PM
ok, thank you. Basically we use Tabulator only to show data and open editor when user click on the row.
3 Views