May 4, 2011

cmer4 cmer4
Lab Rat
109 posts

[SOLVED]What is optimal way of “combining” XmlLIstView and ListView?

 

Hi All,
XmlListModel is perfect for fetching data from the network. But then it is “read only” and allows little manupulation with the actual elements. Is there a way to combine those two??

I mean let’s say I want XmlLIstModel to “populate” certain UI elements (such as descriptions etc) from the web, but I want users to be able to add/remove/append etc (so basically address the model with typical for ListView commands, not just get)..

Many thanks in advance!

12 replies

May 4, 2011

Denis Kormalev Denis Kormalev
Lab Rat
1654 posts

I think best approach will be to populate ListModel from ready XmlListModel if you want pure qml solution. Or subclass QAbstractItemModel and parse xml there (I think it will be better solution, but it is C++ code).

May 4, 2011

cmer4 cmer4
Lab Rat
109 posts

Denis – great to hear back from you;)
Yes, I am trying this approach now as it seemed logical to me…here is the code I tried to use:

XMLfile:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <vendor>
  3.     <info>
  4.         <title>Vendor1</title>
  5.         <title>Vendor2</title>
  6.         <title>Vendor3</title>
  7.     </info>
  8. </vendor>

XmlListModel and ListView extracting the “title”:

  1. Rectangle { // This is Vendor List Block
  2.     id: vendorList
  3.     width: 100; height: 100
  4.     XmlListModel {
  5.         id: vendorsModel
  6.         source: "vendor.xml"
  7.         query: "/vendor/info/title"
  8.         XmlRole { name: "vendortitle"; query: "string()" }
  9.     }
  10.     Component {
  11.         id: vendorDelegate
  12.  
  13.         Text {
  14.            text: vendortitle
  15.            font.bold: true; font.pixelSize: 18
  16.         }
  17.     }
  18.     ListView {
  19.         id: listView
  20.         anchors.fill: parent
  21.         model: vendorsModel
  22.         delegate: vendorDelegate
  23.         spacing: 1
  24.         clip: true
  25.     }
  26. }

Now if I attempt to “populate” the ListModel using the XmlListModel, it gives error: “cannot use script for property value text: vendortitle”

Attempted code:

  1. import QtQuick 1.0
  2.  
  3. Rectangle { // This is Vendor List Block
  4.     id: vendorList
  5.     width: 100; height: 100
  6.     XmlListModel {
  7.         id: vendorsModel
  8.         source: "vendor.xml"
  9.         query: "/vendor/info/title"
  10.         XmlRole { name: "vendortitle"; query: "string()" }
  11.     }
  12.     ListModel {
  13.         id: vendorsModelNonXml
  14.         ListElement {
  15.             text: vendortitle
  16.         }
  17.     }
  18.  
  19.     Component {
  20.         id: vendorDelegate
  21.  
  22.         Text {
  23.            text: vendortitle
  24.            font.bold: true; font.pixelSize: 18
  25.         }
  26.     }
  27.     ListView {
  28.         id: listView
  29.         anchors.fill: parent
  30.         model: vendorsModelNonXml
  31.         delegate: vendorDelegate
  32.         spacing: 1
  33.         clip: true
  34.     }
  35. }

So I wonder if I am using wrong syntax here and getting the error…what should be the approach then? many thanks in advance!

May 4, 2011

Denis Kormalev Denis Kormalev
Lab Rat
1654 posts

you of course can’t use inner name of XmlRole outside of delegate :) You should write JS function that will go throuth all items in xmlmodel and populate listmodel

May 4, 2011

cmer4 cmer4
Lab Rat
109 posts

Ehm, I see…is it the right approach then to do something like…:

  1. Rectangle { // This is Vendor List Block
  2.     id: vendorList
  3.     width: 100; height: 100
  4.     XmlListModel {
  5.         id: vendorsModel
  6.         source: "vendor.xml"
  7.         query: "/vendor/info/title"
  8.         XmlRole { name: "vendortitle"; query: "string()" }
  9.         Component.onCompleted: function populateListModel() {
  10.                                    var i = 1
  11.                                    for (var i=1; i < vendorsModel.count; i++)
  12.                                    vendorsModelNonXml.append({"title":vendorsModel.get(i).vendortitle})
  13.                                }, console.log("populated")
  14.     }
  15.     ListModel {
  16.         id: vendorsModelNonXml
  17.     }
  18.     Component {
  19.         id: vendorDelegate
  20.  
  21.         Text {
  22.            text: text
  23.            color: "black"
  24.            font.bold: true; font.pixelSize: 18
  25.         }
  26.     }
  27.     ListView {
  28.         id: listView
  29.         anchors.fill: parent
  30.         model: vendorsModelNonXml
  31.         delegate: vendorDelegate
  32.         spacing: 1
  33.         clip: true
  34.     }
  35. }

