QML model updating without binding loops / correct behavior
-
Hey,
Suppose I I have the following model:
ListModel { id: actionModel ListElement { name: "Check 1" text: "Performing Check 1..." check: function () {console.log("check1"); return true} status: "waiting" } ListElement { name: "Check 2" text: "Performing Check 2... " check: function () {console.log("check2"); return false} status: "waiting" } ListElement { name: "Check 2" text: "Performing Check 3..." check: function () {console.log("check3"); return false} status: "waiting" } }
to be displayed by a ListView and the following delegate
Component { id: actionDelegate Item { width: parent.width height: 40 anchors.horizontalCenter: parent.horizontalCenter property string myStatus: model.status property int myIndex: model.index property string myText: model.text function updateStatus(index, newStatus) { modelsContainer.actionModel.setProperty(index, "status", newStatus); } onMyStatusChanged: { if (myStatus === "waiting") { state = "waiting"; } else if (myStatus === "running") { state = "running"; var result = model.check(); if (result) { state = "passed" Qt.callLater(updateStatus, myIndex, "passed")
// updateStatus(updateStatus, myIndex, "passed")
}
else {
state = "failed"
Qt.callLater(updateStatus, myIndex, "failed")
}if (result && myIndex < (modelsContainer.actionModel.count - 1)) { Qt.callLater(updateStatus, myIndex + 1, "running"); } } else if (myStatus === "passed") { state = "passed"; } else if (myStatus === "failed") { state = "failed"; } } states: [ State { name: "waiting" PropertyChanges { target: myTextField color: "grey" text: "waiting" } // Define properties and behaviors for 'waiting' state }, State { name: "running" PropertyChanges { target: myTextField color: "blue" text: "running" } // Define properties and behaviors for 'running' state }, State { name: "passed" PropertyChanges { target: myTextField color: "green" text: "passed" } // Define properties and behaviors for 'passed' state }, State { name: "failed" PropertyChanges { target: myTextField color: "red" text: "failed" } // Define properties and behaviors for 'failed' state } ] MyTextField { id: myTextField text: myText color: "grey" } } }.
The status field/role/whatever of the ListElement is bound to a local property called myStatus through:
property string myStatus: model.status.
The intended semantics are: display the list in a waiting status. Trigger a series of checks depending on whether the previous check has completed. On pass check trigger the next. On fail stop everything.
Now an issue appears:
Writing an onMyStatusHandler results in a binding loop, if I try to to update the model synchronously when some change happens because the handler itself is also modifying the model.I guess this is a result of trying to place the "on initial trigger logic, do the test and if that passes move to the next" inside some handler. Although offloading this to a separate function will likely also trigger a binding loop...
The intended unidirectional semantics would be:
1)capture the change
2) while handling the change break the binding(?)
3) calculate the new state <- this step must not trigger a new Change handler
4) store the new state and restore the binding.A temporary hack is to try to (1) modify the local state on model change (2) use Qt.callLater to update the model after the handler without triggering a loop. But the solution feels hacky.
Is there a recommended API and/or solution for this problem?