Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. [SOLVED] How to create delegates and ListModel dynamically in JavaScript?

[SOLVED] How to create delegates and ListModel dynamically in JavaScript?

Scheduled Pinned Locked Moved QML and Qt Quick
6 Posts 2 Posters 10.7k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Q Offline
    Q Offline
    Quteroid
    wrote on 4 May 2015, 12:30 last edited by Quteroid
    #1

    Given I have the following QML, which creates a ListView, defines a simple Button delegate and a model:

    ListView {
        anchors.fill: parent
        snapMode: ListView.SnapToItem
        delegate: Component {
            Button {
                height: 32; width: parent.width;
                text: "Button index " + index + ", " + model.first + ", " + model.second; } }
        model: ListModel {
            ListElement { first: "first 1"; second: "second 1"; }
            ListElement { first: "first 2"; second: "second 2"; } } }
    

    [solved] How can I create the same purely (or mostly) in JavaScript (or C++)?

    I have a JavaScript function like this. However, it exhibits several problems, as annotated:

    function createListView (parent) {
     var listView = Qt.createQmlObject ('import QtQuick 2.3; import QtQuick.Controls 1.2; PathView {}', parent);
     var model = Qt.createQmlObject('import QtQuick 2.2; ListModel {}', parent);
    // Problem 1
     var delegate = Qt.createQmlObject ('import QtQuick 2.3; import QtQml 2.2; import QtQuick.Controls 1.2; Component { }', parent);
     var button = Qt.createQmlObject ('import QtQuick 2.3; import QtQuick.Controls 1.2; Button { }', delegate);
    // Problem 2
        listView.snapMode = ListView.SnapToItem;
        listView.anchors.fill = parent;  // This may fail, depending on what parent was given
        model.append ({"first": "first 1", "second": "second 1"});
        model.append ({"first": "first 2", "second": "second 2"});
        listView.model = model
        button.width = parent.width;
        button.height = 32;
    // Problem 3 and problem 4
        button.text = "Button index " + index + ", " + model.first + ", " + model.second;
    // Problem 5
        delegate.??? = button;
        return listView;
    }
    

    Problem 1: trying to create an empty Component fails with:
    Error: Qt.createQmlObject(): failed to create object:
    inline:1:68: Cannot create empty component specification
    I could solve this one by specifying for instance "Component { Item { id: item } }". Then I could add the delegate as child to item. However: how can I reference item within delegate from JavaScript then?

    Problem 2: how do I get access to enum values of ListView (and other components) from JavaScript?

    Problem 3: how to access index in JavaScript, which is available to delegates in QML?

    Problem 4: how to access model in javaScript, which is available to delegates in QML?

    Problem 5: how to populate a delegate Component with custom children?

    I'd be thankful for help on any of these problems. Alternatively, I'd also appreciate a C++ version of this JavaScript function createListView(), if any one has one and these problems are easier to solve in C++ than in JavaScript.

    P Q 2 Replies Last reply 5 May 2015, 06:09
    0
    • Q Quteroid
      4 May 2015, 12:30

      Given I have the following QML, which creates a ListView, defines a simple Button delegate and a model:

      ListView {
          anchors.fill: parent
          snapMode: ListView.SnapToItem
          delegate: Component {
              Button {
                  height: 32; width: parent.width;
                  text: "Button index " + index + ", " + model.first + ", " + model.second; } }
          model: ListModel {
              ListElement { first: "first 1"; second: "second 1"; }
              ListElement { first: "first 2"; second: "second 2"; } } }
      

      [solved] How can I create the same purely (or mostly) in JavaScript (or C++)?

      I have a JavaScript function like this. However, it exhibits several problems, as annotated:

      function createListView (parent) {
       var listView = Qt.createQmlObject ('import QtQuick 2.3; import QtQuick.Controls 1.2; PathView {}', parent);
       var model = Qt.createQmlObject('import QtQuick 2.2; ListModel {}', parent);
      // Problem 1
       var delegate = Qt.createQmlObject ('import QtQuick 2.3; import QtQml 2.2; import QtQuick.Controls 1.2; Component { }', parent);
       var button = Qt.createQmlObject ('import QtQuick 2.3; import QtQuick.Controls 1.2; Button { }', delegate);
      // Problem 2
          listView.snapMode = ListView.SnapToItem;
          listView.anchors.fill = parent;  // This may fail, depending on what parent was given
          model.append ({"first": "first 1", "second": "second 1"});
          model.append ({"first": "first 2", "second": "second 2"});
          listView.model = model
          button.width = parent.width;
          button.height = 32;
      // Problem 3 and problem 4
          button.text = "Button index " + index + ", " + model.first + ", " + model.second;
      // Problem 5
          delegate.??? = button;
          return listView;
      }
      

      Problem 1: trying to create an empty Component fails with:
      Error: Qt.createQmlObject(): failed to create object:
      inline:1:68: Cannot create empty component specification
      I could solve this one by specifying for instance "Component { Item { id: item } }". Then I could add the delegate as child to item. However: how can I reference item within delegate from JavaScript then?

      Problem 2: how do I get access to enum values of ListView (and other components) from JavaScript?

      Problem 3: how to access index in JavaScript, which is available to delegates in QML?

      Problem 4: how to access model in javaScript, which is available to delegates in QML?

      Problem 5: how to populate a delegate Component with custom children?

      I'd be thankful for help on any of these problems. Alternatively, I'd also appreciate a C++ version of this JavaScript function createListView(), if any one has one and these problems are easier to solve in C++ than in JavaScript.

      P Offline
      P Offline
      p3c0
      Moderators
      wrote on 5 May 2015, 06:09 last edited by
      #2

      Hi @Quteroid,
      For the above scenario I would go with C++ models probably QAbstractItemModel. The model can have Q_INVOKABLE functions which can be accessed from QML directly for operations if required.

      157

      Q 2 Replies Last reply 5 May 2015, 08:59
      0
      • P p3c0
        5 May 2015, 06:09

        Hi @Quteroid,
        For the above scenario I would go with C++ models probably QAbstractItemModel. The model can have Q_INVOKABLE functions which can be accessed from QML directly for operations if required.

        Q Offline
        Q Offline
        Quteroid
        wrote on 5 May 2015, 08:59 last edited by
        #3

        @p3c0
        Creating and maintaining the data model in C++ rather than JavaScript is something I will eventually do, indeed. However, this does not answer any of the particular problems I listed. Creating a data model in JavaScript is one of the aspects which actually have not been a big problem. Creating and handling the delegate in JavaScript is the main problem. Any clues on that?

        1 Reply Last reply
        0
        • P p3c0
          5 May 2015, 06:09

          Hi @Quteroid,
          For the above scenario I would go with C++ models probably QAbstractItemModel. The model can have Q_INVOKABLE functions which can be accessed from QML directly for operations if required.

          Q Offline
          Q Offline
          Quteroid
          wrote on 5 May 2015, 09:20 last edited by
          #4

          @p3c0
          To clarify my motivation: I want to create delegates completely dynamically. Based upon both data model, user preferences and other factors. It may be all sorts like:

          • one simple Button (as in the given example),
          • one Text component
          • three Text components
          • one Rectangle, one Button, one Text
          • two Texts, one Image and one Button

          ...and all combinations thereof (and more). You see that providing static QML for all cases is not feasible for this.

          I know that one way would be to synthesize appropriate, complex QML snipplets that compose the requested delegate structures at run time and hand it to Qt.createQmlObject(). However, synthesizing 10 or 20 (or more) lines of QML in JavaScript (or even C++) seems pretty much ugly and inefficient to me and I'd prefer a more direct, API-oriented way, if possible. Is it possible?

          P 1 Reply Last reply 5 May 2015, 10:38
          0
          • Q Quteroid
            5 May 2015, 09:20

            @p3c0
            To clarify my motivation: I want to create delegates completely dynamically. Based upon both data model, user preferences and other factors. It may be all sorts like:

            • one simple Button (as in the given example),
            • one Text component
            • three Text components
            • one Rectangle, one Button, one Text
            • two Texts, one Image and one Button

            ...and all combinations thereof (and more). You see that providing static QML for all cases is not feasible for this.

            I know that one way would be to synthesize appropriate, complex QML snipplets that compose the requested delegate structures at run time and hand it to Qt.createQmlObject(). However, synthesizing 10 or 20 (or more) lines of QML in JavaScript (or even C++) seems pretty much ugly and inefficient to me and I'd prefer a more direct, API-oriented way, if possible. Is it possible?

            P Offline
            P Offline
            p3c0
            Moderators
            wrote on 5 May 2015, 10:38 last edited by
            #5

            @Quteroid
            Problem 1: The only way AFAIK is to add it statically.
            Problem 2: You can't access list of enum values it takes but only what has been assigned by directly querying property.
            Problem 3: Since it is attached property it is accessible only in delegate not outside of it.
            Problem 4: Since you have model object, access it using get() function.
            Problem 5: IMO Not possible to completely add it dynamically in JavaScript as you require.

            import QtQuick 2.4
            import QtQuick.Controls 1.3
            
            Rectangle {
                id: root
                width: 200
                height: 200
            
                function listViewC() {
                    var listView = Qt.createQmlObject ('import QtQuick 2.4; ListView {anchors.fill: parent}', root);
                    var model = Qt.createQmlObject('import QtQuick 2.4; ListModel {}', parent);
                    var comp = Qt.createQmlObject('import QtQuick 2.4; Component { Text { text: "(" + index + ") " +name + " " + cost } }', parent); //Problem 1 same as you said, Problem 3: index only in delgate, Problem 4: Just the only way I know
                    model.append({"cost": 15.95, "name":"Pizza"});
                    model.append({"cost": 3.95, "name":"Doughnut"});
                    listView.model = model;
                    listView.delegate = comp;
            
                    console.log(listView.model.get(1).name) //Problem 4: I hope :)
                    console.log(listView.headerPositioning) //Problem 2
                }
            
                Button {
                    anchors.bottom: parent.bottom
                    text: "Click"
                    onClicked: listViewC()
                }
            }
            

            There could be limitations as to what can be achieved in JavaScript and also it seems to be painful ;)

            157

            1 Reply Last reply
            0
            • Q Quteroid
              4 May 2015, 12:30

              Given I have the following QML, which creates a ListView, defines a simple Button delegate and a model:

              ListView {
                  anchors.fill: parent
                  snapMode: ListView.SnapToItem
                  delegate: Component {
                      Button {
                          height: 32; width: parent.width;
                          text: "Button index " + index + ", " + model.first + ", " + model.second; } }
                  model: ListModel {
                      ListElement { first: "first 1"; second: "second 1"; }
                      ListElement { first: "first 2"; second: "second 2"; } } }
              

              [solved] How can I create the same purely (or mostly) in JavaScript (or C++)?

              I have a JavaScript function like this. However, it exhibits several problems, as annotated:

              function createListView (parent) {
               var listView = Qt.createQmlObject ('import QtQuick 2.3; import QtQuick.Controls 1.2; PathView {}', parent);
               var model = Qt.createQmlObject('import QtQuick 2.2; ListModel {}', parent);
              // Problem 1
               var delegate = Qt.createQmlObject ('import QtQuick 2.3; import QtQml 2.2; import QtQuick.Controls 1.2; Component { }', parent);
               var button = Qt.createQmlObject ('import QtQuick 2.3; import QtQuick.Controls 1.2; Button { }', delegate);
              // Problem 2
                  listView.snapMode = ListView.SnapToItem;
                  listView.anchors.fill = parent;  // This may fail, depending on what parent was given
                  model.append ({"first": "first 1", "second": "second 1"});
                  model.append ({"first": "first 2", "second": "second 2"});
                  listView.model = model
                  button.width = parent.width;
                  button.height = 32;
              // Problem 3 and problem 4
                  button.text = "Button index " + index + ", " + model.first + ", " + model.second;
              // Problem 5
                  delegate.??? = button;
                  return listView;
              }
              

              Problem 1: trying to create an empty Component fails with:
              Error: Qt.createQmlObject(): failed to create object:
              inline:1:68: Cannot create empty component specification
              I could solve this one by specifying for instance "Component { Item { id: item } }". Then I could add the delegate as child to item. However: how can I reference item within delegate from JavaScript then?

              Problem 2: how do I get access to enum values of ListView (and other components) from JavaScript?

              Problem 3: how to access index in JavaScript, which is available to delegates in QML?

              Problem 4: how to access model in javaScript, which is available to delegates in QML?

              Problem 5: how to populate a delegate Component with custom children?

              I'd be thankful for help on any of these problems. Alternatively, I'd also appreciate a C++ version of this JavaScript function createListView(), if any one has one and these problems are easier to solve in C++ than in JavaScript.

              Q Offline
              Q Offline
              Quteroid
              wrote on 5 May 2015, 16:12 last edited by
              #6

              I seem to have found a way to dynamically create delegates for data models via JavaScript. It's not quite straightforward. The key to success is using a JavaScript callback within the Component definition. Like this:

              listView.delegate = Qt.createQmlObject (
                      'import QtQuick 2.3; ' +
                      'import "Init.js" as Init; ' + // Important to import even self!
                      'Component { ' +
                          'Item { ' + // Dummy item
                              'Component.onCompleted: Init.populateDelegate (this, model, index); }}', parent);
              

              Description:

              • the delegate property of a ListView (or PathView or TableView etc.) is assigned a Component that is created via Qt.createQmlObject()
              • the JavaScript file that contains the callback must be imported, too. Even if it is in the same file as the code above! Otherwise QtQuick will throw a ReferenceError. Here, the code is contained in file "Init.js".
              • since no empty Components can be created by Qt.createQmlObject, a dummy Item is added. This is actually a small waste of resources, but hopefully not too expensive.
              • Callback Init.populateDelegate() is assigned to the dummy Item. It is invoked once per instantiated record of the data model used.
              • this, model and index seem reasonable parameters for all sorts of delegates created this way. this is the dummy Item. It should be used as parent in the callback.
              • parent is the parent component in which this listView is to be created.

              The callback JavaScript function that constructs the delegate may then look like this:

              // In a file called "Init.js", as given in above's example
              function populateDelegate (parent, model, index) {
               var button= Qt.createQmlObject ('import QtQuick 2.3; import QtQuick.Controls 1.2; Button { }', parent);
                  parent.height = 32;
                  button.width = parent.width = 256;
                  button.height = parent.height = 32;
                  button.text = "Button index " + index + ", " + model.first + ", " + model.second;
                  console.log ("Model: " + model + ", index: " + index + ", button: " + button);
              }
              

              Note that parent.width and parent.height must be set explicitly! They are the Item's dimensions and determine the dimension of one row / cell in the View. In this simple example they are identical to the only element (Button). Item's dimensions may differ if you either want padding (extra space) or overlapping between rows / cells. Generally they should be the sums of the widths (if you use Row or RowLayout) or heights (if you use Column or ColumnLayout) of all contained items.

              Also note that this approach allows you to use altering delegate constructs for each row. For instance, a single button in the first row, two Texts in the 2nd, an Image in the 3rd, and so on. For instance with code like this in the populateDelegate() function:

              var buildFn;
              switch (index % 3) {
                  case 0: buildFn = createJustOneButton; break;
                  case 1: buildFn = createTwoTexts; break;
                  case 2: buildFn = createImageOrWhateverElse; break;
                  default: { console.assert (false, "Impossible case!"); Qt.quit (); } }
              buildFn (parent, model, index); // Build delegate contents for one row
              

              or even much more compact and fancier, as a single statement:

              [createJustOneButton, createTwoTexts, createImageOrWhateverElse][index % 3] (parent, model, index);
              

              Quite a gain of flexibility; isn't it?

              1 Reply Last reply
              0

              6/6

              5 May 2015, 16:12

              • Login

              • Login or register to search.
              6 out of 6
              • First post
                6/6
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved