<@UFT11FKSP> When I use the Tabulator in `dataUpda...
# kvision
a
@Robert Jaros When I use the Tabulator in
dataUpdateOnEdit = true
mode I get a (rather lengthy)
Uncaught exception...
in the browser console which seems to indicate an issue when setting data:
Copy code
<redacted>....refreshActiveData@http://localhost:8080/web/main.bundle.js:2:1490426\n_setDataActual@http://localhost:8080/web/main.bundle.js:2:1485115\n2995/setData/<@http://localhost:8080/web/main.bundle.js:2:1484530\nsetData@http://localhost:8080/web/main.bundle.js:2:1484442.....<redacted>
The data source for the Tabulator is declared as:
Copy code
val organizations: ObservableList<OrganizationExch> = observableListOf()
and
OrganizationExch
is declared as
Copy code
@Serializable
data class OrganizationExch(@Id var extId: String,
                            var name: String,
                            var description: String? = null,
                            var webUrl: String? = null,
                            var imageUrl: String? = null,
                            var theme: String? = null,
                            var createdAt: OffsetDateTime? = null,
                            var updatedAt: OffsetDateTime? = null)
[NOTE: I am trying to edit
webUrl
in my tests] The data originates from a
List
on the KVision back-end (SpringBoot) and I convert it into a
MutableList<OrganizationExch>
before synch (probably unnecessary):
Copy code
organizations.syncWithList(organizationService.getOrganizations().toMutableList())
I am clearly missing something..... can you help?
r
Do you use IR or legacy compiler?
a
kotlin.js.compiler=ir
IR compiler
r
And do you create the Tabulator with
serializer = serializer()
?
a
No....
I'd started out using the
dsl
builder (which was causing the same/similar exception)
and switched to Tabulator constructor... I'll add the serializer
r
When using IR you need to pass serializer or use
@JsExport
on your model data class. Do you see your data without it? :-)
a
Actually, I am wrong
serializer
was defined:
Copy code
orgTable = Tabulator(
      OrganizationModel.organizations,
      dataUpdateOnEdit = false,
      options = TabulatorOptions(
        layout = Layout.FITCOLUMNS,
        columns = listOf(
          ColumnDefinition(<http://I18n.tr|I18n.tr>("extId"), "extId", visible = false),
          ColumnDefinition(<http://I18n.tr|I18n.tr>("Name"), "name", editor = Editor.INPUT, cellEdited = { cellEdited(it) }),
          ColumnDefinition(<http://I18n.tr|I18n.tr>("Description"), "description", editor = Editor.INPUT, cellEdited = { cellEdited(it) }, width = "350"),
          ColumnDefinition(<http://I18n.tr|I18n.tr>("Web Url"), "webUrl", editor = Editor.INPUT, cellEdited = { cellEdited(it) }, width = "200"),
          ColumnDefinition(<http://I18n.tr|I18n.tr>("Logo Image Url"), "imageUrl", editor = Editor.INPUT, cellEdited = { cellEdited(it) }, width = "200"),
          ColumnDefinition(<http://I18n.tr|I18n.tr>("Theme"), "theme", editor = Editor.INPUT, cellEdited = { cellEdited(it) }, width = "100"),
          ColumnDefinition(<http://I18n.tr|I18n.tr>("Created At"), "createdAt", width = "175"),
          ColumnDefinition(<http://I18n.tr|I18n.tr>("Updated At"), "updatedAt", width = "175")
        ),
        index = "extId",
        pagination = true,
        paginationMode = PaginationMode.LOCAL,
        paginationSize = 10,
        autoResize = true
      ), types = setOf(TableType.BORDERED, TableType.HOVER, TableType.STRIPED), serializer = serializer()
    ).apply {
      height = 100.perc
    }
r
ok
a
Ignore the second
serializer = serializer
above - just added that 2 seconds ago
The one just before
.apply {
pre-existed
r
what is
cellEdited(...)
function doing?
a
I added the to capture the editing Event so I could try to modify the underlying record by hand. Right now it's just dumping info to
console.log()
Copy code
private fun cellEdited(editedCell: io.kvision.tabulator.js.Tabulator.CellComponent) {
    try {
      if (editedCell.isEdited()) {
        editedCell.getRow().getIndex().let {
          val extId = (it.asDynamic() as String)
          console.log("cellEdited index $extId")
          OrganizationModel.editRow(
            extId = extId,
            fieldName = editedCell.getField(),
            oldValue = editedCell.getOldValue(),
            newValue = editedCell.getValue()
          )
        }
      }
    } catch (e: Exception) {
      console.error("cellEdited threw ${e::class.simpleName}: ${e.message}")
    }
  }
Copy code
fun editRow(extId: String, fieldName: String, oldValue: Any?, newValue: Any?) {
    console.log("OrganizationModel.editRow(extId=$extId, fieldName=$fieldName, oldValue=$oldValue, newValue=$newValue)")
    organizations.firstOrNull { it.extId == extId }?.let { orgExch ->
      console.log("OrganizationModel.editRow(orgExch=$orgExch)")
    }
  }
If
dataUpdateOnEdit = false
then all the log dump runs w/o error or exception
If
dataUpdateOnEdit = true
then the console logs as before and the exception is thrown after the event handler completes
r
Do you use latest KVision version?
a
I think I am one release behind:
Copy code
avaVersion=1.8
#Plugins
systemProp.kotlinVersion=1.7.10
serializationVersion=1.3.3
systemProp.dependencyManagementPluginVersion=1.0.12.RELEASE
systemProp.springBootVersion=2.7.0
#Dependencies
systemProp.kvisionVersion=5.13.1
coroutinesVersion=1.6.4
systemProp.keycloakVersion=12.0.3
systemProp.jibVersion=3.2.1
systemProp.micrometerVersion=1.8.1
systemProp.jacksonVersion=2.13.3
systemProp.apacheCommonsIoVersion=1.3.2
systemProp.hazelcastVersion=5.1.2
systemProp.awsSdkVersion=2.17.42
systemProp.okHttpVersion=4.10.0
systemProp.retrofitVersion=2.9.0

kotlin.mpp.stability.nowarn=true
kotlin.js.compiler=ir
org.gradle.jvmargs=-Xmx2g
5.13.1
r
And you use
kvision-tabulator
module (not
kvision-tabulator4
) ?
a
The other dependencies should be up to date with 5.13.1 (Except KeyCloak - OIDC/OAuth2 provider)
yes,
Copy code
implementation("io.kvision:kvision-tabulator:$kvisionVersion")
r
I've recreated your use case in my simple project and it seems to work fine for me with
dataUpdateOnEdit = true
. But this is in plain frontend project. I'll try once again with the fullstack project.
a
Thank you.
Meanwhile, I'll upgrade to KVision 5.14.0 and see what happens
r
It works for me with the fullstack project as well
a
Oh, I should add one note: I am packaging the webpack of the FrontEnd of my project and copying it to the
resources/public
directory inside the SpringBoot BackEnd. There are two reasons for this: 1.) So that I can use SpringBoot's SpringSecurity/ResourceServer and KeyCloak to secure the entire application, and 2.) So that I can use a Google Jib Gradle task to package the entire thing to a Docker container for production deployment to Kubernetes Right now I am testing locally, so I am not performing all that packaging (i.e. I am running things like your full-stack SpringBoot example), but there is an outside possibility that something is unique to my setup and causing the problem...
r
This is my App.kt with a tabulator:
Copy code
class App : Application() {

    val organizations: ObservableList<OrganizationExch> =
        observableListOf(
            OrganizationExch("1", "111", createdAt = Date(), updatedAt = Date()),
            OrganizationExch("2", "222", createdAt = Date(), updatedAt = Date())
        )

    fun cellEdited(cell: io.kvision.tabulator.js.Tabulator.CellComponent) {
    }

    override fun start(state: Map<String, Any>) {
        I18n.manager =
            DefaultI18nManager(
                mapOf(
                    "en" to io.kvision.require("i18n/messages-en.json"),
                    "pl" to io.kvision.require("i18n/messages-pl.json")
                )
            )
        root("kvapp") {
            tabulator(
                organizations,
                serializer = serializer(),
                dataUpdateOnEdit = true,
                options = TabulatorOptions(
                    layout = Layout.FITCOLUMNS,
                    columns = listOf(
                        ColumnDefinition(<http://I18n.tr|I18n.tr>("extId"), "extId", visible = false),
                        ColumnDefinition(
                            <http://I18n.tr|I18n.tr>("Name"),
                            "name",
                            editor = Editor.INPUT,
                            cellEdited = { cellEdited(it) }),
                        ColumnDefinition(
                            <http://I18n.tr|I18n.tr>("Description"),
                            "description",
                            editor = Editor.INPUT,
                            cellEdited = { cellEdited(it) },
                            width = "350"
                        ),
                        ColumnDefinition(
                            <http://I18n.tr|I18n.tr>("Web Url"),
                            "webUrl",
                            editor = Editor.INPUT,
                            cellEdited = { cellEdited(it) },
                            width = "200"
                        ),
                        ColumnDefinition(
                            <http://I18n.tr|I18n.tr>("Logo Image Url"),
                            "imageUrl",
                            editor = Editor.INPUT,
                            cellEdited = { cellEdited(it) },
                            width = "200"
                        ),
                        ColumnDefinition(
                            <http://I18n.tr|I18n.tr>("Theme"),
                            "theme",
                            editor = Editor.INPUT,
                            cellEdited = { cellEdited(it) },
                            width = "100"
                        ),
                        ColumnDefinition(<http://I18n.tr|I18n.tr>("Created At"), "createdAt", width = "175"),
                        ColumnDefinition(<http://I18n.tr|I18n.tr>("Updated At"), "updatedAt", width = "175")
                    ),
                    index = "extId",
                    pagination = true,
                    paginationMode = PaginationMode.LOCAL,
                    paginationSize = 10,
                    autoResize = true
                ), types = setOf(TableType.BORDERED, TableType.HOVER, TableType.STRIPED)
            ).apply {
                height = 100.perc
            }
        }
    }
}
I've defined
OrganizationExch
in the common module just like you
a
If there's something significantly different between our code, I am missing it Looks effectively the same to me
r
Can you share your project?
a
Not the whole thing... client-proprietary/NDA stuff in there
I can work to make a reduced version
First, I'll try to update to 5.14.0
r
There is a difference ... I have dsl builder
tabulator()
and you have constructor
Tabulator()
.
a
Good point. I'll revert to the
dsl
version
r
The constructor for this class is hard to use - that's why there are some inline
create
functions defined.
When I just use constructor I have an exception as well.
a
Hmmm... stand by
r
Tabulator.create()
works fine
When using constructor directly you need to pass
kClass
as well (
kClass = OrganizationExch::class
)
a
Well, certainly that fixed the issue
But a new question: The
cellEdit
event is firing before the Tabulator has a chance to update the underlying List. How can I capture the change event after the record in the List is updated? I need to message it back to the underlying store....
r
I'm afraid you can't. The list is updated asynchronously and it's done as the last operation.
a
Can I bind an event handler to the ObservableList?
r
Yes. You can use
subscribe()
.
a
I can see that I can bind to the whole list - but not to the individual record that's been updated
r
No
a
Ahhh, OK.... I'll use the
cellEdited
handler then... Thank you for the help!
r
Yep,
cellEdited
is probably the best in this case.
Glad to be helpful :)
a
Understood.... thank you again... KVision is a wonderful framework - learning curve is daunting, but worth it I think 🙂