Nestings

nicolaiparlog edited this page Nov 22, 2014 · 4 revisions

Nestings are concerned with JavaFX properties. The core API provides awesome capabilities but misses a pleasant way to interact with properties which are hidden in a more complex object hierarchy. Nestings solve this.

The feature's concept is described below before an example demonstrates its use. More examples can be found in a demo file in the code. The packages and classes provide further documentation. The best entry point is the comment on the package org.codefx.libfx.nesting.

Concept

This feature provides a simple and fluent API to create nestings, where a Nesting represents a hierarchy like Employee -> Address -> ZIP code. The nesting would collapse the hierarchy to the innermost property (generally called the inner observable; in this example zipProperty) and update itself whenever the employee or address instances change.

A nesting can than be used in several ways:

  • You can build a property which is bidirectionally bound to the inner observable.
  • You can attach an invalidation or a change listener, which is carried along as the inner observable is replaced.
  • You can create bindings which are not only updated when the inner property's value changes but also when the property itself is replaced. (Actually you can not do that yet, see #2. Currently, you have to create a property first and bind to that.)

These further steps can be made with the same fluent API without breaking your stride.

Example

Let's see an example...

The Situation

Say you have an object of type Employee and you're creating an EmployeeEditor for editing a single employee at a time. You will most likely have a model for your UI which has something like a currentEmployeeProperty. Now you might want to bind some properties of the controls you're using for your editor to the current employee's properties. For example you might have a slider and want to bind it to the employee's salaryProperty.

The Problem

Up to now that's all straight forward. But what happens when the current employee is replaced by another? Of course you want your editor to be updated.

The "Solutions"

The most obvious way is to explicitly listen to changes of the model's currentEmployeeProperty and update the binding accordingly. That's rather tedious and leads to a lot of the same code all over the place. And it gets even worse, when you're nesting deeper, e.g. binding to the current employee's address' ZIP code.

You could use Bindings.select but it has some downsides. For one thing, it uses strings to identify the nested properties, which breaks down quickly under refactoring and is not type safe. Unfortunately you won't even get an exception when trying to access properties which aren't there anymore - your binding will just forever contain null. Another downside is the return type. It's just an ObjectBinding (or DoubleBinding or ...) which does not suffice for bidirectional binding.

The Solution

Use LibFX! :)

To bind the slider to the salary:

DoubleProperty currentEmployeesSalary = Nestings
	.on(currentEmployeeProperty)
	.nestDouble(employee -> employee.salaryProperty())
	.buildProperty();
slider.valueProperty().bindBidirectional(currentEmployeesSalary);

It is even more readable with method references:

DoubleProperty currentEmployeesSalary = Nestings
	.on(currentEmployeeProperty)
	.nestDouble(Employee::salaryProperty)
	.buildProperty();
slider.valueProperty().bindBidirectional(currentEmployeesSalary);

To add a listener to the ZIP code property:

Nestings.on(currentEmployeeProperty)
	.nest(Employee::addressProperty)
	.nest(Address::zipProperty)
	.addChangeListener(myListener);