Your get() function doesn’t return anything.
The return r is lost because it’s from the anonymous calllback function.
Your get() function doesn’t return anything.
The return r is lost because it’s from the anonymous calllback function.
I’d define a ListModel and a populate function, and call populate(r) from the callback to fill it.
Do you have an example maybe ? I don’t know the populate function yet.
Well you will have to write one 
My own code is always very convoluted because I have a kind of stream-of-consciousness method of coding.
Also I don’t have an app where I use LocalStorage.
But.
What you have is a data source which returns a list of things. That is common among LocalStorage, a DBus call, or an XMLHttpRequest.
Also, the method of getting the list of things involves a callback function, and you want to popuplate a QML datatype.
So what I do usually is something like this (pseudo-QML):
ListModel { id: myModel } // this is an empty model
function populateModel(data) { // where data is an array
for (i=0; i<data.length; i++) {
// ... mangle the input data to something that can be added to a listmodel
var mangledElement = // whatever you need to do with data[i];
myModel.append(mangledElement);
}
}
// data source, pretty much similar for XMLHTTPRequest, DBus calls, or LocalStorage:
WhateverInterface {
function getData(foo, bar, function(re) {
console.debug("Got data:", JSON.stringify(re, null, 4);
populateModel(re);
});
}
You can see this pattern here:
Another app of mine does it the same way:
onCompleted: install-history/MainPage.qml at master - install-history - Codeberg.org
fillModel as its data handling callback : install-history/MainPage.qml at master - install-history - Codeberg.org
fillModel does a bit of filterning install-history/MainPage.qml at master - install-history - Codeberg.org
Note, I am very much self-taught-through-trial-and-error QML coder, these might not be the best of examples, might even be outright misuse of the languages, but I think the basic concept is sound.
A note about ListModel.append():
To use append() from javascript, you need to pass an object to that function.
So where you would normally construct such a model like this:
import QtQuick 2.0
ListModel { id: fruitModel
ListElement { name: "Apple"; cost: 2.45 }
ListElement { name: "Banana"; cost: 3.25 }
}
… from JS you would:
function addFruit() {
var a = {};
a.name = "Apple";
a.cost = 2.45;
fruitModel.append(a);
// alternatively
var b = {
"name": "Banana",
"cost": 3.25
};
fruitModel.append(b);
}
as you can see, the JS object keys become model “roles” in the ListModel.
This is a primitive use of Localstorage, cribbed from another app that I updated to keep it going. DB functions in:
The id: sourcesListDelegate is used in a SilicaListView in: https://github.com/poetaster/allthenews/blob/main/qml/pages/SourcesPage.qml
It’s a primitive example.
Heh. I just realized that this is a ‘bad’ example for your purposes. I’m actually on reading ‘settings’ from via Localstorage. I’ll find a more suitable example.
Sorry, better example:
The delegate: https://github.com/poetaster/harbour-dwd/blob/main/qml/delegates/LocationItem.qml
Used in this list view: https://github.com/poetaster/harbour-dwd/blob/main/qml/pages/StartPage.qml
With js in: https://github.com/poetaster/harbour-dwd/blob/main/qml/js/storage.js
Not exactly poster child code 
Hi @poetaster , I tried using your code witch seemed more accessible for me. But I still fail to create “content”.
//here the database is filled with data. But no MenuItem is created.
I understand that in your code you crafted a specific delegate called “LocationItem” which creates this table “city”|“delete option” in the Stored Locations. But shouldn’t I be able just by specifying “MenuItem” to create the MenuItems for each DB entry ?
IE:
function getDatabase2() {
return LocalStorage.openDatabaseSync("LanguageOptions", "0.1", "All Language Options", 100000);
}
function getLanguages() {
var db = getDatabase2();
var returned_languages
db.transaction(function tempo (tx) {
var selectLang = tx.executeSql('SELECT * FROM Languages');
var listOfLangs
for (var i = 0; i < selectLang.rows.length; i++) {
listOfLangs += selectLang.rows.item(i).language_code + "\n"
}
returned_languages = listOfLangs;
}
);
//console.log(returned_languages) - returns: "XX" "YY" "WW" as expected
return returned_languages
}
SilicaFlickable {
anchors.fill: parent
Column {
width: parent.width
SilicaListView {
id:listView
model: ListModel {
id: listModel
function update() {
getLanguages()
}
Component.onCompleted:update()
}
delegate: MenuItem {
id:locations
}
}
I also tried building your app to play arround and understand the working parts but when building it i get the Installed (but unpackaged) file(s) found: /usr/bin/harbour-dwd.debug error (i cannot find that file in the repo).
The dwd app contains a ‘dump’ of location names with gps coordinates (in json format read from file in /usr/share/harbour-dwd/qml/pages/LocationSearchPage.qml). These are read and ‘filtered’ when you do a search term (or via gps_coords). The database storage is done when you click on one of the filtered selections.
In /LocationSearchPage.qml look for:
onClicked: {
Store.addLocation(model);
which uses the javascript
In /usr/share/harbour-dwd/qml/js/storage.js
function addLocation(locationData)
In this case, directly after storing, the pageStack is popped in you return to the ‘main’ page which now has a new entry to read / show…
I could have simply created a db with those locations, but I wasn’t certain how stable the list (obtained from the DWD) is, so I stuck to the json I can fetch from them.
That sounds like something is a bit wonky with the sdk. The debug file would be dynamically created but (so not packaged), but I’ve not seen it befor.
You need to append to the listModel, not return the list :
for (var i = 0; i < selectLang.rows.length; i++) {
listModel.append(selectLang.rows.item(i).language_code)
}
MenuItem is a bit special in that it is supposed to be created within a menu such as PullDownMenu or ContextMenu. It won’t work well as a delegate in a ListView (as you see).
So you can either Create a menu and attach it somewhere:
ContextMenu { id: ctxmenu
Repeater {
model: listModel
delegate: MenuItem { text: modelData }
}
}
ListItem {
menu: ctxmenu
}
or create a different delegate:
SilicaListView {
id:listView
model: listModel
delegate: ListItem {
id:locations
Label { text: modelData }
}
}
Ah, dear. I completely missed the menu. Menus, with the versions of qt at our disposal are kinda suck. I try to avoid them. I tend to use them where I have a small number of static elements.
Thank you both as this led me to the right path I think.
But the data doesn’t updates itself after being changed (.append). I think I mixed both of your solutions, but even based on the doc about the ListModel there is no mention of a need to “refresh” the ListModel to get the new data displayed. I followed your tips and moved out of the Menu/ContextMenu display to the list one. Here the data is indeed appended but the displayed text remains “text2”.
I also don’t get the need for the initial ListModel declaration as I can just call the function from the ListView itself, but nothing will be displayed then - but the values are there.
Page {
ListModel{
id: listModel
ListElement {
name : "text2"
}
}
function getDatabase2() {
return LocalStorage.openDatabaseSync("LanguageOptions5", "0.1", "All Language Options", 100000);
}
function getLanguages() {
var db = getDatabase2();
db.transaction(function tempo (tx) {
var selectLang = tx.executeSql('SELECT * FROM Languages');
var listOfLangs
for (var i = 0; i < selectLang.rows.length; i++) {
listModel.append({"name": selectLang.rows.item(i).language_code})
}
}
);
}
SilicaFlickable {
anchors.fill: parent
Column {
width: parent.width
SilicaListView {
id:listView
model: listModel //i can also just { getLanguages() }
delegate: ListItem {
Label { text: modelData }
}
}
Button {
text: "Click me"
onClicked: {
getLanguages()
}
//listModel.append({"name": "Doe"}) - works
}
Button {
text: "Check me"
onClicked: {
console.log("zero", listModel.get(0).name)
console.log("one", listModel.get(1).name)
console.log("two", listModel.get(2).name)
console.log("nine", listModel.get(9).name)
}
}
}
}
}
In your ListItem, try text: name, now that you have a name role in your model.
I tried by setting
Label { text: modelData }
to
Label { text: name }
but the result is the same. Is that what you meant ?
So I’ve been trying to find a way, but I’m unable to update the data (aka the name value) when the button is pressed. But no even the second ListElement aka “text2” is displayed in the app.
Page {
ListModel{
id: listModel
ListElement {
name : "text"
}
ListElement {
name : "text2"
}
}
function getDatabase2() {
return LocalStorage.openDatabaseSync("LanguageOptions5", "0.1", "All Language Options", 100000);
}
function getLanguages() {
var db = getDatabase2();
db.transaction(function tempo (tx) {
var selectLang = tx.executeSql('SELECT * FROM Languages');
var listOfLangs
for (var i = 0; i < selectLang.rows.length; i++) {
listModel.append({"name": selectLang.rows.item(i).language_code})
}
}
);
}
SilicaFlickable {
anchors.fill: parent
Column {
width: parent.width
SilicaListView {
id:listView
model: listModel
delegate: ListItem {
Label { text: name }
}
}
Button {
text: "Click me"
onClicked: {
getLanguages()
}
//listModel.append({"name": "Doe"}) - works
}
this was quite some time ago and I forgot to mark it as solved, but thank you very much for your input ! Hopefully this will help others in the future.
This post was flagged by the community and is temporarily hidden.