0
votes

I have a hierarchy of nested QML components, and would like to set some values in the inner components (e.g. the color of items).

What I think I would need to do is pass arguments to the next inner components, which then in turn forward parts of these data to their children, etc, until the recipient is reached. This would honor the idea of encapsulation.

However, I struggle to implement this in QML/JS. First, I am not sure how to export a function so that it can be called from outside the component (in a property var? I tried this but got an error 'JavaScript declaration outside Script element'). Second, I am not sure how to call functions for the elements within the Repeater. Finally, maybe there is a more straightforward way to implement this altogether?

Here is a MWE that conveys what I am trying to achieve:

File mwe.qml

import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.2

Window {
    id: root
    visible: true
    width: 640
    height: 480

    ColumnLayout {
        Mwe2 {
            id: m2
        }

        Button {
            text: "Test"
            onClicked: {
                var colors = [];
                var letters = '0123456789ABCDEF';
                for (var i = 0; i<12; i++) {
                    var color = '#';
                    for (var j = 0; j < 6; j++) {
                        color += letters[Math.floor(Math.random() * 16)];
                    }
                    colors.push(color);
                }
                console.log(colors);
                m2.setColor(colors);
                // call function in m2 with colors as argument
            }
        }
    }
}

File Mwe2.qml:

import QtQuick.Layouts 1.2

RowLayout {
    spacing: 2
    // somehow export setColors... I tried var this does not work
    //property alias setColors: setColors_
    Repeater {
        id: rect
        model: 3
        ColumnLayout {
            Rectangle {
                color: 'red'
                width: 50
                height: 20
            }
            Mwe3 { id: m3}
        }
    }
    function setColors(colors) {
        // loops over repeater items, passing
        // slice of colors array to function in m3
    }
}

File Mwe3.qml:

import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.2

RowLayout {
    spacing: 2
    Repeater {
        id: rect2
        model: 4
        Rectangle {
            color: 'orange'
            width: 50
            height: 20
        }
    }
    // function that sets the colors in the repeater items,
    // using something like rect2.itemAt(i).child(0).color = ...
}
1
Typo: change to m2.setColors(colors); - eyllanesc
I think your design is poor, what is your ultimate goal? Why do you have nested Repeater. Give a real example since your fictional example is very confusing. - eyllanesc

1 Answers

0
votes

Here's one way. Note that I moved the ColumnLayout part into Mwe3.qml to make it part of the delegate -- this simplifies access quite a bit. It's fairly straight-forward after that, just loop over the repeater children. "mwe1.qml" remains unchanged (except fixing the "setColors" typo as noted in comments), so I'm not posting it.

Mwe2.qml

import QtQuick 2.9
import QtQuick.Layouts 1.2

RowLayout {
    spacing: 2
    Repeater {
        id: rect
        model: 3
        delegate: Mwe3 { }
    }
    function setColors(colors) {
      // loops over repeater items, passing
      // slice of colors array to function in m3
      for (var i=0; i < rect.count; ++i)
        rect.itemAt(i).setColors(colors.slice(i, i+3));
    }
}

Mwe3.qml

import QtQuick 2.9
import QtQuick.Layouts 1.2

ColumnLayout {
    Rectangle {
        color: 'red'
        width: 50
        height: 20
    }
    RowLayout {
        spacing: 2
        Repeater {
            id: rect2
            model: 4
            delegate: Rectangle {
                color: 'orange'
                width: 50
                height: 20
            }
        }
    }

    function setColors(colors)
    {
      console.log(colors);
      for (var i=0; i < rect2.count && i < colors.length; ++i) {
        console.log(rect2.itemAt(i));
        rect2.itemAt(i).color = colors[i];
      }
    }
}

You may also be interested in an alternate approach using a data model. I posted one example of such (also using colors as it happens) here: How can I get property in gridview which is Repeater's child