[Solved] Not all properties from ListModel are being made available to delegate
-
Hi. I currently have a ListView with a ListModel that uses a Loader for the delegate.
in the Loader I have the following:
onLoaded: { item.modelData = model }
Every Component that the delegate can use as its source has the modelData property.
When I add the following item to the list using the insert command:storiesModel.insert(insertIndex, { elementType: "imageelement", img: imgid })
I am able to access that "img" property from inside the delegate without any issue at all, using the following:
imgId: modelData.img
However when I add an item to the list using this instead:
storiesModel.insert(insertIndex, { elementType: "gridelement", topimg: topid, leftimg: leftid, rightimg: rightid })
I don't have access to any of the properties. Funnily enough, if I add the element using "gridelement" as the elementType and "img: imgid" as one of the properties, I can still access img just fine, however topimg, leftimg and rightimg all give me the following error:
qrc:/res/qml/learningstories/PhotoGrid.qml:26:28: Unable to assign [undefined] to QStringAt first I thought it may have been due to topid, leftid, or rightid playing up, but if I manually assign the delegate properties using topid, leftid and rightid it works completely fine.
Is there a way I can print all of the properties of modelData so I can see what's actually being stored in there? Seems weird that it only stores select properties.
-
@eLim Can you post some more code ?
Is there a way I can print all of the properties of modelData so I can see what's actually being stored in there? Seems weird that it only stores select properties.
Yes in this way:
for(var prop in modelData) { // your property console.log("Property: ", prop, "Value: ", modelData[prop]) }
-
@p3c0 Hey thanks for that. It seems like my properties simply refuse to be passed into the modelData. Not really sure what's up.
I'm currently using a Loader to load a specific Component into a ListView dependent on its "elementType"ListView { id: storiesCreatorList anchors.fill: parent visible: false clip: true model: storiesModel delegate: Loader { sourceComponent: { elementList.push(this) if(elementType == coverElement) return coverPhotoScreen if(elementType == adderElement) return addElement if(elementType == textElement) return textEditorElement if(elementType == imageElement) return photoElement if(elementType == gridElement) return photoGridElement if(elementType == frameHeaderElement) return frameworkHeaderElement if(elementType == frameContentElement) return frameworkContentElement } onLoaded: { item.modelData = model } } }
One of the components looks like this:
Component { id: frameworkHeaderElement Rectangle { property var modelData: [] id: frameworkHeaderElementRect width: stories.width height: stories.height * 0.075 color: "transparent" onModelDataChanged: { for(var prop in modelData) console.log("Property: ", prop, ", Value: ", modelData[prop]) } Text { id: frameworkHeaderText anchors.left: parent.left anchors.leftMargin: parent.width * 0.05 //text: modelData.headerText <-- undefined apparently font.family: "Museo Sans" font.pixelSize: parent.height * 0.75 color: "#f26259" } } }
As you can see, the Component contains a "modelData" property for storing its information from the ListView's model. This is due to the various components having different custom properties. I add this component to the list using the following:
storiesModel.append({ elementType: "frameheaderelement", headerText: header }) //Note header here is text from the function this is called in. It is just a regular QML string, it works fine
In the component code I posted you can see that I'm printing all the modelData properties as you explained. This is what the console shows:
qml: Property: objectName , Value: qml: Property: index , Value: 2 qml: Property: model , Value: QQmlDMAbstractItemModelData(0x38595bd8) qml: Property: hasModelChildren , Value: false qml: Property: elementType , Value: frameheaderelement qml: Property: modelData , Value: frameheaderelement qml: Property: objectNameChanged , Value: function() { [code] } qml: Property: modelIndexChanged , Value: function() { [code] } qml: Property: __0 , Value: function() { [code] } qml: Property: __1 , Value: function() { [code] }
I can see "elementType" in there, but where did "headerText" go? This is really confusing me :(
Edit: Why is there a "modelData" property in my modelData property? What's going on x_x
-
@eLim The only problem in the above code that I see is that if
headerText
is not set during append and theelementType
if condition insourceComponent
as you have passed a string it should be a string in condition too.
For reference here is a code similar to yours:import QtQuick 2.0 Item { width: 100 height: 100 ListModel { id: myModel } ListView { id: listview anchors.fill: parent model: myModel delegate: Loader { sourceComponent: { if(name == "abc") return myComp1 if(name == "xyz") return myComp2 } onLoaded: item.modelData = model } } Component { id: myComp1 Text { property var modelData: [] text: modelData.name + " " + modelData.age } } Component { id: myComp2 Text { property var modelData: [] text: modelData.age + " " + modelData.name } } Component.onCompleted: { myModel.append({ name: "abc", age: 10 }) myModel.append({ name: "xyz", age: 12 }) } }
-
I've managed to solve the problem by first adding a second property to the Components, so now the Component has this at the top:
Component { id: frameworkHeaderElement Rectangle { property var modelData: [] property var propData: [] ... } ... }
Then when the Component is Loaded during the Loader I've changed the onLoaded function to the following:
onLoaded: { item.modelData = model item.propData = storiesModel.get(index) }
Now I can access my index using modelData, and I can access all of my properties (which may be unique to a Component) through propData as follows
text: propData.headerText
I'm sure there's a better way to solve this, but for the time being this works just fine for what I need.
Thanks for your help!