Reputation: 4687
I'm having a lot of trouble creating extendable components in QML. There are techniques which work but what I'd like to do is something like this:
//Outline.qml
Rectangle {
color: 'red'
//Children are inserted here
}
//main.qml
Rectangle {
...
Outline {
Text { I'm trying to inject this into the Outline component
text: "Hi I'm the inner text!"
}
}
}
I know I can achieve this by using a Loader
component and setting the sourceComponent
to a Component
instance but when I'm doing anything moderately complex (reusable dialogs for instance) my ability to write functions and reference the instance of a child component is hampered. If the component was instantiated in the same QML file the id/function relationship would be fine and I could simply reference my text fields directly.
Here's an example of the loader:
//Outline.qml
Rectangle {
id : root
color: 'red'
property Component content;
Loader {
source : root.content
}
}
//main.qml
Rectangle {
function getData(){
return inner.getData();
//ERROR inner is undefined because it is created by the Loader instance.
}
...
Outline {
content: Component {
TextField {
id: inner
function getData(){
return inner.text || 'none';
}
}
}
}
}
I would love an better way to handle it. I know I can build a Component directly inside the parent and achieve a top level reference, but it doesn't allow for the level of control I'm looking for. A 'static' extension of the component would be preferable in this case.
EDIT: here's a QML example that illustrates the problem. On click the output is:
ReferenceError: getMe is not defined
import QtQuick 2.2
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.1
Item {
id: app
width: 640
height: 480
function accessData(){
output.text = getMe.text
}
Loader {
width: parent.width
height: 300
sourceComponent: Component {
Rectangle {
anchors.fill: parent
color: 'yellow'
TextField {
anchors.centerIn: parent
id: getMe
placeholderText: 'Input data...'
}
}
}
}
Button {
id: button
y: app.height - 50
text: 'get data'
onClicked: {
accessData();
}
}
Text {
y: app.height - button.height
id: output
text: 'none'
}
}
As you can see when you use the "loader" method the sourceComponent isn't part of the same tree (you can't access the inner data).
Upvotes: 3
Views: 7769
Reputation: 879
This is a working solution for your illustrative example of the problem and later try it in your project
give an id to your Loader ex:" id: yourLoader " and the rectangle inside the loader ex: " id: yourRect "
add a String property to yourRect which will hold your TextField.text
Loader { id: yourLoader
width: parent.width
height: 300
sourceComponent: Component {
Rectangle {
property string yourText : getMe.text
id: yourRect
anchors.fill: parent
color: 'yellow'
TextField {
anchors.centerIn: parent
id: getMe
placeholderText: 'Input data...'
}
}
}
}
we change your function accessData
function accessData(){
var rect= yourLoader.item //we get yourRect from the loader
output.text = rect.yourText // yourText <-- getMe.text
}
Upvotes: 2