Views
4 months ago

tornadofx-guide

11. Editing Models and

11. Editing Models and Validation Introducing ViewModel The ViewModel is a mediator between the TableView and the Form . It acts as a middleman between the data in the text fields and the data in the actual Person object. As you will see, the code is much shorter and easier to reason about. The implementation code of the PersonModel will be shown shortly. For now just focus on its usage. class PersonEditor : View("Person Editor") { override val root = BorderPane() val persons = listOf(Person("John", "Manager"), Person("Jay", "Worker bee")).obser vable() 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() } } } } 156

11. Editing Models and Validation 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(var person: Person) : ViewModel() { val name = bind { person.nameProperty } val title = bind { person.titleProperty } } This looks a lot better, but what exactly is going on here? We have introduced a subclass of ViewModel called PersonModel . The model holds a Person object and has properties for the name and title fields. We will discuss the model further after we have looked at the rest of the client code. Note that we hold no reference to the TableView or the text fields. Apart from a lot less code, the first big change is the way we update the Person inside the model: model.rebindOnChange(this) { selectedPerson -> } person = selectedPerson ?: Person() The rebindOnChange() function takes the TableView as an argument and a function that will be called when the selection changes. This works with ListView , TreeView , TreeTableView , and any other ObservableValue as well. This function is called on the model and has the selectedPerson as its single argument. We assign the selected person to the person property of the model, or a new Person if the selection was empty/null. That way we ensure that there is always data for the model to present. When we create the TextFields, we bind the model properties directly to it since most Node builders accept an ObservableValue to bind to. field("Name") { } textfield(model.name) Even when the selection changes, the model properties persist but the values for the properties are updated. We totally avoid the manual binding from our previous attempt. 157

GUIDE
GUIDE
Guide
Guide
Guide
GUIDE
Guide
GUIDE
GUIDE
Guide
GUIDE
GUIDE