I’m trying to insert the values from a Database (SQLite) into a MenuItem (so with a repeater). The database consists of two tables with text data. As such: xx|XXXX. The end goal being to retrieve all lines from the DB and make a MenuItem of each line. I think the Repeater is the optimal solution.
As you can see in the code bellow, I have a function “get()” that actually gets the data from the DB. And then I call that function from within the Repeater, so it can be used for the Model.
That’s where it doesn’t workout for me, no data gets to the Repeater part (or the Model), even when using a global function. I don’t think (and i tried), putting the function inside the Repeater would be the correct option either.
ComboBox {
id: comboBox
label: “List”
function getDatabase() {
return LocalStorage.openDatabaseSync("MyDatabase", "1", "My DataBase", 100000);
}
function get() {
var db = getDatabase();
db.transaction(function(tx) {
var rs = tx.executeSql('SELECT * FROM Table1');
var r = "";
for (var i = 0; i < rs.rows.length; i++) {
r += rs.rows.item(i).table1_value + "\n"
}
return r;
}
);
}
menu : ContextMenu {
id: contextMenu
Repeater {
id: repeaterLang
Component.onCompleted: comboBox.get()
model: comboBox.get()
MenuItem {
id: menuItem
text: "%1".arg(modelData)
}
}
}
}
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);
});
}
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.
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.
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.
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:
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)
}
}
}
}
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.