[Solved] Not all properties from ListModel are being made available to delegate



  • Hi. I currently have a ListView with a ListModel that uses a Loader for the delegate.

    in the Loader I have the following:

    onLoaded: {
        item.modelData = model
    }
    

    Every Component that the delegate can use as its source has the modelData property.
    When I add the following item to the list using the insert command:

     storiesModel.insert(insertIndex, { elementType: "imageelement", img: imgid })
    

    I am able to access that "img" property from inside the delegate without any issue at all, using the following:

     imgId: modelData.img
    

    However when I add an item to the list using this instead:

    storiesModel.insert(insertIndex, { elementType: "gridelement", topimg: topid, leftimg: leftid, rightimg: rightid })
    

    I don't have access to any of the properties. Funnily enough, if I add the element using "gridelement" as the elementType and "img: imgid" as one of the properties, I can still access img just fine, however topimg, leftimg and rightimg all give me the following error:
    qrc:/res/qml/learningstories/PhotoGrid.qml:26:28: Unable to assign [undefined] to QString

    At first I thought it may have been due to topid, leftid, or rightid playing up, but if I manually assign the delegate properties using topid, leftid and rightid it works completely fine.

    Is there a way I can print all of the properties of modelData so I can see what's actually being stored in there? Seems weird that it only stores select properties.


  • Moderators

    @eLim Can you post some more code ?

    Is there a way I can print all of the properties of modelData so I can see what's actually being stored in there? Seems weird that it only stores select properties.

    Yes in this way:

    for(var prop in modelData) { // your property
        console.log("Property: ", prop, "Value: ", modelData[prop])
    }
    


  • @p3c0 Hey thanks for that. It seems like my properties simply refuse to be passed into the modelData. Not really sure what's up.
    I'm currently using a Loader to load a specific Component into a ListView dependent on its "elementType"

    ListView {
        id: storiesCreatorList
        anchors.fill: parent
        visible: false
        clip: true
    
        model: storiesModel
        delegate:
            Loader {
                sourceComponent: {
                    elementList.push(this)
    
                    if(elementType == coverElement)
                        return coverPhotoScreen
    
                    if(elementType == adderElement)
                        return addElement
    
                    if(elementType == textElement)
                        return textEditorElement
    
                    if(elementType == imageElement)
                        return photoElement
    
                    if(elementType == gridElement)
                        return photoGridElement
    
                    if(elementType == frameHeaderElement)
                        return frameworkHeaderElement
    
                    if(elementType == frameContentElement)
                        return frameworkContentElement
                }
    
                onLoaded: {
                    item.modelData = model
                }
            }
    }
    

    One of the components looks like this:

    Component {
        id: frameworkHeaderElement
    
        Rectangle {
            property var modelData: []
    
            id: frameworkHeaderElementRect
            width: stories.width
            height: stories.height * 0.075
            color: "transparent"
    
            onModelDataChanged: {
                for(var prop in modelData)
                    console.log("Property: ", prop, ", Value: ", modelData[prop])
            }
    
            Text {
                id: frameworkHeaderText
                anchors.left: parent.left
                anchors.leftMargin: parent.width * 0.05
    
                //text: modelData.headerText <-- undefined apparently
                font.family: "Museo Sans"
                font.pixelSize: parent.height * 0.75
                color: "#f26259"
            }
        }
    }
    

    As you can see, the Component contains a "modelData" property for storing its information from the ListView's model. This is due to the various components having different custom properties. I add this component to the list using the following:

    storiesModel.append({ elementType: "frameheaderelement", headerText: header }) //Note header here is text from the function this is called in. It is just a regular QML string, it works fine
    

    In the component code I posted you can see that I'm printing all the modelData properties as you explained. This is what the console shows:

    qml: Property:  objectName , Value:  
    qml: Property:  index , Value:  2
    qml: Property:  model , Value:  QQmlDMAbstractItemModelData(0x38595bd8)
    qml: Property:  hasModelChildren , Value:  false
    qml: Property:  elementType , Value:  frameheaderelement
    qml: Property:  modelData , Value:  frameheaderelement
    qml: Property:  objectNameChanged , Value:  function() { [code] }
    qml: Property:  modelIndexChanged , Value:  function() { [code] }
    qml: Property:  __0 , Value:  function() { [code] }
    qml: Property:  __1 , Value:  function() { [code] }
    

    I can see "elementType" in there, but where did "headerText" go? This is really confusing me :(

    Edit: Why is there a "modelData" property in my modelData property? What's going on x_x


  • Moderators

    @eLim The only problem in the above code that I see is that if headerText is not set during append and the elementType if condition in sourceComponent as you have passed a string it should be a string in condition too.
    For reference here is a code similar to yours:

    import QtQuick 2.0
    
    Item {
        width: 100
        height: 100
    
        ListModel {
            id: myModel
        }
    
        ListView {
            id: listview
            anchors.fill: parent
            model: myModel
            delegate: Loader {
                sourceComponent: {
                    if(name == "abc")
                        return myComp1
                    if(name == "xyz")
                        return myComp2
                }
                onLoaded: item.modelData = model
            }
        }
    
        Component {
            id: myComp1
            Text {
                property var modelData: []
                text: modelData.name + " " + modelData.age
            }
        }
    
        Component {
            id: myComp2
            Text {
                property var modelData: []
                text: modelData.age + " " + modelData.name
            }
        }
    
        Component.onCompleted: {
            myModel.append({ name: "abc", age: 10 })
            myModel.append({ name: "xyz", age: 12 })
        }
    }
    
    


  • I've managed to solve the problem by first adding a second property to the Components, so now the Component has this at the top:

     Component {
        id: frameworkHeaderElement
    
        Rectangle {
            property var modelData: []
            property var propData: []
            ...
        }
        ...
     }
    

    Then when the Component is Loaded during the Loader I've changed the onLoaded function to the following:

    onLoaded: {
        item.modelData = model
        item.propData = storiesModel.get(index)
    }
    

    Now I can access my index using modelData, and I can access all of my properties (which may be unique to a Component) through propData as follows

    text: propData.headerText
    

    I'm sure there's a better way to solve this, but for the time being this works just fine for what I need.

    Thanks for your help!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.