Search code examples
qtqmllistmodel

how to implement nested listmodels in qml


Is it possible to implement 3 ListModels (one inside the other) and if yes how could I do it? The 3 ListModels are for hour day and month accordingly.In other words I want one model for hour inside the model for day inside the model for month and I am going to use them in nested ListView s (3 of them ) to display the hour the day and the month in a calendar. I have made a try below :

   ListModel{
      id:monthlistModel
      ListElement {
                  monthName:0
                  daylistModel:[
                      ListElement  {
                          day:0
                          hourlistModel: [
                            ListElement{ hour:0;notes:"" }
                          ]
                      }
                  ]
       }
        ListElement {
                  monthName:1
                  daylistModel:[
                      ListElement  {
                          day:1
                          hourlistModel: [
                            ListElement{ hour:1;notes:"" }
                          ]
                      }
                  ]
       }

but I could not finish it . Moreover I have some typeerror issues when I am running my code.The hourlistModel insists to be undefined for my nested listview and I dont no why. Anyway back to my question , how can I go on with the above listmodel to display 24 hours , 31 days and 12 months ?


Solution

  • I suggest doing this imperatively with javascript rather than declaratively in QML, as it can be more dynamic and brief. One downside is that this is not well documented in my experience.

    If you append an array to a ListModel, all of the array elements are converted into ListElements. Further than this, if an array is appended, and that array has nested arrays inside of it, the nested arrays are automatically converted to nested ListModels inside.

    Here is a full example:

    import QtQuick 2.15
    import QtQuick.Window 2.0
    import QtQuick.Controls 2.15
    import QtQuick.Layouts 1.12
    
    Window {
        visible: true
        width: 1000
        height: 600
    
        ListModel {
            id: monthsModel
    
            Component.onCompleted: {
                let months = [
                        {
                            name: "January",
                            days: createDays(31) // returns an array, nested arrays become nested ListModels when appended
                        },
                        {
                            name: "February",
                            days: createDays(28)
                        },
                        // add more months etc.
                    ]
                append(months) // appending a whole array makes each index into a ListElement at the top level
            }
    
            function createDays(dayCount) {
                let days = []
    
                for (let i = 0; i < dayCount; i++) {
                    days.push({
                                  day: i + 1,
                                  hours: createHours()
                              }
                              )
                }
                return days
            }
    
            function createHours() {
                let hours = []
                for (let i = 0; i < 24; i++) {
                    hours.push({
                                   hour: i,
                                   notes: ""
                               }
                               )
                }
                return hours
            }
        }
    
        // Visual example code starts here ///////////////
    
        ColumnLayout {
            id: monthsColumn
    
            Repeater {
                model: monthsModel
    
                delegate: Rectangle {
                    id: month
                    color: "pink"
                    implicitWidth: daysRow.implicitWidth + 10
                    implicitHeight: daysRow.implicitHeight + 10
    
                    RowLayout {
                        id: daysRow
                        anchors {
                            centerIn: parent
                        }
    
                        Text {
                            text: model.name
                        }
    
                        Repeater {
                            model: days // refers to the "days" entry in monthsModel.get(<monthIndex>)
    
                            delegate: Rectangle {
                                id: day
                                color: "orange"
                                implicitWidth: hoursColumn.implicitWidth + 10
                                implicitHeight: hoursColumn.implicitHeight + 10
    
                                ColumnLayout {
                                    id: hoursColumn
                                    anchors {
                                        centerIn: parent
                                    }
    
                                    Text {
                                        text: model.day
                                    }
    
                                    Repeater {
                                        model: hours // refers to the "hours" entry in monthsModel.get(<monthIndex>).get(<dayIndex>)
    
                                        delegate: Rectangle {
                                            id: hour
                                            color: "yellow"
                                            implicitHeight: 5
                                            implicitWidth: 5
    
                                            // do something here with model.notes for each hour
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    

    The output of this shows months in pink, days in orange, and hours in yellow:

    months in pink, days in orange, and hours in yellow