QML How to call method of a model from the delegate

Viewed 1071

I have a simple QML program which has one ListView. ListView's model and delegate are defined in a separate QML files.

//Main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
//import TheModel.qml
//import TheDelegate.qml


Window {
    id: window
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    ListView {
        anchors.fill: parent
        model: theModel
        delegate: theDelegate
        focus: true

    }
    Button{
        x: 394
        y: 257
        text: "press me"
       onPressed: theModel.append({"color":"black", "cost": 5.95, "name":"Pizza"})
   }

    TheDelegate{
        id: theDelegate
    }

    TheModel{
        id:theModel
    }
}

then the model file

//TheModel.qml
import QtQuick 2.0

ListModel{
    ListElement {
        color:"red";
        name: "Bill Smith"
        number: "555 3264"
    }
    ListElement {
        color:"blue";
        name: "John Brown"
        number: "555 8426"
    }
    ListElement {
        color:"green";
        name: "Sam Wise"
        number: "555 0473"
    }
}

and finally the delegate

    //TheDelegate.qml
    import QtQuick 2.0
    
    Component {
        Rectangle{
            color: model.color
            width: 100
            height: 100
            MouseArea{
                anchors.fill: parent
                onPressed: model.append({"color":"black", "cost": 5.95, "name":"Pizza"})
            }
        }
    }

if I click on delegate's MouseArea the onPressed method will need to create one ListItem, but the issue is that I cannot access the model's function from delegate. what is confusing though, that properties are being accessed in delegate through model. can anyone point out on the right way of doing this, say if I know that the model is a ListModel and it has append method, but delegate doesn't know that, is there a way to cast model to known type then call a method of it?

2 Answers

This can be done by adding a signal to TheDelegate, which you can connect in the scope where theModel is available. I'd like to call this 'contained components', there is probably some fancy term for it ;-)

Please change TheDelegate.qml to (btw, I also changed it to not be a Component, for reusability):

import QtQuick 2.0

Rectangle{
    color: model.color
    width: 100
    height: 100

    signal appendRequested(var newItem)

    MouseArea{
        anchors.fill: parent
        onPressed: appendRequested({"color":"black", "cost": 5.95, "name":"Pizza"})
    }
}

Then in main.qml you can use it as following:

ListView {
    anchors.fill: parent
    model: theModel
    delegate: theDelegate

    focus: true
}

Component {
    id: theDelegate

    TheDelegate {
        onAppendRequested: theModel.append(newItem)
    }
}

The model object is exposed to each delegate in a view, and it provides the model data for that particular delegate. It is not the same as the ListView's model property.

@Amfasis' approach is nice, because it will work with any type of model and view.

If you don't mind tying the delegate to ListView, you can use the attached ListView API:

Component {
    Rectangle{
        id: root
        color: model.color
        width: 100
        height: 100
        MouseArea{
            anchors.fill: parent
            onPressed: root.ListView.view.model.append({"color":"black", "cost": 5.95, "name":"Pizza"})
        }
    }
}
Related