tanghus
tanghus

Reputation: 55

QML: Update a nested ListView

I'm making a TV-Guide on Sailfish OS, and have met an - for now - obstacle. As I want to have the possiblity to mark each entry for a customized list I need to modify the model behind. I have tried modifying the model directly: model.favorite = true but that doesn't work. I have tried to modify the underlying arrayOfObjects, but that isn't reflected in the UI, and I can't trigger an update because I can't access the ListView. I have tried to make a customized model, but since I can't reference it's instance, to no avail.

Below is a very simplified representation of the layout using mostly basic QML.

Page {
    Flickable {
        Column {
            SlideshowView { // PathView
                model: arrayOfObjects
                delegate: channelDelegate
            }
        }
    }

    Component {
        id: channelDelegate
        ListView {
            id: channelList
            // ProgramModel just iterates thru arrayOfObjects and appends them
            model: ProgramModel {
                programs: arrayOfObjects
            }
            delegate: programDelegate
            Component.onCompleted: {
                // I can't reference channelList from here.
            }
        }
    }

    Component {
        id: programDelegate
        ListItem {
            Button {
                onClicked: {
                    // How do I reference channelList?
                    // Doing it by name doesn't work.
                }
            }
        }
    }
}

I have tried calling ApplicationWindow (which works), to send a signal that I connect to in channelList.onCompleted (which also works), but since I can't reference the list from there it doesn't help.

I'm on QT 5.6 so some solutions may not work. And I would really prefer keeping it pure QML; no C++.

Upvotes: 0

Views: 1033

Answers (2)

tanghus
tanghus

Reputation: 55

Because the SlideshowView replaces the focus in DaszuOne's answer, i.e. switches slide, list.model = listModel causes the entire view to change. Changing a single entry in the model, doesn't trigger an update, so it would require even more code to make that happen. So after considering back and forth, I decided that doing it "the right way" would be more cumbersome and hard to read, than doing it the easy way.

I made a very simple wrapper, that can use what kind of persistent storage you use, having only two methods: setFavorite() and isFavorite.

In the delegate I update the property in onCompleted so it will be called every time the element needs to be rendered:

    Component.onCompleted: {
        favorite = favorites.isFavorite(model.channelId, model.id)
    }

My conclusion is, that DaszuOne's answer is the correct answer - but not in this case ;)

Thank you for all your help, it made me understand QML better.

Upvotes: 0

DaszuOne
DaszuOne

Reputation: 809

I can't reproduce your case exactly, but maybe this quick example help you somehow.

import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Controls 2.2

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

    signal updateModel(int id, string name)

    Component {
        id: listWrapper

        ListView {
            id: list

            model: [
               { name: "name0" },
               { name: "name1" },
               { name: "name2" }
           ]

            delegate: Loader {
                width: parent.width
                property variant model: modelData
                property int modelIndex: index
                sourceComponent: listDelegate
            }

            function updateModelName(id, name) {
                var listModel = list.model
                listModel[id].name = name
                list.model = listModel
            }
        }
    }

    Loader {
        id: loadedList
        x: 0
        anchors.fill: parent
        sourceComponent: listWrapper

        Component.onCompleted: {
            mainWrapper.updateModel.connect(loadedList.item.updateModelName)
        }
    }

    Component {
        id: listDelegate

        Rectangle {
            id: listRow
            width: 200
            height: 80
            Text {
                text: model.name
            }

            Button {
                x: 100
                text: "update"
                onClicked: {
                    mainWrapper.updateModel(modelIndex, "better_name"+modelIndex)
                }
            }
        }
    }
}

The clue of this example is to use signal and function to update model.

Upvotes: 1

Related Questions