Pass an Array in ListModel - javascript

I'm wondering how can I pass an array in ListModel?
ok, In QML I have a ListView and I set it's ListModel like so:
model: ListModel
{
id: myList
ListElement
{
name: ""
card: 0
books: []
}
}
I can append to it by using:
myList.append({name:"terry", card:00100, books:["024589","865976","879582","215645"]});
but when I try to output it on screen I get this.
{
"card": 00100
"books": {
"objectName": "",
"count": 4,
"dynamicRoles": false
},
"name": "terry",
"name": "terry"
}
I'm not sure why I'm getting 2 names though! and how can I get the value of books?
I look up the QML documentation of ListModel and ListElement couldn't find anything related to passing an array, all the examples are integer or string.
Any idea how I can get the date?
I did work around it by calling the array in Delegate with Component.onCompleted:{} but I believe that's not a good/correct way since Delegate is not responsible for holding the data and should be done in Model, please do correct me if I'm wrong.
Thanks for your time.
Edit01: Thanks for the reply, here is the reason I need array:
I have a ComboBox in Delegate like so:
delegate: Rectangle
{
id: rowID
width: 50
height: 40
color: "#323232"
Row
{
anchors.fill: parent
anchors.leftMargin: 10
anchors.rightMargin: 10
Label{
id: nameID
text: name
font.pixelSize: 12
width: 200
wrapMode: Text.WrapAnywhere
anchors.verticalCenter: parent.verticalCenter
color: "#999"
}
Label{
anchors.verticalCenter: parent.verticalCenter
text: "out:"
font.pixelSize: 12
color: "#999"
}
ComboBox{
id: booksID
height: 20
width: 50
model: books
anchors.verticalCenter: parent.verticalCenter
}
}
}
as you can see I'm feeding the name to Label (id: nameID) and I want to feed the books to ComboBox (id: booksID) that has model, if I make books key as ListElement how can I feed all the values?
in QML ListModel or ListElement documentation didn't mention anything about getting all the key's value right? it only supports get(int index) which is based on an index number.

You did that wrong. Array members must be ListElement:
ListModel {
id: mod
ListElement {
name: "ali"
dic: [ ListElement{text:"asad-o-llah"; code: 14}, ListElement{text:"aboo torab"; code: 72}, ListElement{text:"amir al-momenin"; code: 110}]
}
}
ListView {
model: mod
anchors.fill: parent
delegate: Component {
Rectangle {
width: parent.width; height: 50
Row {
Text {
text: name
}
ComboBox {
width: 100; height: 30
model: dic //<-- set dic as model for combo box
textRole: "text" //<-- important!
onCurrentIndexChanged: {
console.log("current code is "+model.get(currentIndex).code); //<-- get code value
}
}
}
}
}
}
Component.onCompleted: {
var v = mod.get(0).dic.get(0).value; //<-- sample usage
console.log(v);
}

Do you want some thing similar to this:
Rectangle {
id: root
visible: true
width: 360
height: 360
ListModel
{
id: myList
ListElement {
name: "Story"
card: 3
books: [
ListElement { bookName: "Story 1" },
ListElement { bookName: "Story 2" },
ListElement { bookName: "Story 3" }
]
}
ListElement {
name: "Novel"
card: 3
books: [
ListElement { bookName: "Novel 1" },
ListElement { bookName: "Novel 2" },
ListElement { bookName: "Novel 3" }
]
}
}
Component {
id: displayDelegate
Rectangle
{
id: rowID
width: 300 //50
height: 40
color: "#323232"
border.color: "white"
Row
{
anchors.fill: parent
anchors.leftMargin: 10
anchors.rightMargin: 10
Text{
id: nameID
text: name
font.pixelSize: 12
width: 50 //200
wrapMode: Text.WrapAnywhere
/*anchors.verticalCenter: parent.verticalCenter*/
color: "white"//"#999"
}
Text{
/*anchors.verticalCenter: parent.verticalCenter*/
text: "out:"
font.pixelSize: 12
color: "white"//"#999"
}
/*ComboBox{
id: booksID
height: 20
width: 50
model: books
anchors.verticalCenter: parent.verticalCenter
}*/
Repeater {
model: books
Text { text: bookName + "\t"; color: "white" }
}
}
}
}
ListView {
id: disp
anchors.fill: parent
model: myList
delegate: displayDelegate
}
}
I have modified few line of the code which you have shared. I am not sure about your ComboBox implementation. Therefore, I have used my own implementation of Repeater. You can try to execute and check the result.

