Reputation: 146
I have an Item
with a property. This property contains an array of JavaScript objects wich in turn contain other properties.
When I set binding for one of object's properties to some variable and its (variable) value changes triggering the binding then all properties in the array are reset to their initial values. I've created a small demo to show the problem.
C++ code:
// main.cpp
#include <QGuiApplication>
#include <QQuickWindow>
#include <QQmlApplicationEngine>
#include <QQmlContext>
class Test : public QObject
{
Q_OBJECT
Q_PROPERTY(QString value READ value NOTIFY valueChanged)
public:
Test(QObject* parent = 0) : QObject(parent) {}
QString value() const { return ""; }
Q_SIGNAL void valueChanged();
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
Test test;
engine.rootContext()->setContextProperty("__test__", &test);
engine.load(QUrl(QStringLiteral("qrc:/ui/main.qml")));
return app.exec();
}
#include "main.moc"
QML code:
// main.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
ApplicationWindow {
id: container
visible: true
width: 640
height: 480
property int clicksCounter: 0
Item {
id: testObject
property var myArray : [{
name : "CustomName" + __test__.value,
boolFlag : false
}]
}
Rectangle {
x: 10
y: 10
width: 100
height: 100
color: "red"
MouseArea {
anchors.fill: parent
onClicked: {
container.clicksCounter++
console.log("CLICK #" + container.clicksCounter + "[RED SQUARE] : Set testObject.myArray[0] to TRUE\n")
testObject.myArray[0].boolFlag = true
console.log("CLICK #" + container.clicksCounter + "[RED SQUARE] : DONE\n")
}
}
}
Rectangle {
x: 120
y: 10
width: 100
height: 100
color: "blue"
MouseArea {
anchors.fill: parent
onClicked: {
container.clicksCounter++
console.log("CLICK #" + container.clicksCounter + "[BLUE SQUARE] : Triggering notify by calling C++ <Test::valueChanged> method \n")
console.log("CLICK #" + container.clicksCounter + "[BLUE SQUARE] : [BEFORE] testObject.myArray[0].name: " + testObject.myArray[0].name + ', testObject.myArray[0].boolFlag: ' + testObject.myArray[0].boolFlag)
__test__.valueChanged()
console.log("CLICK #" + container.clicksCounter + "[BLUE SQUARE] : [AFTER] testObject.myArray[0].name: " + testObject.myArray[0].name + ', testObject.myArray[0].boolFlag: ' + testObject.myArray[0].boolFlag)
}
}
}
}
Here is what I get:
qml: CLICK #1[RED SQUARE] : Set testObject.myArray[0] to TRUE qml: CLICK #1[RED SQUARE] : DONE
qml: CLICK #2[BLUE SQUARE] : Triggering notify by calling C++ <Test::valueChanged> method
qml: CLICK #2[BLUE SQUARE] : [BEFORE] testObject.myArray[0].name: CustomName, testObject.myArray[0].boolFlag: true qml: CLICK #2[BLUE SQUARE] : [AFTER] testObject.myArray[0].name: CustomName, testObject.myArray[0].boolFlag: false
So what happens here is that after I set testObject.myArray[0].boolFlag
from false
to true
and call test.valueChanged()
method, my flag automatically resets to its initial value. Same goes for any other type used - int
, string
, etc.
Why does this happen?
Visual Studio has update 4 installed.
Upvotes: 3
Views: 647
Reputation: 8973
Very tricky, but this is intended behavior of QML. What you're suffering from is a conflict in binding assignment vs. procedural assignment, which is a common pitfall of QML.
First, you've set up a binding to myArray
that (in effect) says that any time __test__.value
changes, re-assign a literal array to myArray
. And later you subvert that binding by procedurally modifying that array. Remember that the literal array is really JavaScript code that's being evaluated every time a signal fires in the binding.
So, what you're seeing in the last console.log is not that the bool has been reverted, but instead that the entire myArray has been reset to a new value, rebuilt from the binding.
What you really want is a new bool property that you can assign to, and your literal array gets bound to this property. Like this:
property bool myBoolFlag: false
property var myArray : [{
name : "CustomName" + __test__.value,
boolFlag : myBoolFlag
}]
Then, when you assign to myBoolFlag
the array gets rebuilt. And when you fire valueChanged
the array also gets rebuilt, but this time the bool value is pulled from the property and has the right value.
Upvotes: 1