Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Accessing child properties in a Component type



  • Hi All,
    Please have a look into the following code snippet.
    I have a conditional statement to load one of the component defined below. I can switch between the components by using the Switch button. On switching I want to read the text data from the text field in the already visible component, print the text to console and then switch to the other component.

    The issue I am facing is to read the text from the text field from the active component.

    import QtQuick 2.5
    import QtQuick.Layouts 1.3
    import QtQuick.Controls 2.2
    
    Item {
        ColumnLayout {
            id: cl
            Switch {
                id: switchButton
                checked: true
                onCheckedChanged: {
                    ***console.log the text from loaded component***
                    ldr.sourceComponent = checked === true ? c1 : c2
                }
            }
            Text {
                id: label
                text: "Text field"
            }
            Loader {
                id: ldr
                sourceComponent: switchButton.checked === true ? c1 : c2
            }
        }
    
        Component {
            id: c1
            TextField {
                id: tf1
                placeholderText: "Text Input 1"
                width: 400
                height: 50
                background: Rectangle {
                    anchors.fill: parent
                    border{
                        color: "#252525"
                        width: 1
                    }
                    radius: 2
                }
            }
        }
    
        Component {
            id: c2
            TextField {
                id: tf2
                placeholderText: "Text Input 2"
                width: 400
                height: 50
                background: Rectangle {
                    anchors.fill: parent
                    border{
                        color: "#252525"
                        width: 1
                    }
                    radius: 2
                }
            }
        }
    }
    

    One solution could be to have a string property as part of the root Item element and update the same on change text in any of the text field

    Item {
        property string textData: ""
        :
        :
        :
        Component {
            TextField {
                 .
                 .
                 .
                 onTextChanged: {
                       textData  = text
                 }
            }
        }
    }
    

    Just wanted to know, if there is any proper solution for this situation.



  • @BikashRDas said in Accessing child properties in a Component type:

    Just wanted to know, if there is any proper solution for this situation.

    I would do it like this:

    • add a property alias on each Component declaration
    • setup text value in onLoaded event

    Like this:

    import QtQuick 2.5
    import QtQuick.Layouts 1.3
    import QtQuick.Controls 2.2
    
    Item {
        ColumnLayout {
            id: cl
            Switch {
                id: switchButton
                checked: true
                onCheckedChanged: {
                    ldr.sourceComponent = checked === true ? c1 : c2
                }
            }
            Text {
                id: label
                text: "Text field"
            }
            Loader {
                id: ldr
                sourceComponent: switchButton.checked === true ? c1 : c2
                onLoaded: {
                    // read current value
                    item.text = label.text
                    // uddate value on text changes
                    item.text = Qt.bindings(Qt.binding(function () { return label.text; })
                }
            }
        }
    
        Component {
            id: c1
            
            property alias text: tf1.text
            TextField {
                id: tf1
                placeholderText: "Text Input 1"
                width: 400
                height: 50
                background: Rectangle {
                    anchors.fill: parent
                    border{
                        color: "#252525"
                        width: 1
                    }
                    radius: 2
                }
            }
        }
    
        Component {
            id: c2
            property alias text: tf2.text
            TextField {
                id: tf2
                placeholderText: "Text Input 2"
                width: 400
                height: 50
                background: Rectangle {
                    anchors.fill: parent
                    border{
                        color: "#252525"
                        width: 1
                    }
                    radius: 2
                }
            }
        }
    }
    


  • @KroMignon said in Accessing child properties in a Component type:

    property alias text: tf2.text

    give error, Component objects cannot declare new properties.



  • You can access properties in your top level item inside the component through item:

                onCheckedChanged: {
                    console.log(ldr.item.placeholderText)
                    ldr.sourceComponent = checked === true ? c1 : c2
                }
    


  • @BikashRDas said in Accessing child properties in a Component type:

    give error, Component objects cannot declare new properties.

    Oh my bad, sorry.
    In fact it is much easier as @fcarney already explained.



  • @fcarney said in Accessing child properties in a Component type:

    console.log(ldr.item.placeholderText)

    I replaced the "placeholdertext" with "text". It's working. Thanks.
    Is there anyway to make the text data in the active component, available to other QML?
    E.g. Suppose the above code is part of MyTextField.qml.
    In my main. qml, I am creating an instance of MyTextField component like,

    in Main.qml

    Window {
        .
        .
        .
        MyTextField {
            id: mtf
            .
            .
            .
        }
    
        onSomthing : {
            // mtf.textData refers to the text property of the textfield of active component in MyTextField.qml
            console.log(mtf.textData)
        }
    }
    


  • @BikashRDas said in Accessing child properties in a Component type:

    Is there anyway to make the text data in the active component, available to other QML?
    E.g. Suppose the above code is part of MyTextField.qml.

    Yes, I think here using property alias should do the job:

    import QtQuick 2.5
    import QtQuick.Layouts 1.3
    import QtQuick.Controls 2.2
    
    Item {
        property alias textData: ldr.item.text
        ...
    }
    


  • @KroMignon said in Accessing child properties in a Component type:

    @BikashRDas said in Accessing child properties in a Component type:

    Is there anyway to make the text data in the active component, available to other QML?
    E.g. Suppose the above code is part of MyTextField.qml.

    Yes, I think here using property alias should do the job:

    import QtQuick 2.5
    import QtQuick.Layouts 1.3
    import QtQuick.Controls 2.2
    
    Item {
        property alias textData: ldr.item.text
        ...
    }
    

    I had tried this earlier. getting this error. qrc:/MyTextBox.qml:7 Invalid alias target location: text



  • @BikashRDas said in Accessing child properties in a Component type:

    I had tried this earlier. getting this error. qrc:/MyTextBox.qml:7 Invalid alias target location: text

    One other solution would be to use another trick:

    import QtQuick 2.5
    import QtQuick.Layouts 1.3
    import QtQuick.Controls 2.2
    
    Item {
        property alias textData: ldr.textData
        ColumnLayout {
            id: cl
            Switch {
                id: switchButton
                checked: true
                onCheckedChanged: {
                    console.log(ldr.item.placeholderText)
                    ldr.sourceComponent = checked === true ? c1 : c2
                }
            }
            Text {
                id: label
                text: "Text field"
            }
            Loader {
                id: ldr
                property var textData
                sourceComponent: switchButton.checked === true ? c1 : c2
                onLoaded: {
                    // read current value
                    textData = item.text
                    // uddate value on text changes
                    textData = Qt.bindings(Qt.binding(function () { return item.text; })
                }
            }
        }
    ...
    }
    

    But you can only read it from outside, not write on it.



  • @KroMignon Okay got it. Thanks for the reply. This trick will solve my requirement. But it would be good to know if there is any appropriate way to be able to do both read and write.

    Another question is, hope you have noticed, initially I had posted an approach to use a property as part of root Item and update the same on on text change in the texfield of active components. In the approaches (mentioned by you and one by me) we can have a read access to the text data of the text field. Just wanted to know if there is any advantage/disadvantage of these 2 approaches.



  • @BikashRDas If you need to have a read/write access you could do it like this:

    import QtQuick 2.5
    import QtQuick.Layouts 1.3
    import QtQuick.Controls 2.2
    
    Item {
        id: _root
        property string textData
        onTextDataChanged: {
            if(ldr.status === Loader.Ready)
                ldr.item.text = textData
        }
        ColumnLayout {
            id: cl
            Switch {
                id: switchButton
                checked: true
                onCheckedChanged: {
                    console.log(ldr.item.placeholderText)
                }
            }
            Text {
                id: label
                text: "Text field"
            }
            Loader {
                id: ldr
                property string textData
                onTextDataChanged: _root.textData = textData
                sourceComponent: switchButton.checked === true ? c1 : c2
                onLoaded: {
                    // read current value
                    textData = item.text
                    // update value on text changes
                    textData = Qt.bindings(Qt.binding(function () { return item.text; })
                }
            }
        }
    ...
    }
    

    I think there is no risk about "binding loop", but I don't guarantee anything... Try it out.


Log in to reply