How do I make this JavaFX binding fire more than once?

Viewed 85

Why does this binding not trigger its associated code when the source data it is bound to is updated the second time?

It seems to me that every change of any TestObj's 'name' field should result in testProperty1 showing the 3rd element's name. In this case it shows the name after binding, and after the first update, but after the second update it still shows the name from the first update. How do I get it to update testProperty1 every time a TestObj is changed?

class TestObj {
    var name by property<String>()
    fun nameProperty() = getProperty(TestObj::name)
}

class TestBase {
    var testObjs by property<List<TestObj>>()
    fun testObjsProperty() = getProperty(TestBase::testObjs)
}

class TestBaseModel: ItemViewModel<TestBase>(TestBase()) {
    val objects: ListProperty<TestObj> =
        bind(autocommit = true) { item?.testObjsProperty() } as ListProperty<TestObj>

    fun observableObjs(): StringBinding {
        val observableObjs = FXCollections.observableList(objects) { o: TestObj ->
            arrayOf<Observable>(
                o.nameProperty()
            )
        }
        return stringBinding(observableObjs, objects) {
            (observableObjs.map {
                it.name
            }).joinToString()
        }
    }
}

val testProperty1 = SimpleStringProperty()
testProperty1.value = "unset"

val base = TestBaseModel()
for (i in 0..4) {
    base.objects.add(TestObj().apply{
        name = "$i"
    })
}

assertEquals("unset", testProperty1.value)

testProperty1.bind(stringBinding(base.observableObjs()) {
    base.objects[2].name
})

assertEquals("2", testProperty1.value)

base.objects[2].name = "a"

assertEquals("a", testProperty1.value)

base.objects[2].name = "ab"

// This assertion fails, claims value is still "a"
assertEquals("ab", testProperty1.value)

1 Answers

It looks like a race condition. Binding works asynchronously it may not have a chance to fire listeners before you assert value.

Especially if testing code is being run on JavaFX Application thread.

Related