May 5, 2011

cmer4 cmer4
Lab Rat
109 posts

Still can’t refer to vendortitle in the example above;( always states “undefined”. How should I explicitly make this variable available to javascript?

Tried inserting JS function almost everywhere now…plz help!

May 5, 2011

cmer4 cmer4
Lab Rat
109 posts

Also the help file says:

***
For example, for a model like this:

  1. XmlListModel {
  2.      id: model
  3.      source: "http://mysite.com/feed.xml"
  4.      query: "/feed/entry"
  5.      XmlRole { name: "title"; query: "title/string()" }
  6.  }

This will access the title value for the first item in the model:
  1. var title = model.get(0).title;

***

Trying to apply the logic I used previously is still gets me nowhere…(undefined)

May 5, 2011

cmer4 cmer4
Lab Rat
109 posts

Nope all methods fail for me…
Where can I read on how-tos cause all existing documentation just didn’t help.

Here is what I ended up with:

  1. Rectangle { // This is Vendor List Block
  2.     id: vendorList
  3.     width: 100; height: 100
  4.     ListModel {
  5.         id: vendorsModelNonXml
  6.         ListElement {
  7.             vendortitle: "test"
  8.         }
  9.         ListElement {
  10.             vendortitle: "test"
  11.         }
  12.         ListElement {
  13.             vendortitle: "test"
  14.         }
  15.     }
  16.     XmlListModel {
  17.         id: vendorsModel
  18.         function populate() {
  19.             var vendortitle = vendorsModel.get(0).vendortitle
  20.             vendorsModelNonXml.append({"vendortitle":vendortitle})
  21.         }
  22.         source: "vendor.xml"
  23.         query: "/vendor/info/title"
  24.         XmlRole { name: "vendortitle"; query: "string()" }
  25.         Component.onCompleted: populate()
  26.         }
  27.     Component {
  28.         id: vendorDelegate
  29.         Text {
  30.            text: vendortitle
  31.            color: "black"
  32.            font.bold: true; font.pixelSize: 18
  33.         }
  34.     }
  35.     ListView {
  36.         id: listView
  37.         anchors.fill: parent
  38.         model: vendorsModelNonXml
  39.         delegate: vendorDelegate
  40.         spacing: 1
  41.         clip: true
  42.     }
  43. }

May 5, 2011

Denis Kormalev Denis Kormalev
Lab Rat
1654 posts

You should do it not in Component.onCompleted, but in onStatusChanged when status will be XmlListModel.Ready

May 5, 2011

cmer4 cmer4
Lab Rat
109 posts

Denis, thanks a lot – that explains things:) I will post here back the working code today’s evening.

I also found a useful article re this here: LINK [mail-archive.com]

May 5, 2011

cmer4 cmer4
Lab Rat
109 posts

  1. onStatusChanged: {
  2.         if (status == vendorsModel.Ready);
  3.         var vendortitle = vendorsModel.get(1).vendortitle
  4.         vendorsModelNonXml.append({"vendortitle":vendortitle})
  5.         }

Here is what solved the issue. The only concern is that even though it works there is a warning in console log saying: Result of expression ‘vendorsModel.get(1)’ [undefined] is not an object.

Any clue what that means? did I miss something else? or I can ignore this message?

May 6, 2011

Denis Kormalev Denis Kormalev
Lab Rat
1654 posts

Change code to

  1. onStatusChanged: {
  2.         if (status == vendorsModel.Ready) {
  3.               var vendortitle = vendorsModel.get(1).vendortitle
  4.               vendorsModelNonXml.append({"vendortitle":vendortitle})
  5.         }  
  6.         }

And please don’t forget to mark thread as [solved] :)

May 10, 2011

cmer4 cmer4
Lab Rat
109 posts

Denis, btw your code didn’t work the way it meant to be, only adding “progress” helped it:

  1. onStatusChanged: {
  2.         if (status == vendorsModel.Ready, progress ==1.0) {
  3.               var vendortitle = vendorsModel.get(1).vendortitle
  4.               vendorsModelNonXml.append({"vendortitle":vendortitle})
  5.         }  
  6.         }

Weird…

 
  ‹‹ How to embed qml-files etc. in executable file? [solved]      Qml Image downloading error ››

You must log in to post a reply. Not a member yet? Register here!