1

I have a ListModel that is dynamically populated. I want to select/switch delegate for one of the elements in the model (All other elements should be viewed with another delegate), is that possible?

My QML model has two loaded component delegates. I want that when the "key" of listElement matches a value ("link"), the delegate is set to one of the components. I do that by setting the listView.delegate; The problem is that the selection seems to work, but it changes the delegate recursively for all elements including already loaded elements, I end up with only one delegate being loaded for the listView.

I have seen many solutions in SO, but none matches my use case, for the most part this post seems similar, but it loads the delegate component dynamically and I am not sure if it even works, I don't want to end up loading components dynamically. Here is my QML code including how I populate the model and select the delegate:

import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import QtQuick.Window 2.3

Window {
    visible: true
    id: mainQML
    width: 640
    height: 480

    title: qsTr("Offers View")
    Item {
        id: offersPage

        ListModel{
            id: barcdeLockupOffers
            // model will be dynamically populated.
            // Depending on value of key , I want to select listView delegete.
            ListElement {
                key: ""
                value: ""
            }
        }
        Component {
            id: offersTextDelegate
            Rectangle {
                id: offersRect
                anchors { left: parent.left; right: parent.right}
                height: offersColumn.implicitHeight + 4
                border.width: 1
                border.color: "lightsteelblue"
                radius: 2
                Column {
                    id: offersColumn
                    //Text { text: "Merchent: " + merchant ; property bool useStyle: true}
                    Text {
                        id: offerdata
                        text: '<font color="blue" size=14> <b>' + key +':' + '</b> </font>'  + value + '\n\n';
                        width: mainQML.width - 40
                        height: 30
                        wrapMode: Text.Wrap
                    }
                }
            }
        }
        Component {
            id: offersHTTPDelegate
            Rectangle {
                id: offersRect2
                anchors { left: parent.left; right: parent.right}
                height: offersColumn2.implicitHeight + 4
                border.width: 1
                border.color: "lightsteelblue"
                radius: 2
                Column {
                    id: offersColumn2
                    Text {
                        text: "<a href=" + value + ">" + "Click here for Offer details" + "</a>"
                        color: "blue"
                        property bool useStyle: true
                        MouseArea{
                            anchors.fill: parent
                            onClicked: {
                                parent.color="grey"
                                Qt.openUrlExternally(value)
                            }
                        }
                    }
                }
            }
        }

        ListView {
            id: offersView
            anchors.top: parent.top
            height: mainQML.height - 100
            anchors.margins: 10
            model: barcdeLockupOffers
        }
    }
    Component.onCompleted: {
        barcdeLockupOffers.clear();
        var listModelKeys= ["Title", "Price", "Condition", "Avalability", "link"]
        var listModelValues = ["Coffee Machine", "14", "New", "Available", "https://www.amazon.com/Nespresso-VertuoPlus-Coffee-Espresso-DeLonghi/dp/B01NBJ2UT5/ref=sr_1_1_sspa?s=home-garden&ie=UTF8&qid=1527543544&sr=1-1-spons&keywords=coffee+maker&psc=1"]

        for (var x = 0 ; x < listModelKeys.length ; x++){
            barcdeLockupOffers.append({key: listModelKeys[x] , value: listModelValues[x]})
            //console.log("Key = : " +  listModelKeys[x])
            if (listModelKeys[x] === "link")
                offersView.delegate = offersHTTPDelegate
            else
                offersView.delegate = offersTextDelegate
        }
    }
}
Mohammad Kanan
  • 4,452
  • 10
  • 23
  • 47

1 Answers1

2

As you know, delegate is common to all of items, you cannot assign different delegate for specified item. The simplest way is to use Loader, for example:

import QtQuick 2.10
import QtQuick.Window 2.2

Window {
    visible: true
    width:480
    height: 640
    title: qsTr("Test")

    Component {
        id: type1
        Rectangle {
            color: "yellow"
            anchors.fill: parent
        }
    }
    Component {
        id: type2
        Rectangle {
            color: "orange"
            anchors.fill: parent
        }
    }

    ListView {
        anchors.fill: parent
        model: 10
        delegate: Loader {
            sourceComponent: index % 2 ? type1 : type2
            height: 30
            width: parent.width
        }
    }
}
folibis
  • 12,048
  • 6
  • 54
  • 97
  • Thanks folibis. This is strange! `Loader` is breaking the role and able to alternate the delegate, or this is effectively the result. – Mohammad Kanan May 29 '18 at 10:59
  • 1
    ListView.delegate is a `Component` and so the ListView anyway instantiates items when needed. So you just add a small overload when ListView instantiates `Loader` which, in turn instantiates the item itself. The Qt docs says: _If at all possible, place functionality that is not needed for the normal display of the delegate in a Loader which can load additional components when needed_. – folibis May 29 '18 at 11:14