[SOLVED]Dynamic Binding of QML Object and Factory created C++ object
-
wrote on 16 May 2014, 06:14 last edited by
Hi all first post here. Been lurking a while though.
I have successfully wrote and registered some cpp objects to QML env:
Product
and
Products -> product 'factory' or 'repository' with Product * Products::get(QVariant id) methodI've tested these, Product creation, saving, deletion works like a charm from QML side (imperative js)
Now I'm progressing to building the UI.
this is my 1st component (simplified) - master_item.qml
@
import QtQuick 2.0
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.1Item {
anchors.fill: parent
property alias ref: ref.text
property alias name: name.textGridLayout { id: gridLayout flow: GridLayout.TopToBottom rows:4 Label { text: qsTr("REF") } Label { text: qsTr("Name") } TextField { id: ref placeholderText: qsTr("REF") } TextField { id: name placeholderText: qsTr("Name") Layout.fillWidth: true } }
}
@then on main.qml i do
@
import my.app.repositories 1.0ApplicationWindow {
TabView {
id: tabview
}Button { text: "Add Tab" anchors.top: tabview.bottom onClicked: { var component = Qt.createComponent("master_item.qml"); if (component.status == Component.Ready) { var tab = tabview.addTab("tab " + (tabview.count + 1), component); var item = products.get('CR2011W')); tab.item.ref = Qt.binding(function() { return item.ref; }); tab.item.name = Qt.binding(function() { return item.name; }); } } }
@
this worked for the first time I clicked the button(just the first tab), but subsequent calls yield Type Error.
the component loaded, but the binding does not.
What did I do wrong?My 2nd try involves embedding the Product object in the component directly:
@
import my.app.entities 1.0Item {
anchors.fill: parent
Product {
id: data
}GridLayout { id: gridLayout flow: GridLayout.TopToBottom rows:4 Label { text: qsTr("REF") } Label { text: qsTr("Name") } TextField { id: ref placeholderText: qsTr("REF") text: data.ref } TextField { id: name placeholderText: qsTr("Name") Layout.fillWidth: true text: data.name } }
}
@so in main.qml I thought it's gonna be more intuitive:
@
onClicked: {
var component = Qt.createComponent("master_item.qml");
if (component.status == Component.Ready) {
var tab = tabview.addTab("tab " + (tabview.count + 1), component);
tab.item.data = products.get('CR2011W'));
}
}
}
@Now the bindings worked for subsequent calls, but tab.item.data = products.get('ID') doesn't seem to override the component's internal data property. so it's just a blank component with blank Product object.
I've tried putting the component creation in separate js file, to no avail.
What I need basically is bind a dynamically create QML component to an (also dynamically) Factory created cpp Object.
How do I do this correctly?
Thanks.
Edit - Solution
user-accesible property of the underlying cpp object's type in component's qml
@
import my.app 1.0Item {
property Product obj
obj: Product {}TextField { id: name text: obj.name }
}
@
and in main.qml
@
import my.app 1.0ApplicationWindow {
Products { id: products }
TabView { id: tabview }Button { text: "Add Tab" anchors.top: tabview.bottom onClicked: { var component = Qt.createComponent("component.qml"); if (component.status == Component.Ready) { var tab = tabview.addTab("tab " + (tabview.count + 1), component); var obj = products.get("ID HERE"); if (tab.status == Loader.Ready) tab.item.obj = obj; else tab.loaded.connect(function() { if (tab.status == Loader.Ready) tab.item.obj = obj; }); } } }
}
@
according to the docs, local components should not be lazy loaded so in this case the component status check might be alright if omitted. -
wrote on 16 May 2014, 08:19 last edited by
I think the problem lies in Tabview.addTab() only accepting Component.
if it accepts instantiated object instead, I could probably do something like:
@
var component = Qt.createComponent("master_item.qml");
if (component.status == Component.Ready) {
var obj = component.createObject(tabview, {"data": Products.get("CR2001W")});
tabview.addTab("CR2001W", obj);
}
@but no, tabview is basically a Loader...
Any solution to this?
-
wrote on 16 May 2014, 09:12 last edited by
I got my 2nd approach working as follows:
@
import my.app.entities 1.0Item { anchors.fill: parent property Product obj obj: Product {} GridLayout { flow: GridLayout.TopToBottom rows:4 Label { text: qsTr("REF") } Label { text: qsTr("Name") } TextField { id: ref text: obj.ref } TextField { id: name text: obj.name } } }
@
and on main.qml
@
onClicked: {
var component = Qt.createComponent("master_item.qml");
if (component.status == Component.Ready) {
var tab = tabview.addTab("tab " + (tabview.count + 1), component);
tab.item.obj = products.get('CR2001W');
}
@but then I'm back to SQUARE ONE. first add it works, second adding of tab yields Type Error.
WTH am I doing wrong??
-
wrote on 16 May 2014, 09:50 last edited by
After a closer inspection after the call:
var tab = tabview.addTab(...)tab var will not be ready if the corresponding Tab isn't on focus, that is, the Tab is lazily loaded. Now if only I could assign a callback once its status is Loader.Ready...
-
wrote on 16 May 2014, 10:05 last edited by
GOT IT.
this worked:
@
onClicked: {
var component = Qt.createComponent("master_item.qml");
if (component.status == Component.Ready) {
var tab = tabview.addTab("tab " + (tabview.count + 1), component);
if (tab.status == Loader.Ready) {
tab.item.obj = products.get("CR2001W");
} else {
tab.loaded.connect(function() {
console.log("this will be executed when corresponding tab is on focus");
if (tab.status == Loader.Ready) {
tab.item.obj = products.get("CR2001W");
}
});
}
}
}
@
by extension, I should be putting callbacks on component.statusChanged as well... this gets ugly pretty fast. Refactor this on different JS file? how about my Products factory object? will it be available from js? Can I even put imports in plain js?
1/5