7
votes

I want to access my QML elements by ID from the main file but it is not working. In my test (case) environment I have 2 files:

  1. main.qml , this is the file from which I want to access by ID
  2. Accounts.qml, this is the file where my element is defined

main.qml

import QtQuick 2.7
import QtQuick.Controls 2.0

Item {

    Accounts {
        id: accounts
    }
    Component.onCompleted: {
        console.log(accounts);
        console.log(accounts.accounts_pane);
        console.log(accounts.list_area);
        console.log(accounts.accounts_pane.list_area.list_column.list_identities.model);
    }
}

Accounts.qml

import QtQuick 2.7
import QtQuick.Controls 2.0

Pane {
    id: accounts_pane
    anchors {
        fill: parent
    }    
    Rectangle {
        id: list_area
        border.color: "black"
        border.width: 2
        anchors {
            left: parent.left
            right: parent.right
            top: parent.top
            bottom: parent.bottom
        }
        Column {
            id: list_column
            ListView {
                id: list_identities
                width: list_area.width
                height: 200
                model: ListModel {
                    ListElement {
                        name: "Bill Smith"
                        number: "555 3264"
                    }
                    ListElement {
                        name: "John Brown"
                        number: "555 8426"
                    }
                }
                delegate: Text {
                    text: name + ',' + number
                }
            }
        }
    }
}

So, from the main.qml file I want to change the model of the Accounts (different Company uses different accounts), and I need to do it with Javascript. (My model is a C++ class, registered within the QML engine but I will be using Javascript to switch different model objects). The notation to access the model, as I understand it, should be something like this:

accounts.accounts_pane.list_area.list_column.list_identities.model

However, when testing it, with console.log():

    console.log(accounts);
    console.log(accounts.accounts_pane);
    console.log(accounts.list_area);
    console.log(accounts.accounts_pane.list_area.list_column.list_identities.model);

I can't pass past the Pane {} element, which is defined in Accounts.qml. The output of qmlscene shows me these errors:

 hier $ qmlscene main.qml 
qml: QQuickPane(0x13b85f0)
qml: undefined
qml: undefined
file:///QT_snippets/qmlscenes/hier/main.qml:13: TypeError: Cannot read property 'list_area' of undefined

How can you access QML elements by ID arbitrarily from the main.qml document?

1

1 Answers

6
votes

Preferably you should only use the id to access objects in the same QML file. It is also possible to access objects via ids as long as you are in component scope that is nested inside the component that has the id, as long as it is not shadowed by anything with the same name. In short, ids are only accessible from the inside, but never from the outside, the latter being your usage scenario.

If you want to access elements from other QML files, they must be exposed as properties. Also, properties defined in the root QML file will be visible from the entire project, as long as an identically named property shadows it.

Think of every object in a QML file as a private, whereas properties are public. if you want to access one of those private objects, expose it as a property.

property Item someItem : _item
Item { id: _item } 

In your case accounts_pane is redundant, because it will be the same as accounts - it is the same object.

If you expose list_identities directly you don't need to expose all the previous objects in order to access it:

accounts.accounts_pane.list_area.list_column.list_identities.model // no need
accounts.list_identities.model // all you need

Additionally, you can expose specific properties of specific objects as aliases:

// in Accounts.qml
property alias model: list_identities.model
// then you can simply
accounts.model

Lastly, consider this approach to be wrong, you shouldn't need to expose everything for outside use, you should define an interface through which to control the internals of each QML component.

The interface of a Component contains all the top level properties it defines.