rjaros / kvision

Object oriented web framework for Kotlin/JS
https://kvision.io
MIT License
1.2k stars 67 forks source link

Tabulator Exception (The data class type is unknown) when using formatterComponentFunction with ajax #467

Closed drammelt closed 8 months ago

drammelt commented 1 year ago

I have been in the process of upgrading our projects KVision from 5.18.1 to 6.2.2 and have run into this issue. Previously all the tabulator tables were working correctly but since changing to 6.2.2 it throws an exception when any column contains a formatterComponentFunction. If I remove these columns the tabulator will load correctly with no exceptions. If I remove everything except for the formatterComponentFunction column it will still throw an exception.

This is the most stripped down example of what is failing (It works fine if I remove the ajax calls and just use local data, so the issue appears to be caused by using Ajax data?)

tabulator(
                    options = TabulatorOptions(
                        height = "calc(100vh - ${tabHeight + Dimentions.TOP_BAR_HEIGHT.first.toInt() + Dimentions.SEARCH_BAR_HEIGHT.first.toInt()}px)",
                        pagination = true,
                        ajaxURL = ajaxURL,
                        ajaxConfig = "POST",
                        ajaxContentType = "json",
                        paginationMode = PaginationMode.REMOTE,
                        filterMode = FilterMode.REMOTE,
                        sortMode = SortMode.REMOTE,
                        layout = Layout.FITCOLUMNS,
                        columns = listOf(
                            ColumnDefinition(
                                "", hozAlign = Align.CENTER,
                                width = "50",
                                formatterComponentFunction = { _, _, d ->
                                   Div()
                                },
                                headerSort = false,
                            ),
                        )
                    )
                )

Exception...

kvision-kvision-tabulator.js?963c:5534 

       Uncaught (in promise) IllegalStateExceptioncause: undefinedmessage: "The data class type is unknown"name: "IllegalStateException"stack: "IllegalStateException: The data class type is unknown\n    at Format.eval (webpack-internal:///./kotlin/kvision-kvision-tabulator.js:5518:15)\n    at Format.formatValue (webpack-internal:///../../node_modules/tabulator-tables/dist/js/tabulator_esm.js:10754:47)\n    at eval (webpack-internal:///../../node_modules/tabulator-tables/dist/js/tabulator_esm.js:24322:33)\n    at Array.forEach (<anonymous>)\n    at InternalEventBus._chain (webpack-internal:///../../node_modules/tabulator-tables/dist/js/tabulator_esm.js:24321:21)\n    at Cell.chain (webpack-internal:///../../node_modules/tabulator-tables/dist/js/tabulator_esm.js:131:30)\n    at Cell._generateContents (webpack-internal:///../../node_modules/tabulator-tables/dist/js/tabulator_esm.js:1692:14)\n    at Cell.layoutElement (webpack-internal:///../../node_modules/tabulator-tables/dist/js/tabulator_esm.js:1803:8)\n    at Cell.getElement (webpack-internal:///../../node_modules/tabulator-tables/dist/js/tabulator_esm.js:1729:10)\n    at eval (webpack-internal:///../../node_modules/tabulator-tables/dist/js/tabulator_esm.js:20497:33)"[[Prototype]]: RuntimeExceptionconstructor: ƒ IllegalStateException()$metadata$: {kind: 'class', simpleName: 'IllegalStateException', associatedObjectKey: undefined, associatedObjects: undefined, suspendArity: Array(0), …}length: 0name: "IllegalStateException"prototype: RuntimeExceptionconstructor: ƒ IllegalStateException()$metadata$: $kClass$: undefinedassociatedObjectKey: undefinedassociatedObjects: undefinediid: nullkind: "class"simpleName: "IllegalStateException"suspendArity: [][[Prototype]]: Objectconstructor: ƒ Object()hasOwnProperty: ƒ hasOwnProperty()isPrototypeOf: ƒ isPrototypeOf()propertyIsEnumerable: ƒ propertyIsEnumerable()toLocaleString: ƒ toLocaleString()toString: ƒ toString()valueOf: ƒ valueOf()__defineGetter__: ƒ __defineGetter__()__defineSetter__: ƒ __defineSetter__()__lookupGetter__: ƒ __lookupGetter__()__lookupSetter__: ƒ __lookupSetter__()__proto__: (...)get __proto__: ƒ __proto__()set __proto__: ƒ __proto__()length: 0name: "IllegalStateException"prototype: RuntimeException {constructor: ƒ}arguments: (...)caller: (...)[[FunctionLocation]]: kotlin-kotlin-stdlib-js-ir.js?46ac:25119[[Prototype]]: ƒ ()[[Scopes]]: Scopes[4][[Prototype]]: Exceptionarguments: (...)caller: (...)[[FunctionLocation]]: kotlin-kotlin-stdlib-js-ir.js?46ac:25119[[Prototype]]: ƒ ()[[Scopes]]: Scopes[4][[Prototype]]: Exception
rjaros commented 1 year ago

How is the data class defined?

drammelt commented 1 year ago

Not 100% sure what you mean, if you mean the data from the Ajax endpoint its just json data.

drammelt commented 1 year ago

Using the same endpoint as the example above the following code will not throw an exception...

tabulator(
                    options = TabulatorOptions(
                        height = "calc(100vh - ${tabHeight + Dimentions.TOP_BAR_HEIGHT.first.toInt() + Dimentions.SEARCH_BAR_HEIGHT.first.toInt()}px)",
                        pagination = true,
                        ajaxURL = ajaxURL,
                        ajaxConfig = "POST",
                        ajaxContentType = "json",
                        paginationMode = PaginationMode.REMOTE,
                        filterMode = FilterMode.REMOTE,
                        sortMode = SortMode.REMOTE,
                        layout = Layout.FITCOLUMNS,
                        columns = listOf(
                            ColumnDefinition(title = "ID", field = "id", width = "100"),
                        )
                    )
                )

Only if I add a column using formatterComponentFunction will it fail.

rjaros commented 1 year ago

If I assume correctly you are using a tabulator component without a strict type (Tabulator<dynamic>). In this case formatterComponentFunction will not currently work in KVision 6.

The formatterComponentFunction was generally designed to work with Kotlin data model. With dropping the legacy backend support, the Tabulator component is now more strict when it comes to Kotlin data model. With current implementation, you need to define a @Serializable Kotlin data class, use typed Tabulator<T> and also pass a serializer parameter to allow Tabulator component convert internal JavaScript model to Kotlin model.

I will look at the code a bit more and check if it would be possible to just pass the dynamic data without any conversion.

drammelt commented 1 year ago

That is correct, thank you for clearing that up.

drammelt commented 1 year ago

Moved the AjaxDTO to common code.

Made data models in the common code generic (had to remove ZonedDateTime fields and replace with String as there appears to be no way to create a KSerializer for kvisions ZonedDateTime as it has no constructors in common code and contextual does not work when calling from the backend as it has no idea about kvisions datetime).

Changed all tabulators to use specific data model and added serializer and it all works fine now.

Thank you.

rjaros commented 8 months ago

Fixed in 7.1.0