从QML ListView委托调用JavaScript对象方法

从QML ListView委托调用JavaScript对象方法,javascript,qt,qml,Javascript,Qt,Qml,我有一个带有JavaScript对象列表的ListView作为模型。代理需要响应单击,我希望存储附加到模型项的单击处理程序(actionproperty): 但是当我点击一个项目时,我得到 TypeError:对象[object]的属性“action”不是函数 将函数附加到对象并调用它的正确方法是什么?不幸的是,无法将函数存储在: 值必须是简单常量;字符串(带引号,可选) 在对QT_TR_NOOP)、布尔值(真、假)、数字、, 或枚举值(如AlignText.AlignHCenter) 从委托调

我有一个带有JavaScript对象列表的ListView作为模型。代理需要响应单击,我希望存储附加到模型项的单击处理程序(
action
property):

但是当我点击一个项目时,我得到

TypeError:对象[object]的属性“action”不是函数


将函数附加到对象并调用它的正确方法是什么?

不幸的是,无法将函数存储在:

值必须是简单常量;字符串(带引号,可选) 在对QT_TR_NOOP)、布尔值(真、假)、数字、, 或枚举值(如AlignText.AlignHCenter)

从委托调用函数的一种简单方法是将函数保持在模型之外,并在模型中引用其名称:

ListView {
    id: idListView

    readonly property var actions: {
        "action1": function() {
            console.log("called action 1!");
        },
        "action2": function() {
            console.log("called action 2!");
        }
    }

    model: [
        {
            name: "Item 1",
            icon: "icon1.svg",
            action: "action1"
        },
        {
            name: "Item 2",
            icon: "icon2.svg",
            action: "action2"
        },
        /* other items */
    ]
    delegate: MyDelegate {
        name: modelData.name
        icon: modelData.icon

        MouseArea {
            anchors.fill: parent
            onClicked: {
                if (typeof idListView.actions[modelData.action] === "function") {
                    idListView.actions[modelData.action]()
                }
            }
        }
    }
}

您应该将函数定义为QML属性
Object
不允许这样做,因此您可以使用
ListModel

import QtQuick 2.11
import QtQuick.Window 2.11

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

    ListView {
        anchors.fill: parent
        spacing: 2
        model: ListModel {
            ListElement {
                name: "Item 1"
                property var func: function(){ console.log("Item 1 clicked"); }
            }
            ListElement {
                name: "Item 2"
                property var func: function(){ console.log("Item 2 clicked"); }
            }
        }

        delegate: Rectangle {
            height: 30
            color: "#EFEFEF"
            border { width: 1; color: "#CCC" }
            width: parent.width
            Text {
                text: name
                anchors.centerIn: parent
            }
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    if(typeof func === "function")
                        func();
                    else
                        console.error("Click handler not defined");
                }
            }
        }
    }
}
另一个有点诡计的解决方案:

import QtQuick 2.11
import QtQuick.Window 2.11

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

    ListView {
        anchors.fill: parent
        spacing: 2
        property list<QtObject> arr: [
            QtObject {
                property string name: "Item 1"
                property var func: function(){ console.log("Item 1 clicked"); }
            },
            QtObject {
                property string name: "Item 2"
                property var func: function(){ console.log("Item 2 clicked"); }
            }
        ]
        model: arr

        delegate: Rectangle {
            height: 30
            color: "#EFEFEF"
            border { width: 1; color: "#CCC" }
            width: parent.width
            Text {
                text: modelData.name ? modelData.name : "Undefined item"
                anchors.centerIn: parent
            }
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    if(typeof modelData.func === "function")
                        modelData.func();
                    else
                        console.error("Click handler not defined");
                }
            }
        }
    }
}
导入QtQuick 2.11
导入QtQuick.Window 2.11
窗口{
id:根
可见:正确
宽度:480
身高:640
标题:qsTr(“你好世界”)
列表视图{
锚定。填充:父级
间距:2
财产清单:[
QtObject{
属性字符串名称:“项1”
属性var func:function(){console.log(“单击项目1”);}
},
QtObject{
属性字符串名称:“项2”
属性var func:function(){console.log(“单击项目2”);}
}
]
型号:arr
代表:矩形{
身高:30
颜色:“EFEFEF”
边框{宽度:1;颜色:“#CCC”}
宽度:parent.width
正文{
text:modelData.name?modelData.name:“未定义项”
anchors.centerIn:父对象
}
鼠耳{
锚定。填充:父级
再次点击:{
if(typeof modelData.func==“函数”)
modelData.func();
其他的
控制台。错误(“未定义单击处理程序”);
}
}
}
}
}

我遇到了类似的问题。我想要一个
抽屉
包含
动作
列表视图
。一些尝试和错误,终于让它运行(Qt5.11)。也许我的代码对其他人有用

Action {
    id: aboutAction
    text: "About"
    icon.source: "icon_info.png"
}

Drawer {
    edge: Qt.LeftEdge
    width: Screen.pixelDensity * 100  // mm
    height: parent.height

    ListView {
        id: listView
        anchors.fill: parent

        model: ListModel {}

        delegate: ItemDelegate {
            width: parent.width
            action: action_
        }
    }

    Component.onCompleted: {
        listView.model.append({action_: aboutAction});
    }
}

谢谢你的建议!所以,不管模型类型如何,模型项都会转换为列表元素?不,我不这么认为。我想奥格是指福利比斯的回答,我不确定。我使用了
liselement
的文档,因为我认为它与使用对象作为模型的行为相同(具有相同的可能值)。但是我不知道如何使用对象VS
ListModel
来管理模型。无论如何,我没有想到使用属性,如果您不介意使用
ListModel
的话,我发现fobilis的答案是一个很好的选择,因为您不必依赖函数名!多谢!以上是唯一的解决方法。您是否测试了第一个代码段?我认为函数不能在
liselement
中使用。是的,在5.11中测试过。首先,我发布了第二个示例,使用
QtObject
s,然后我决定用
liselement
尝试同样的方法,并惊讶地发现它是有效的。一个
ListElement
只能有简单的类型,但对
property
:-)直接使用ListElement对我不起作用,因为它不能处理QmlEngine动态重传。第二个选项看起来不错(而且很有效),尽管更加详细-我正在寻找一种将处理程序附加到模型项的方法。它在Qt 5.11中确实是新的:-“从Qt 5.11 ListElement开始还允许将函数声明分配给角色。这允许使用可调用操作定义ListElement。”第一个变量在5.12中仍然返回错误:
liselement:无法对属性值使用脚本。这里应该有什么正确的语法?文件中没有样本。
Action {
    id: aboutAction
    text: "About"
    icon.source: "icon_info.png"
}

Drawer {
    edge: Qt.LeftEdge
    width: Screen.pixelDensity * 100  // mm
    height: parent.height

    ListView {
        id: listView
        anchors.fill: parent

        model: ListModel {}

        delegate: ItemDelegate {
            width: parent.width
            action: action_
        }
    }

    Component.onCompleted: {
        listView.model.append({action_: aboutAction});
    }
}