James Hudson
James Hudson

Reputation: 904

How do you dynamically load multiple components with QML?

I have a sample project at: https://github.com/jh3010-qt-questions/dynamic_loading

I have read https://doc.qt.io/qt-5/qtqml-javascript-dynamicobjectcreation.html which explains how to load a component dynamically. I have implemented the method described in the documentation in my sample project and it works.

However, because the method is based on global variables, it is unclear how it can work when I am loading many component dynamically and asynchronously. If I wanted to loop over componentNames and load each component dynamically, how would I construct this asynchronous code?

main.qml

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12

ApplicationWindow
{
  id: appWindow

  width:    640
  height:   480
  visible:  true
  title:    qsTr("Hello World")

  property var component
  property var componentObject
  property var componentNames: [ "ComponentA.qml", "ComponentB.qml", "ComponentC.qml" ]

  function finishCreation()
  {
    console.log( "finish creation" )

    if ( component.status === Component.Ready )
    {
      componentObject = component.createObject( contentColumn );

      if ( componentObject === null )
      {
          // Error Handling
          console.log("Error creating object");
      }
    }
    else if ( component.status === Component.Error )
    {
        // Error Handling
        console.log("Error loading component:", component.errorString());
    }
  }

  Connections
  {
    target: appWindow

    Component.onCompleted:
    {
      component = Qt.createComponent( `qrc:/${componentNames[0]}` );

      if ( component.status === Component.Ready )
      {
        console.log( "ready" )

        finishCreation();
      }
      else
      {
        component.statusChanged.connect( finishCreation );
      }


      console.log( "completed window" )
    }
  }

  Item
  {
    anchors.fill: parent

    Column
    {
      id: contentColumn

//      ComponentA {
//      }

//      ComponentB {
//      }

//      ComponentC {
//      }
    }
  }
}

qml.qrc

<RCC>
    <qresource prefix="/">
        <file>main.qml</file>
        <file>qtquickcontrols2.conf</file>
        <file>ComponentA.qml</file>
        <file>ComponentAForm.ui.qml</file>
        <file>ComponentB.qml</file>
        <file>ComponentBForm.ui.qml</file>
        <file>ComponentC.qml</file>
        <file>ComponentCForm.ui.qml</file>
    </qresource>
</RCC>

Upvotes: 0

Views: 1177

Answers (1)

JarMan
JarMan

Reputation: 8277

One way to do it is to localize the scope of the functions/variables. You can nest functions within other functions so that they can still reference common variables, but subsequent calls to the outer function won't interfere.

function generateObjects() {
    function generateOneObject(name) {
        var component
        var componentObject

        function finishCreation() {
            componentObject = component.createObject( contentColumn );
        }

        component = Qt.createComponent(`qrc:/${name}`)

        if (component.status === Component.Ready) {
            finishCreation()
        } else {
            component.statusChanged.connect( finishCreation );
        }
    }

    for (var index in componentNames) {
        generateOneObject(componentNames[index])
    }
}

Component.onCompleted: {
    generateObjects()
}

Upvotes: 1

Related Questions