edvin / tornadofx-guide

TornadoFX Guide
133 stars 67 forks source link

Sample code in 11. Editing Models and Validation has Unresolved reference error #99

Closed dragonic09 closed 5 years ago

dragonic09 commented 5 years ago

Hi,

After I copied the code from the Introducing ViewModel section into Intellij IDE, I found that there are Unresolved reference errors in model.rebindOnchange and in the save function, which occur from person.

person = selectedPerson ?: Person()
and
val person = model.person

then I change it to

item = selectedPerson ?: Person()
and
val person = model.item

the errors are vanished, but I'm not sure if this is the right way to fix because I'm new to Tornadofx and Kotlin.

ahinchman1 commented 5 years ago

It depends on whether you were using an ItemViewModel or a ViewModel - for example:

class PersonModel(var person: Person? = null) : ViewModel() {
    val name = bind(true, defaultValue = "Yoyo") { person?.nameProperty() ?: SimpleStringProperty() as Property<String> }
}

// JavaFX Property
open class PersonModel(person: Person? = null) : ItemViewModel<Person>(person) {
    val name = bind { item?.nameProperty() }
    val age = bind { item?.ageProperty() }
    val phone = bind { item?.phoneProperty() }
    val email = bind { item?.emailProperty() }
}

ItemViewModel is a subclass of ViewModel, and possesses the property item in its class (which you can see if you click Cmd + click the ItemViewModel in the IDE to dig into the code). Hope this helps!

edvin commented 5 years ago

Can you post the surrounding/complete code so I know exactly where you're taking this from? On a general note, there is almost never a need to implement rebindOnChange, this is included mostly to explain what happens behind the scenes.

dragonic09 commented 5 years ago

https://edvin.gitbooks.io/tornadofx-guide/content/part1/11.%20Editing%20Models%20and%20Validation.html At "Introducing ViewModel section."

class PersonEditor : View("Person Editor") {
    override val root = BorderPane()
    val persons = listOf(Person("John", "Manager"), Person("Jay", "Worker bee")).observable()
    val model = PersonModel(Person())

    init {
        with(root) {
            center {
                tableview(persons) {
                    column("Name", Person::nameProperty)
                    column("Title", Person::titleProperty)

                    // Update the person inside the view model on selection change
                    model.rebindOnChange(this) { selectedPerson ->
                        person = selectedPerson ?: Person()
                    }
                }
            }

            right {
                form {
                    fieldset("Edit person") {
                        field("Name") {
                            textfield(model.name)
                        }
                        field("Title") {
                            textfield(model.title)
                        }
                        button("Save") {
                            enableWhen(model.dirty)
                            action {
                                save()
                            }
                        }
                        button("Reset").action {
                            model.rollback()
                        }
                    }
                }
            }
        }
    }

    private fun save() {
        // Flush changes from the text fields into the model
        model.commit()

        // The edited person is contained in the model
        val person = model.person

        // A real application would persist the person here
        println("Saving ${person.name} / ${person.title}")
    }

}
class PersonModel(person: Person) : ItemViewModel<Person>(person) {
    val name = bind(Person::nameProperty)
    val title = bind(Person::titleProperty)
}
edvin commented 5 years ago

Ah, I see. I've updated the code and also added a warning stating that this is not the way to do it, it just explains what's going on inside the view model. You should probably never write your code this way. I've also included:

"The above example is included to clarify how the rebindOnChange() function works under the hood. For real use cases involving a TableView, you should opt for the bindSelected(model) function that is available when you combine TableView and ItemViewModel."

dragonic09 commented 5 years ago

Thank you! I was just trying to see how the code work, so I had been playing with it a bit. :+1:

edvin commented 5 years ago

Great :) Thanks for reporting. Some areas of the guide should probably be rewritten because we have better ways of doing stuff now, and some of the explanations of old concepts are just confusing to new users. Please report if you find more inaccuracies :)