How to create dynamic bindings in ListModel?

95 Views Asked by At

In small example below, I'm trying to create rectangles whose width depends on foo. As we all know, we cannot use property binding in ListElement because ListElement doesn't allow to use script for property value.

import QtQuick 2.0
import QtQuick.Controls

Window {
    width: 400
    height: 300
    visible: true
    property int foo: 10

    ListModel {
        id: listModel
        // ListElement {
        //     width: foo // error: ListElement: cannot use script for property value
        // }
    }

    Row {
        anchors.top: parent.top
        Repeater {
            id: repeater
            model: listModel

            delegate: Rectangle {
                width: model.width
                height: 50
                color: "green"
            }
        }
    }
    Button {
        text: "add item"
        anchors.top: parent.top
        anchors.horizontalCenter: parent.horizontalCenter
        onClicked: {
            listModel.append({
                                 "width": foo // works, but is not a binding
                             })
        }
    }
}

In order to use script, I have to try to append in function. However, in this way, if foo is changed somewhere, the width of rectangles won't change accordingly. So anyone knows a better solution?

3

There are 3 best solutions below

0
JarMan On

You can update a ListModel when your data has changed. You can do something like this:

ListModel
{
    id: listModel
}

onFooChanged:
{
    var index = // The index of the element you want to modify

    // Update element with new value of foo
    listModel.set(index, {"width": foo});
}

It's definitely clunkier than a direct binding within the ListElement, but it works.

1
Kamichanw On

I finally tried my best to find out the most elegant way to implement dynamic property binding for ListModel

// other code...
    Button {
        text: "add item"
        anchors.top: parent.top
        anchors.horizontalCenter: parent.horizontalCenter
        onClicked: {
            listModel.append({
                                 "width": foo
                             })
            listModel.get(0).width = Qt.binding(() => return foo)
        }
    }
// other code...

Try this code here

3
Stephen Quan On

A hack would be to add {"width":"foo"} then you use width: eval(model.width) width: page[mode.width], e.g.

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
    id: page
    property alias foo: slider.value
    ListModel { id: listModel }
    ColumnLayout {
        anchors.centerIn: parent
        ListView {
            Layout.preferredWidth: contentItem.childrenRect.width
            Layout.preferredHeight: contentItem.childrenRect.height
            orientation: ListView.Horizontal
            model: listModel
            delegate: Rectangle {
                width: page[model.width]
                height: 50
                color: "green"
            }
        }
        Button {
            text: "add item"
            onClicked: listModel.append({"width": "foo"})
        }
        Slider { id: slider; from: 10; to: 100; }
    }
}

You can Try it Online!