2
votes

I want to build multiple ListModel that will all start with common elements. I want to fill the remaining elements from another ListModel.

Example : ListModel (1) :

COMMON A
COMMON B
COMMON C
A1
B1
C1

ListModel (2) :

COMMON A
COMMON B
COMMON C
A2
B2
C2

Is there a way to declaratively "merge" or extend two ListModel?

So far I am doing it dynamically:

import QtQuick 2.9
import QtQuick.Window 2.2

Window {
    width: 480
    height: 320
    visible: true

    ListModel {
        id: commonModel
        ListElement { value: "COMMON A" }
        ListElement { value: "COMMON B" }
        ListElement { value: "COMMON C" }
    }

    ListModel {
        id: model1
        ListElement { value: "A1" }
        ListElement { value: "B1" }
        ListElement { value: "C1" }
    }

    ListModel {
        id: model2
        ListElement { value: "A2" }
        ListElement { value: "B2" }
        ListElement { value: "C2" }
    }

    ListView {
        anchors.fill: parent
        model: commonModel // here I would like to directly specify the "concatenated" model

        delegate: Text { text: model.value}

        // dynamic part that I want to avoid
        readonly property var additionalModel: model2
        Component.onCompleted: {
            for (var i = 0; i < additionalModel.count; ++i) {
                var elt = additionalModel.get(i)
                model.append(elt)
            }
        }
    }
}

Edit: In other words, I would like to do something as simple as this :

// MyModel.qml
ListModel {
    ListElement { value: "COMMON A" }
    ListElement { value: "COMMON B" }
    ListElement { value: "COMMON C" }
}

Being able to extend it as needed in another component:

ListView {
    model: MyModel {
        // keep original elements from MyModel and add custom "component-related" elements
        ListElement { value: "OTHER A" }
        ListElement { value: "OTHER B" }
        ListElement { value: "OTHER C" }
    }
}

But it does not seem possible since I obtain the following error:

Cannot assign to non-existent default property

1

1 Answers

3
votes

Let's begin with patching the error. It comes from here:

model: MyModel {
    ListElement { role_value: "OTHER A" }
    ListElement { role_value: "OTHER B" }
    ListElement { role_value: "OTHER C" }
}

To get this sort of behaviour, you'll need to set one of the properties of MyModel to a default property. Trivially, something like

//  MyModel.qml
ListModel {
    default property list<ListElement> otherModels

    ListElement { role_value: "COMMON A" }
    ListElement { role_value: "COMMON B" }
    ListElement { role_value: "COMMON C" }
}

//  Main.qml
import QtQuick 2.0
import QtQuick.Window 2.2

Window {
    ListView {
        model: MyModel {
            // these should be auto-assigned to the default property      
            ListElement { role_value: "OTHER A" }
            ListElement { role_value: "OTHER B" }
            ListElement { role_value: "OTHER C" }
        }
    }
}

Normally, with other types, this would work fine. But ListElement is a special case. If you try running the above, you'd probably get an error:

Cannot assign to non-existent property "role_value"

Documentation hints that ListElements can only be defined under ListModels. As far as I know, that seems to be the only feasible workaround. This means changing the default property to list<ListModel> and changing the usage to

ListView {
    model: MyModel {
        // ListModel is auto-assigned to the default property
        ListModel {
            ListElement { role_value: "OTHER A" }
            ListElement { role_value: "OTHER B" }
            ListElement { role_value: "OTHER C" }
        }
    }
}

Here's a minimal and complete example:

//  MyModel.qml
import QtQuick 2.0

ListModel {
    id: model
    default property list<ListModel> otherModels

    ListElement { role_value: "COMMON A" }
    ListElement { role_value: "COMMON B" }
    ListElement { role_value: "COMMON C" }

    //  called everytime a model is append to otherModels
    onOtherModelsChanged: {
        //  add ListElements from the last otherModels to this model
        var i = otherModels.length - 1;
        if (i < 0) return;

        for (var j = 0; j < otherModels[i].count; j++)
            model.append(otherModels[i].get(j));
    }
}


//  Main.qml
import QtQuick 2.6 
import QtQuick.Window 2.2      //  Window
import QtQuick.Controls 2.2    //  ItemDelegate

Window {
    visible: true
    width: 640
    height: 480

    ListView {
        anchors.fill: parent
        model: MyModel {
            ListModel {
                ListElement { role_value: "OTHER A" }
                ListElement { role_value: "OTHER B" }
                ListElement { role_value: "OTHER C" }
            }
        }

        delegate: ItemDelegate {
            text: role_value
            width: parent.width
        }
    }
}

See a more sophisticated demo on Github.

default property list<ListModel> and the implementation of onOtherModelsChanged allows multiple models to be "concatenated" onto the base model.

Screenshot of Success:

enter image description here

As noted by augre, in the case where only one model is ever concatenated, you can simplify the property to default property ListModel otherModel and have

onOtherModelChanged: {
    if (otherModel === undefined) return;
    for (var i = 0; i < otherModel.count; i++)
        model.append(otherModel.get(i));
}