QML: Functions inside var-array model become undefined after switching Repeater model
-
Title:
QML: JS functions in var-array model disappear when switching models in Repeater
Post:
I'm running into a strange issue in QML when using a
Repeaterwith a JavaScript array model that contains objects with function properties.When the
Repeateruses a plain JavaScript array (property var), the functions are preserved and callable from the delegate.
But when I switch the model to another one (even anotherproperty vararray), or mix it with aListModel, the function properties becomeundefinedinside the delegate.
Expected behavior
modelData.handlershould remain a valid JavaScript function inside the delegate, as long as the model contains JS objects with function members.
Actual behavior
Inside the delegate:
console.log(typeof modelData.handler)prints:
undefinedThe same model works correctly when used directly, but breaks when assigned dynamically using an expression like:
model: condition ? actionsModel : defaultActionsModel
Minimal reproducible example
main.qml
import QtQuick 2.15 Item { id: root width: 400 height: 200 // Works when used alone property var actionsModel: [ { title: "Action A", handler: function() { console.log("A run") } } ] // Switching to this causes functions to disappear property var defaultActionsModel: [ { title: "Default", handler: function() { console.log("Default run") } } ] property bool useDefault: false Repeater { id: rep model: root.useDefault ? root.defaultActionsModel : root.actionsModel delegate: Rectangle { width: 120; height: 40; color: "lightgray" MouseArea { anchors.fill: parent onClicked: { console.log("typeof handler:", typeof modelData.handler) if (modelData.handler) modelData.handler() } } } } Timer { running: true; interval: 1500; repeat: false onTriggered: root.useDefault = true } }After the model switches,
typeof modelData.handlerbecomes"undefined".
What I have tried
- Using
indexinstead ofmodelData→ same result - Removing bindings and animations → no effect
- Ensuring both models are
property varJS arrays (notListModel) - If I never switch the model, functions always work
Environment
- Qt 5.15.2 (also tested on 5.12 and 6.2)
- Windows 10
- qmake build, Release
Question
Is this a known limitation of how QML handles dynamic model assignment?
Why does switching between two JS array models cause function properties to becomeundefinedinside the delegate?Is there a reliable way to use JS functions inside model objects when the model is selected dynamically?
Additional note:
I’ve already gone through several potential explanations (including those
suggested by AI tools), but none of them addressed the actual underlying
issue.Therefore, I’m specifically looking for an answer grounded in QML internals
or model handling mechanisms, rather than speculative suggestions. - Using
-
In QML, the model of a Repeater is always treated as an item model (conceptually similar to a ListModel).
Even when you pass a plain JavaScript array (property var), QML internally wraps it into a list model so that the delegate can access rows via index, modelData, roles, etc.A ListModel (and the internal list model used by Repeater) can only store values that are representable as QVariants: numbers, strings, booleans, simple JS objects, etc.
JavaScript functions are not supported as model data, so when the array is wrapped into this internal model, any function properties are effectively lost or become non-callable. That’s why inside the delegate typeof modelData.handler (or modelData[1]) is no longer "function". -
In QML, the model of a Repeater is always treated as an item model (conceptually similar to a ListModel).
Even when you pass a plain JavaScript array (property var), QML internally wraps it into a list model so that the delegate can access rows via index, modelData, roles, etc.A ListModel (and the internal list model used by Repeater) can only store values that are representable as QVariants: numbers, strings, booleans, simple JS objects, etc.
JavaScript functions are not supported as model data, so when the array is wrapped into this internal model, any function properties are effectively lost or become non-callable. That’s why inside the delegate typeof modelData.handler (or modelData[1]) is no longer "function". -
T timafaer has marked this topic as solved
-
In QML, the model of a Repeater is always treated as an item model (conceptually similar to a ListModel).
Even when you pass a plain JavaScript array (property var), QML internally wraps it into a list model so that the delegate can access rows via index, modelData, roles, etc.A ListModel (and the internal list model used by Repeater) can only store values that are representable as QVariants: numbers, strings, booleans, simple JS objects, etc.
JavaScript functions are not supported as model data, so when the array is wrapped into this internal model, any function properties are effectively lost or become non-callable. That’s why inside the delegate typeof modelData.handler (or modelData[1]) is no longer "function".I was aware of the issue with
ListModelas I have encountered this myself but I didn't know it applied to a raw JS array model, so that is interesting to know. Thanks for your update.When I encountered the issue using
ListModel(I wanted a field in the model to hold a function), I worked around it by moving the model data into a JS array and programmatically creating theListModelfrom it. The raw array still held the functions but I skipped these when adding the data to the model. Instead, when I needed to access a function in a delegate, I used theindexto index back into the JS array.I guess another approach that might work for you, which would avoid introducing the
ListModel, is simply having two parallel arrays one of which you use as the model and the other to hold the functions. You could use the same indexing approach with this. (In my case, there were other reasons why it was convenient to have aListModel.) -
In QML, the model of a Repeater is always treated as an item model (conceptually similar to a ListModel).
Even when you pass a plain JavaScript array (property var), QML internally wraps it into a list model so that the delegate can access rows via index, modelData, roles, etc.A ListModel (and the internal list model used by Repeater) can only store values that are representable as QVariants: numbers, strings, booleans, simple JS objects, etc.
JavaScript functions are not supported as model data, so when the array is wrapped into this internal model, any function properties are effectively lost or become non-callable. That’s why inside the delegate typeof modelData.handler (or modelData[1]) is no longer "function".@timafaer said in QML: Functions inside var-array model become undefined after switching Repeater model:
JavaScript functions are not supported as model data, so when the array is wrapped into this internal model, any function properties are effectively lost or become non-callable. That’s why inside the delegate typeof modelData.handler (or modelData[1]) is no longer "function".
That was the case in Qt 5.15, but that's no longer true in Qt 6.5+. As I mentioned before, your code works just fine with a recent version of Qt.