[Solved] Dynamic sourceComponent of Loader and changing property of components



  • My requirement is to make a ListView of various types of components reading componentType and its properties from an XML file. I could create the components in the ListView, but could not change/set properties of that component. Below is the xml and qml files.

    Components.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    	<myComponent>
    		<component>
    			<componentType>component1</componentType>
    			<buttonText>Button1</buttonText>
    		</component>
    		<component>
    			<componentType>component2</componentType>
    			<text>Text1</text>
    		</component>
    		<component>
    			<componentType>component1</componentType>
    			<buttonText>Button2</buttonText>
    		</component>
    		<component>
    			<componentType>component2</componentType>
    			<text>Text2</text>
    		</component>
    	</myComponent>
    

    TestXml.qml

    import QtQuick 2.2
    import QtQuick.Controls 1.2
    import QtQuick.XmlListModel 2.0
    
    Rectangle {
        width: 200
        height: 360
    
        XmlListModel {
            id: xmlModel
            source: "Components.xml"
            query: "/myComponent/component"
    
            XmlRole { name: "componentType"; query: "componentType/string()" }
    
            XmlRole { name: "buttonText"; query: "buttonText/string()" }
            XmlRole { name: "txt"; query: "text/string()" }
    
            onStatusChanged: {
                if (status === XmlListModel.Loading) {
                    console.log("Loading...")
                }
                if (status === XmlListModel.Ready) {
                    console.log("Loaded " + source)
                }
                if (status === XmlListModel.Error) {
                        console.log("Xml Error: " + errorString())
                }
            }
        }
    
        ListView {
            id: listView
            anchors.fill: parent
            model: xmlModel
    
            delegate: delegateComponent
    
            Component {
                id: component1
    
                Button {
                    width: parent.width
                    height: 50
    //                text: buttonText
                }
            }
    
            Component {
                id: component2
    
                Text {
                    color: "red"
    //                text: txt
                }
            }
    
            Component {
                id: delegateComponent
    
                Loader {
                    id: loader
                    width: parent.width
                    sourceComponent: (componentType === "component1") ? component1 : component2
    
                    Component.onCompleted: {
                        if (componentType === "component1") {
                            text: buttonText
                        }
                        else if (componentType === "component2") {
                            text: txt
                        }
                    }
                }
            }
        }
    }
    

    Setting text to buttonText for component1 from Loader Component.onCompleted does not change the text.
    I am stuck here. Please help me.


  • Moderators

    Hi

    Since you are using a Loader you can access the loaded Component using item. So

    Component.onCompleted: {
        if (componentType === "component1") {
            loader.item.text = buttonText
        }
        else if (componentType === "component2") {
            loader.item.text = txt
        }
    }
    

    P.S: The editor in this new forum using markdown syntax. So to add a code you can use ``` (3 backticks) with the same followed at the end.



  • Hi,
    Thanks a lot. How do I flag it solved?


  • Moderators

    You're Welcome :)
    There's a topic combobox at the bottom. It has the "Mark Solved" option.



  • By the way, if I want to change the properties of the components loaded by the loader out of the Loader block, how can that be done? As I understand there is no id of the instances of the components.


  • Moderators

    @myQtQml in the same way as earlier. Loader would be accessible outside too.



  • item is the present item being loaded. loader.item.text would give text of which component? There are 4 components loaded by the loader in the above example.


  • Moderators

    Ok understood, so in that case to access delegates from outside ListView, you will need to use children or you can set an item as current item and then access it

    listview.currentIndex=1 // 1 is index of delegate, listview = id of ListView
    console.log(listview.currentItem)
    


  • I have added the following code to access text of the components

        Button {
            width: 100; height: 40; text: "Get Text";
            anchors.top: listView.bottom
            onClicked: {
                listView.currentIndex = 2   // or anything else (0 - 3)
                console.log(listView.currentIndex, listView.currentItem.text)
            }
        }
    

    The output (listView.currentItem.text) is alway undefined for any value of listView.currentIndex. Am I doing anything wrong?


  • Moderators

    Ok. Now you have to go further. The currentItem will be the Loader. Try getting children of it

    console.log(listView.currentIndex, listView.currentItem.children)
    

    and then try accessing the childrens. It is an array.



  • Working. Thanks again. I don't see any "Mark Solved" option in Topic Tools combobox.


  • Moderators

    Well I do see it. Its the bottom entry in the combobox . I'll mark it as solved.


  • Moderators

    Strange I'm to not able to mark it as solved. Clicking doesn't have any effect.



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.