I have a ListView that displays some data from an API. Within my list item, I need to have two different component trees depending on the data for that row. More specifically, if the row has an associated image, I need to display the image with a label, arranged a certain way. If it does not have an image, then I want to display only a label, arranged a different way. To me, that sounds like I want to create two different components and choose which component to include, dynamically.
It currently looks something like this, in abbreviated form:
ListItem.Empty {
id: matchItem
property string team1Name
property string team2Name
property string team1Logo
property string team2Logo
width: parent.width
Item {
id: team1Info
width: parent.width*0.3
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
Item {
anchors.fill: parent
anchors.margins {
top: units.gu(2)
bottom: units.gu(2)
}
Image {
id: team1LogoImage
source: team1Logo
width: parent.width
height: units.gu(5)
fillMode: Image.PreserveAspectFit
anchors.horizontalAlignment: parent.horizontalCenter
}
Label {
text: team1Name
anchors.horizontalAlignment: Text.Center
}
}
}
// Some more elements and a repeat of the above for the second team
}
The issue is that if team1Logo
or team2Logo
is not a valid URL, such as if a team doesn't have a logo, the Image component will fail.
What I would like to do is essentially:
if (team1Logo === "") {
Label {
// Stuff to make it look good without an image
}
} else {
Image {
source: team1Logo
}
Label {
// Stuff
}
}
But as far as I know, that's not how QML works.
I've taken a look at the Loader
component, which seems like it might fit the bill, since I could use conditionals when setting the source
property on the loader, but I couldn't get it to work. Does anyone know how to achieve what I described?
Turned out to be fairly straightforward to implement a Loader
. Example:
Item {
id: team1Info
Loader {
id: team1ItemLoader
property string name: model.team1Name
property string logo: model.team1Logo
source: (logo) ? "TeamLogoItem.qml" : "TeamItem.qml"
}
}
In this example, name
and logo
then become available inside TeamLogoItem.qml
or TeamItem.qml
as properties.
The answer by @TommyBrunn only works if TeamItem.qml
does not define any property
you want to pass in. This means that:
property alias
in your componentAlternatively, you can use setSource()
for a Loader
to pass property values in to the loaded component:
// ### TeamItem.qml (and TeamLogoItem.qml, similarly)
Label {
property alias name: text
property string logo: "qrc:/images/logos/dummy.png"
}
// ### main.qml
Item {
id: team1Info
Loader {
Component.onCompleted: {
var qml = model.team1Logo ? "TeamLogoItem.qml" : "TeamItem.qml";
setSource( qml, { name:model.team1Name, logo:model.team1Logo } )
}
}
}
You could also choose to pass different property values in—e.g. not pass in a logo to TeamItem.qml—based on the QML you are loading. They do not have to have the same interface.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With