如何绑定到转发器外部的转发器生成项目的 属性?
How to bind to a property of a repeater-generated item outside of the repeater?
我希望能够绑定到 Repeater
生成的项目的 属性 以对其进行处理,例如显示它的坐标。为此,我使用 itemAt()
这样的:
ListModel {
id: modelNodes
ListElement { name: "Banana"; x: 100; y: 200 }
ListElement { name: "Orange"; x: 150; y: 100 }
}
Repeater {
id: foo
model: modelNodes
Rectangle {
x: model.x; y: model.y
width: textBox.implicitWidth + 20
height: textBox.implicitHeight + 20
color: "red"
Drag.active: true
Text {
id: textBox
anchors.centerIn: parent
color: "white"
text: model.name + ": " + foo.itemAt(index).x
}
MouseArea {
anchors.fill: parent
drag.target: parent
}
}
}
Text {
id: moo
Binding {
target: moo
property: "text"
value: foo.itemAt(0).x + " -> " + foo.itemAt(1).x
}
}
在委托内部这工作正常,但是当我尝试在 Repeater 外部使用它时(即将 moo 的文本绑定到它),我收到以下错误:
TypeError: Cannot read property 'x' of null
如何解决这个问题?
Binding
对象在 Repeater 之外不起作用的原因是因为在计算绑定时 Repeater 尚未构建其项目。要解决此问题,您可以将绑定移动到 Component.onCompleted 处理程序中。然后只需使用 Qt.binding()
函数从 javascript (docs).
进行绑定
Text {
Component.onCompleted: {
text = Qt.binding(function() { return foo.itemAt(0).x + ", " + foo.itemAt(1).x })
}
}
你没有。
(或者更准确地说,你不应该)
代表不应该存储状态或数据,只是显示它或能够与之交互。
在您的情况下,您所追求的是存储在模型中的数据。
您的解决方案应该是在您的委托中修改您的模型,并根据需要从您的模型中获取数据。
我创建了一个小例子来说明我的意思:
import QtQuick 2.15
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
visible: true
width: 800
height: 640
ListModel {
id: modelNodes
ListElement { name: "Banana"; x: 50; y: 50 }
ListElement { name: "Orange"; x: 50; y: 100 }
}
Row {
anchors.centerIn: parent
spacing: 1
Repeater {
model: 2 // display 2 copy of the delegates for demonstration purposes
Rectangle {
color: "transparent"
width: 300
height: 300
border.width: 1
Repeater {
id: foo
model: modelNodes
Rectangle {
x: model.x; y: model.y
width: textBox.implicitWidth + 20
height: textBox.implicitHeight + 20
color: "red"
DragHandler {
dragThreshold: 0
}
onXChanged: model.x = x // modify model data when dragging
onYChanged: model.y = y
Text {
id: textBox
anchors.centerIn: parent
color: "white"
text: model.name + ": " + foo.itemAt(index).x
}
}
}
}
}
}
Instantiator {
model: modelNodes
delegate: Binding { // the hacky solution to the initial problem.
target: myText
property: model.name.toLowerCase() + "Point"
value: Qt.point(model.x, model.y)
}
}
Text {
id: myText
property point bananaPoint
property point orangePoint
anchors.right: parent.right
text: JSON.stringify(bananaPoint)
}
ListView {
anchors.fill: parent
model: modelNodes
delegate: Text {
text: `${model.name} - (${model.x} - ${model.y})`
}
}
}
我已经使用了一个 hacky 解决方案来解决你最初的问题 Instantiator
of Binding
s,我不太了解用例,所以这可能不是理想的解决方案。在这里,它为模型的每个元素创建了一个绑定,但这很奇怪。如果您只需要第一行的数据,您可能需要在 Binding
中执行 when: index === 0
。我创建了第三方库以获得更清晰的代码:https://github.com/okcerg/qmlmodelhelper
这将为您的外部生成以下代码 Text
(并允许您摆脱奇怪的 Instantiator + Binding 部分):
Text {
readonly property var firstRowData: modelNodes.ModelHelper.map(0)
text: firstRowData.x + ", " + firstRowData.y
}
请注意,我关于不在委托中存储数据(或从外部访问它们)的观点仍然代表您选择的任何解决方案。
我希望能够绑定到 Repeater
生成的项目的 属性 以对其进行处理,例如显示它的坐标。为此,我使用 itemAt()
这样的:
ListModel {
id: modelNodes
ListElement { name: "Banana"; x: 100; y: 200 }
ListElement { name: "Orange"; x: 150; y: 100 }
}
Repeater {
id: foo
model: modelNodes
Rectangle {
x: model.x; y: model.y
width: textBox.implicitWidth + 20
height: textBox.implicitHeight + 20
color: "red"
Drag.active: true
Text {
id: textBox
anchors.centerIn: parent
color: "white"
text: model.name + ": " + foo.itemAt(index).x
}
MouseArea {
anchors.fill: parent
drag.target: parent
}
}
}
Text {
id: moo
Binding {
target: moo
property: "text"
value: foo.itemAt(0).x + " -> " + foo.itemAt(1).x
}
}
在委托内部这工作正常,但是当我尝试在 Repeater 外部使用它时(即将 moo 的文本绑定到它),我收到以下错误:
TypeError: Cannot read property 'x' of null
如何解决这个问题?
Binding
对象在 Repeater 之外不起作用的原因是因为在计算绑定时 Repeater 尚未构建其项目。要解决此问题,您可以将绑定移动到 Component.onCompleted 处理程序中。然后只需使用 Qt.binding()
函数从 javascript (docs).
Text {
Component.onCompleted: {
text = Qt.binding(function() { return foo.itemAt(0).x + ", " + foo.itemAt(1).x })
}
}
你没有。
(或者更准确地说,你不应该)
代表不应该存储状态或数据,只是显示它或能够与之交互。 在您的情况下,您所追求的是存储在模型中的数据。
您的解决方案应该是在您的委托中修改您的模型,并根据需要从您的模型中获取数据。
我创建了一个小例子来说明我的意思:
import QtQuick 2.15
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
visible: true
width: 800
height: 640
ListModel {
id: modelNodes
ListElement { name: "Banana"; x: 50; y: 50 }
ListElement { name: "Orange"; x: 50; y: 100 }
}
Row {
anchors.centerIn: parent
spacing: 1
Repeater {
model: 2 // display 2 copy of the delegates for demonstration purposes
Rectangle {
color: "transparent"
width: 300
height: 300
border.width: 1
Repeater {
id: foo
model: modelNodes
Rectangle {
x: model.x; y: model.y
width: textBox.implicitWidth + 20
height: textBox.implicitHeight + 20
color: "red"
DragHandler {
dragThreshold: 0
}
onXChanged: model.x = x // modify model data when dragging
onYChanged: model.y = y
Text {
id: textBox
anchors.centerIn: parent
color: "white"
text: model.name + ": " + foo.itemAt(index).x
}
}
}
}
}
}
Instantiator {
model: modelNodes
delegate: Binding { // the hacky solution to the initial problem.
target: myText
property: model.name.toLowerCase() + "Point"
value: Qt.point(model.x, model.y)
}
}
Text {
id: myText
property point bananaPoint
property point orangePoint
anchors.right: parent.right
text: JSON.stringify(bananaPoint)
}
ListView {
anchors.fill: parent
model: modelNodes
delegate: Text {
text: `${model.name} - (${model.x} - ${model.y})`
}
}
}
我已经使用了一个 hacky 解决方案来解决你最初的问题 Instantiator
of Binding
s,我不太了解用例,所以这可能不是理想的解决方案。在这里,它为模型的每个元素创建了一个绑定,但这很奇怪。如果您只需要第一行的数据,您可能需要在 Binding
中执行 when: index === 0
。我创建了第三方库以获得更清晰的代码:https://github.com/okcerg/qmlmodelhelper
这将为您的外部生成以下代码 Text
(并允许您摆脱奇怪的 Instantiator + Binding 部分):
Text {
readonly property var firstRowData: modelNodes.ModelHelper.map(0)
text: firstRowData.x + ", " + firstRowData.y
}
请注意,我关于不在委托中存储数据(或从外部访问它们)的观点仍然代表您选择的任何解决方案。