I have a use case where depending on the presence or absence of a property value an object referencing it is either created or removed. I am using a Loader
for the purpose, where the active
property is bound to the property, that is when the property has a non-null value the loader is activated, when it is set to null it is deactivated.
However the problem is that the loader doesn't release its item immediately, so for a moment the item references a null property, thus is unable to access data, while the setting of the property to null triggers reevaluations that result in a swarm of cannot read property x of null
.
Simple logic suggest this should not happen. So I thought that maybe the problem is the order of binding evaluations is wrong, resulting in the item's bindings being evaluated before the loader is deactivated. So I tried removing the binding for active
and setting it up manually. The problem however persisted.
So here is a minimal representation to illustrate what's going on:
Window {
id: main
visible: true
width: 500
height: 300
property QtObject object : QtObject {
property QtObject subObject: null
}
QtObject {
id: subo
property int i : 1
}
Loader {
id: ld
active: false
sourceComponent: Text {
text: object.subObject.i
font.pointSize: 20
}
}
MouseArea {
anchors.fill: parent
onClicked: {
if (object.subObject) {
ld.active = false
object.subObject = null
} else {
object.subObject = subo
ld.active = true
}
}
}
}
Note that in this case the loader is explicitly deactivated before the property is set to null
, yet nonetheless, every time that this happens I get a type error in the console:
qrc:/main.qml:25: TypeError: Cannot read property 'i' of null
This doesn't seem like correct behavior. So maybe a bug? Or am I missing something? Any suggestions on how to work around that limitation? Note that this is somehow avoided when using views or repeaters.
Update: To clarify further, in my actual production code the loader's item cannot really exist without referencing that property. So the idea was that the object should only be created when the property has a value other than null
and be destroyed when it is null
.
I came across this yesterday. I need two Loaders which share the same property. In C++ class, I update the two Loader's active property one by one, which will also lead to that problem. My solution for this is using a Binding and do some setting like this:
Actually maybe this will not help you, but this is the solution in my code. I hope you will see it and have a try at
binding
. Wish you success. here is the link to my project: https://github.com/begoat/qmlLiveIt's not really a bug, but more like a technical limitation. A slot must not delete the sender of the connected signal. It must use
deleteLater()
to avoid that the execution returns to a deleted object. Essentially for the same reason,Loader
cannot immediately delete the loaded item, because theactive
property might be bound to something that is controlled from within the loaded hierarchy of items.Even though it wasn't new to me that QML objects'
destroy()
is adeleteLater()
and the latter doesn't destroy the object until the next event loop cycle, I expected such objects to stop processing events after that point.It seems the object keeps on living for a while, and its bindings keep on being evaluated, failing to resolve because unlike the object destruction, the property setting happens instantaneously.
And since it wasn't an option to ever have
null
in the item, I managed to work-around the issue by having a global dummy data object and using a conditional binding:This seems to work, providing dummy data to the object in its final moments.
You can also use
Connections
to connect to theLoader
'sonActiveChanged
-event and break the binding there: