You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TornadoFX中TableView与详情编辑器数据同步问题及最佳实践咨询

Hey there! Let's tackle your problem step by step—first fixing the TableView update issue, then reviewing whether your current setup follows TornadoFX best practices.

Why the TableView Isn't Updating

The core issue here is that your Person data class uses plain var properties instead of JavaFX ObservableValue types (like StringProperty). TornadoFX's TableView relies on either:

  • Observable properties in your model to detect in-place value changes, or
  • Changes to the underlying ObservableList (like adding/removing/replacing elements) to trigger row refreshes.

When you call model.commit() in your PersonEditor, it updates the properties of the original Person instance—but since those properties aren't observable, the TableView has no way to know the values changed. It only watches for structural changes to the personlist itself.

Best Solutions

Option 1: Trigger List Update After Commit (No Model Changes)

Since you want to keep your model free of JavaFX dependencies, this is the cleanest approach. After committing changes in the editor, replace the modified Person instance in the ObservableList to trigger a refresh:

  1. Pass a save callback to your editor:
class PersonEditor(private val person: Person, private val onSave: (Person) -> Unit) : Fragment("Edit Person") {
    val model = PersonModel(person)

    override val root = form {
        fieldset("Person Details") {
            field("Last Name") { textfield(model.name) }
            field("First Name") { textfield(model.firstname) }
            // Add fields for the `complex: Stuff` property as needed
        }
        buttonbar {
            button("Save") {
                action {
                    if (model.commit()) {
                        onSave(person) // Pass the updated instance back
                        close()
                    }
                }
            }
            button("Cancel") { action { close() } }
        }
    }
}

// Your ItemViewModel implementation
class PersonModel(person: Person) : ItemViewModel<Person>(person) {
    val name = bind(Person::name)
    val firstname = bind(Person::firstname)
    // Bind `complex` properties if needed, or create a nested ViewModel for Stuff
}
  1. Update the ObservableList in your main view:
// In your TableView-containing view
private val personlist = mutableListOf<Person>().observable()

// When opening the editor (e.g., on Enter key press)
fun openEditor() {
    val selectedPerson = personlist.selectedItem ?: return
    find<PersonEditor>(mapOf(
        PersonEditor::person to selectedPerson,
        PersonEditor::onSave to { updatedPerson ->
            val index = personlist.indexOf(selectedPerson)
            if (index != -1) {
                // Replace the instance in the list to trigger TableView refresh
                personlist[index] = updatedPerson
            }
        }
    )).openModal()
}

This works because replacing an element in an ObservableList fires a change event, which the TableView detects and uses to refresh the corresponding row.

Option 2: Use a Mapper to Sync ViewModel and List (Advanced)

If you want to avoid manual list updates, you can set up a listener on your ItemViewModel to sync changes back to the list automatically. This is useful if you have frequent edits:

class PersonModel(person: Person, private val list: ObservableList<Person>) : ItemViewModel<Person>(person) {
    val name = bind(Person::name)
    val firstname = bind(Person::firstname)

    init {
        // Listen for commit events and update the list
        onCommit {
            val index = list.indexOf(item)
            if (index != -1) {
                list[index] = item
            }
        }
    }
}

Then pass personlist to the PersonModel when creating it in the editor.

Are You Following Best Practices?

Your current setup is mostly aligned with TornadoFX and clean architecture principles—here's the breakdown:

  • ✅ Model independence: Keeping Person free of JavaFX dependencies is great for separation of concerns. Your model can be reused in non-UI contexts (like serialization/deserialization) without issues.
  • ✅ ObservableList for data: Using mutableListOf<Person>().observable() is the recommended way to manage data in TornadoFX, as it integrates seamlessly with TableView.
  • ✅ ItemViewModel for editing: Using ItemViewModel to handle form state and validation is a core TornadoFX best practice—it keeps your UI code clean and separates editing logic from the view.

Minor Improvements to Consider

  • Handle null selection: Add a check for personlist.selectedItem before opening the editor to avoid crashes if no row is selected.
  • Nested ViewModel for Stuff: If Stuff has its own properties, create a nested ItemViewModel for it in PersonModel to simplify form binding and validation.
  • Validation: Add validation rules to your PersonModel (e.g., non-empty name) using validator blocks to improve user experience.

内容的提问来源于stack exchange,提问作者IARI

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:39:21