Search code examples
qmlsignal-handlingqqmlcomponent

QML connect signal from the main component to the child components


I have a Qt application that calls qt_update_values() to the main QML component. I want to send the new values to a specific delegate. How do I connect update_values() from the main component to be received by a specific child component, which is defined in another qml?

I have tried defining Connections in the child, but I am not sure what target I need to define...

In the main.qml i have something similar to this:

...
signal update_values(new_values)

function qt_update_values(newValues){
     update_values(newValues);
}

Repeater {
     id:idRepeater
     model: 3

     Rectangle {
         id:example

         Text{ text: "hello"}
         ...

         AnotherComponent {name: "name", othervariables: "others"}
     }
}
...

Then on AnotherComponent.qml i have:

...
signal update_values_child(new_values)

function onUpdate_values(newValues){
     textid = newValues;
}

Text{ id:textid}
...

Solution

  • You don't connect from the parent to the main, but the other way around like this:

    ...
    id: idOfTheParent  // <=== THIS IS IMPORTANT
    signal update_values(new_values)
    
    function qt_update_values(newValues){
         update_values(newValues);
    }
    
    Repeater {
        id:idRepeater
        model: 3
    
        Rectangle {
            id:example
    
            Text{ text: "hello"}
            ...
    
            AnotherComponent {
                id: idOfAnotherComponent // This ID is only available in the 
                                         // scope of the Component
                                         // that will be instantiated by the
                                         // Repeater, i.e. children of the Rectangle
                name: "name"
                othervariables: "others"
            }
            Connections {
                target: idOfTheParent
                onUpdate_values: idOfAnotherComponent.dosomethingWith(new_values)
            }
        }
    }
    ...
    

    You could also use signal.connect() to add new connections

    Repeater {
        model: 10
        delegate: Item { ... }
        onItemAdded: {
            idOfTheParent.update_values.connect(function() { // do what you want })
        }
    }
    

    But if it is only broadcasting of a new value, the declarative way would be, to have properties in your delegate, and bind them to the properties that held the changeing values:

    ...
    id: idOfTheParent
    property var valueThatWillChange
    
    Repeater {
        model: 10
        delegate: Item {
            property int valueThatShallChangeToo: idOfTheParent.valueThatWillChange
        }
    }
    ...
    

    To have all of it with different signals ofc. is possible:

    For the Connections-solution the simplest thing is to call doSomething only if it is the right delegate instance:

    // in the delegate
    Connections {
        target: idOfTheParent
        onValue1Updated: if (index === 1) doYourStuff()
        onValue2Updated: if (index === 2) doYourStuff()
        onValue...
    }
    

    But this is easier with the second method:

    id: idOfTheParent
    Repeater {
        model: 10
        delegate: SomeItem {
            function doSomething() { console.log(index, 'does something')
        }
        onItemAdded: {
            idOfTheParent['value' + index + 'Updated'].connect(item.doSomething)
        }
        onItemRemoved: {
            idOfTheParent['value' + index + 'Updated'].disconnect(item.doSomething)
        }
    }