As an alternative to working with ListModel and ListElement, you can also have a look at the QSyncable JsonListModel QML type. (It is an open-source component by Ben Lau, you can find it on GitHub here: https://github.com/benlau/qsyncable)
The JsonListModel is a specialized ListModel type, that can handle JSON Arrays that you e.g. create in your QML or fetch from a REST service. It automatically synchronizes the JSON to a QML ListModel, so its very convenient to use:
ListView {
id: listView
anchors.fill: parent
// property for json data, used as source for JsonListModel
property var jsonData: []
// use JsonListModel as model
model: JsonListModel {
source: listView.jsonData
keyField: "id"
}
// delegate
delegate: DelegateItem { /* ... */ }
}
You can also find a comprehensive guide how it works here: JsonListModel guide

Related

How to merge cells in a single column?

Given the sample code using ag-grid with Vue 3
<script setup lang="ts">
import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-alpine.css";
import { AgGridVue } from "ag-grid-vue3";
import { ref } from "vue";
const columnDefs = ref([
{
field: "person",
},
{
field: "car",
},
]);
const rowData = ref([
{
person: "person-1",
car: "car-1",
},
{
person: "person-1",
car: "car-2",
},
{
person: "person-1",
car: "car-3",
},
{
person: "person-2",
car: "car-4",
},
{
person: "person-3",
car: "car-5",
},
{
person: "person-3",
car: "car-6",
},
]);
</script>
<template>
<ag-grid-vue
style="width: 500px; height: 500px"
class="ag-theme-alpine"
:columnDefs="columnDefs"
:rowData="rowData"
>
</ag-grid-vue>
</template>
You will get the following output
I could group the table based on person but is there a way to merge cells in a single column?
I think I can't use row-spanning for a single column definition because inside the rowSpan function it is not possible to tell ag-grid to combine multiple cells of a single column.
Any ideas?
After spending a few hours on this requirement, I came up with the solution.
Here are the steps which I performed :
To implement rowSpan, You have to manipulate the rowData so that it will contain the person property only in the first occurance object. As a result, When we will apply the rowSpan based on the length it will not take that length for each same person name. This is how I manipulated the rowData :
const uniq = {};
const rowData = [{
person: "person-1",
car: "car-1",
}, {
person: "person-1",
car: "car-2",
}, {
person: "person-1",
car: "car-3",
}, {
person: "person-2",
car: "car-4",
}, {
person: "person-3",
car: "car-5",
}, {
person: "person-3",
car: "car-6",
}];
rowData.forEach(obj => {
!uniq[obj.person] ? uniq[obj.person] = true : delete obj.person;
});
console.log(rowData);
Now, Created a rowSpan function which is basically used to return the length of the objects contains occurrence of same person names.
function rowSpan(params) {
const person = params.data.person;
const gridData = getData(); // getData() with return the original rowData array.
return gridData.filter((e) => e.person === person).length;
}
At the end, for styling the rowSpan columns. I used these styles (refer from here)
.show-cell {
background: white;
border-left: 1px solid lightgrey !important;
border-right: 1px solid lightgrey !important;
border-bottom: 1px solid lightgrey !important;
}
This is how the columnDefs for person object looks like :
{
field: "person",
rowSpan: rowSpan,
cellClassRules: { 'show-cell': 'value !== undefined' }
}
Live Demo : Row Spanning Demo
Are you sure row-spanning will not work? Can you try to implement this?
function rowSpan(params) {
const person = params.data.person;
return rowData.value.filter((e) => e.person === person).length;
}
const columnDefs = ref([
{
field: "person",
rowSpan: rowSpan,
},
{
field: "car",
},
]);

Using vue-virtual-scroller not showing

I'm trying out https://github.com/Akryum/vue-virtual-scroller but I cannot get it to work. What am I doing wrong?
I use pug / jade for server-side HTML rendering. I don't receive any error messages but nothing gets rendered...
Pug / Jade
#test
recyclescroller.scroller(:items='active_projects' :item-size='32' key-field='id' v-slot='{ item }')
.user
| {{ item.name }}
CSS
.user {
height: 32%;
padding: 0 12px;
display: flex;
align-items: center;
}
javascript
Vue.component('RecycleScroller', VueVirtualScroller.RecycleScroller)
test = new Vue({
el: '#test',
data: {
active_projects : [{name : 'test'}]
}
})
When you use Recyclescroller you should inside each item has id
Because the uniqueness of the item is done by using the id
Your list should look like this :
data () {
return {
active_projects: [
{
name: 'test', id: 1
},
{
name: 'test', id: 2
},
{
name: 'test', id: 3
},
{
name: 'test', id: 4
},
]
}
}
Doc - Important notes
If the items are objects, the scroller needs to be able to identify them. By default it will look for an id field on the items. This can be configured with the keyField prop if you are using another field name.

List Element and javascript values

I'm trying to create a list and append values to it that I've retrieved from xml http request. I've tested just a text block with the value of typeAssetProcess and it prints fine, but when I start trying to use a list is when everything starts to break. What am I doing wrong and how can I fix it?
import QtQuick 2.0
import "../controls" as Controls
Item {
Column {
id: column
width: parent.width
height: parent.height
}
ListView {
id: listView
width: parent.width
height: parent.height
model: ListModel {
ListElement {
name: qstr("Proccess: %1").arg(typeAssetProcess)
colorCode: "grey"
}
ListElement {
name: "Red"
colorCode: "red"
}
ListElement {
name: "Blue"
colorCode: "blue"
}
ListElement {
name: "Green"
colorCode: "green"
}
}
delegate: Item {
x: 5
width: 80
height: 40
Row {
id: row1
Rectangle {
width: 40
height: 40
color: colorCode
}
Text {
text: name
font.bold: true
anchors.verticalCenter: parent.verticalCenter
}
spacing: 10
}
}
}
}
This code is what is breaking:
ListElement {
name: qstr("Proccess: %1").arg(typeAssetProcess)
colorCode: "grey"
}
This is a known limitation to the ListElement type, containing a "collection of role definitions instead of properties". This is why you can not use script or property binding for these roles (otherwise you get the error ListElement: cannot use script for property value).
Improvements are frequently requested to the Qt team but as far as I know there is no implementation yet.
One thing you can do is dynamically initialize the model instead of using fixed ListElement:
ListView {
id: listView
readonly property var modelElements: [
{
name: qsTr("Proccess: %1").arg(typeAssetProcess),
colorCode: "grey"
},
{
name: "Red",
colorCode: "red"
},
{
name: "blue",
colorCode: "blue"
},
{
name: "Green",
colorCode: "green"
}]
Component.onCompleted: {
modelElements.forEach(function(element) {
model.append(element)
})
}
width: parent.width
height: parent.height
model: ListModel {}
delegate: ...
}
You can also choose to implement your own model in C++.

How to change the image to text for Treantjs collapsible tree view?

I have to draw a representation of data using collapsible tree structure, i am using below example link for my purpose :
http://fperucic.github.io/treant-js/examples/collapsable/
But the problem i am facing is the above link had images at every node, i want to replace it with text which when i am doing it is generating the tree with correct branches but the text is not appearing.I am using my json like this:
{
"name":"sourcetable",
"children":[{"name":"MARD"},{"name":"MARD"},{"name":"MARD"},{"name":"MARD"}]
}
It comes something like this:
Let me know how i can show the name labels on the collapsible tree.
I think you are missing a bit:
nodeStructure: {
text: { name: "Parent node" },
children: [
{
text: { name: "First child" }
},
{
text: { name: "Second child" }
}
]
}
Taken from here:
http://fperucic.github.io/treant-js/
var simple_chart_config = {
chart: {
container: "#OrganiseChart-simple"
},
nodeStructure: {
text: { name: "Parent node" },
children: [
{
text: { name: "First child" }
},
{
[enter image description here][1]text: { name: "Second child" }
}
]
}
};
Demo Link:
https://fperucic.github.io/treant-js/examples/super-simple

Change QML GridView model by javascript

I have one ListView and one GridView. Imagine it like this: The first view presents the categories and the second view presents article in each category. I want to dynamically change the data model of the GridView when the current index of the ListView change by javascript. How do we do that?
You simply need to assign a new model. Here's one example, based on the ListModel docs. This model shows the fruit in the model in the ListView on the left. When a delegate is clicked, it sets the model for the GridView on the right to the list defined by the attributes role.
import QtQuick 1.0
Item {
width: 600; height: 400
ListView {
width: 300; height: 400
model: fruitModel
delegate: Text {
font.pixelSize: 20
text: name
width: 300
MouseArea {
anchors.fill: parent
onClicked: grid.model = attributes
}
}
}
GridView {
id: grid
x: 300; width: 300; height: 400
delegate: Text {
text: description
font.pixelSize: 20
}
}
ListModel {
id: fruitModel
ListElement {
name: "Apple"
cost: 2.45
attributes: [
ListElement { description: "Core" },
ListElement { description: "Deciduous" }
]
}
ListElement {
name: "Orange"
cost: 3.25
attributes: [
ListElement { description: "Citrus" }
]
}
ListElement {
name: "Banana"
cost: 1.95
attributes: [
ListElement { description: "Tropical" },
ListElement { description: "Seedless" }
]
}
}
}
This is an example of a nested model, but there are other possibilities. For example, if you're sourcing your data from a database, perhaps you just need to change the query used by the GridView's model, rather than setting a different model.
Depends on your models. Assuming CategoriesModel has category role, and ArticlesModel has setCategory method:
ListView {
model: CategoriesModel {}
delegate: Item {
MouseArea {
anchors.fill: parent
onClicked: {
grid_view.model.setCategory(model.category)
}
}
// ...
}
}
GridView {
id: grid_view
model: ArticlesModel {}
// ...
}

Categories