Views
8 months ago

tornadofx-guide

11. Editing Models and

11. Editing Models and Validation @Test fun swap_source_object() { val person1 = Person("Person 1") val person2 = Person("Person 2") val model = PersonModel(person1) assertEquals(model.name, "Person 1") } model.rebind { person = person2 } assertEquals(model.name, "Person 2") The test creates two Person objects and a ViewModel . The model is initialised with the first person object. It then checks that model.name corresponds to the name in person1 . Now something weird happens: model.rebind { person = person2 } The code inside the rebind() block above will be executed and all the properties of the model are updated with values from the new source object. This is actually analogous to writing: model.person = person2 model.rebind() The form you choose is up to you, but the first form makes sure you do not forget to call rebind. After rebind is called, the model is not dirty and all values will reflect the ones form the new source object or source objects. It's important to note that you can pass multiple source objects to a view model and update all or some of them as you see fit. Rebind Listener Our TableView example called the rebindOnChange() function and passed in a TableView as the first argument. This made sure that rebind was called whenever the selection of the TableView changed. This is actually just a shortcut to another function with the same name that takes an observable and calls rebind whenever that observable changes. If you call this function, you do not need to call rebind manually as long as you have an observable that represent the state change that should cause the model to rebind. As you saw, TableView has a shorthand support for the selectionModel.selectedItemProperty . If not for this shorthand function call, you would have to write it like this: 162

11. Editing Models and Validation model.rebindOnChange(table.selectionModel.selectedItemProperty()) { } person = it ?: Person() 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 shorter version or use the ItemViewModel . ItemViewModel When working with the ViewModel you will notice some repetitive and somewhat verbose tasks. They include calling rebind or configuring rebindOnChange to change the source object. The ItemViewModel is an extension to the ViewModel and in almost all use cases you would want to inherit from this instead of the ViewModel class. The ItemViewModel has a property called itemProperty of the specified type, so our PersonModel would now look like: class PersonModel : ItemViewModel() { val name = bind(Person::nameProperty) val title = bind(Person::titleProperty) } You will notice we no longer need to pass in the var person: Person in the constructor. The ItemViewModel now has an observable property called itemProperty and getters/setters via the item property. Whenever you assign something to item or via itemProperty.value , the model is automatically rebound for you. There is also an observable empty boolean value you can use to check if the ItemViewModel is currently holding a Person . The binding expressions need to take into account that it might not represent any item at the time of binding. That is why the binding expressions above now use the null safe operator. We just got rid of some boiler plate, but the ItemViewModel gives us a lot more functionality. Remember how we bound the selected person from the TableView to our model earlier? // Update the person inside the view model on selection change model.rebindOnChange(this) { selectedPerson -> person = selectedPerson ?: Person() } 163

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