Daniel B. Chapman
Daniel B. Chapman

Reputation: 4687

How do I place dynamic content into a QML component

I'm having a lot of trouble creating extendable components in QML. There are techniques which work but what I'd like to do is something like this:

//Outline.qml
Rectangle {
  color: 'red'
  //Children are inserted here
}

//main.qml
Rectangle {
  ... 
  Outline {
    Text { I'm trying to inject this into the Outline component
      text: "Hi I'm the inner text!"
    }
  }
}

I know I can achieve this by using a Loader component and setting the sourceComponent to a Component instance but when I'm doing anything moderately complex (reusable dialogs for instance) my ability to write functions and reference the instance of a child component is hampered. If the component was instantiated in the same QML file the id/function relationship would be fine and I could simply reference my text fields directly.

Here's an example of the loader:

//Outline.qml
Rectangle {
  id : root
  color: 'red'
  property Component content;
  Loader {
    source : root.content
  }
}

//main.qml
Rectangle {
  function getData(){
     return inner.getData(); 
     //ERROR inner is undefined because it is created by the Loader instance.
  }
  ... 
  Outline {
    content: Component {
        TextField {
          id: inner
          function getData(){
            return inner.text || 'none';
          }
        }
    }
  }
}

I would love an better way to handle it. I know I can build a Component directly inside the parent and achieve a top level reference, but it doesn't allow for the level of control I'm looking for. A 'static' extension of the component would be preferable in this case.

EDIT: here's a QML example that illustrates the problem. On click the output is: ReferenceError: getMe is not defined

import QtQuick 2.2
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.1

Item {
    id: app
    width: 640
    height: 480

    function accessData(){
        output.text = getMe.text
    }

    Loader {
        width: parent.width
        height: 300
        sourceComponent: Component {
            Rectangle {
                anchors.fill: parent
                color: 'yellow'

                TextField {
                    anchors.centerIn: parent
                    id: getMe
                    placeholderText: 'Input data...'
                }
            }
        }
    }


    Button {
        id: button
        y: app.height - 50
        text: 'get data'
        onClicked: {
            accessData();
        }
    }

    Text {
        y: app.height - button.height
        id: output
        text: 'none'
    }
}

As you can see when you use the "loader" method the sourceComponent isn't part of the same tree (you can't access the inner data).

Upvotes: 3

Views: 7769

Answers (1)

Redanium
Redanium

Reputation: 879

This is a working solution for your illustrative example of the problem and later try it in your project

  1. give an id to your Loader ex:" id: yourLoader " and the rectangle inside the loader ex: " id: yourRect "

  2. add a String property to yourRect which will hold your TextField.text

    Loader { id: yourLoader
             width: parent.width
             height: 300
             sourceComponent: Component {
    
         Rectangle {
                     property string yourText : getMe.text
                     id: yourRect
                     anchors.fill: parent
                     color: 'yellow'
    
            TextField {
                anchors.centerIn: parent
                id: getMe
                placeholderText: 'Input data...'
            }
        }
      }
    }
    
  3. we change your function accessData

     function accessData(){
                    var rect= yourLoader.item //we get yourRect from the loader 
                     output.text = rect.yourText // yourText <-- getMe.text
    
     } 
    

Upvotes: 2

Related Questions