9

I have a width property on a QML Rectangle that is set based on another Rectangle with an id of mainwindow and one of the array properties of mainwindow:

width: mainwindow.width/mainwindow.numColsPerRow[positionRow]

This works at the time my rectangle is setup; that is, the element inside the array numColsPerRow is correctly involved.

However, after this Rectangle is setup, if I change the values inside numColsPerRow the width of this Rectangle does not have any effect.

Does QML not allow property bindings to array elements?

johnbakers
  • 24,158
  • 24
  • 130
  • 258
  • I don't think you can bind to an array this way. More info here: http://qt-project.org/doc/qt-5.0/qtqml/qml-variant.html#storing-arrays-and-objects You should use a `Model` to store dynamic data that you want your items to bind to. – koopajah Oct 25 '13 at 10:46
  • @koopajah that example is using `variant` however if you use your arrays as `var` instead, the notion that you cannot modify the array elements directly doesn't apply. `variant` is obsolete in favor of ordinary javascript `var`, though this still doesn't address the binding issue. – johnbakers Oct 25 '13 at 10:51
  • > Yes I know but I did not find another documentation explaining that binding to array values did not work. I really think you should use a `ListModel` for this – koopajah Oct 25 '13 at 11:13

2 Answers2

17

Values in a var JS array don't emit and 'changed' signal when you call :

my_array  [n] = value;

In order to get the array property notified to every code using it you must use this trick :

var tmp =  my_array;
tmp [n] = value; // you can do multiple changes, and also push/splice items
my_array = tmp;

This way, QML engine will emit the signal and other bindings using my_array will be notified and updated.

PS: you can't use a ListModel for this, because you won't have a way to get a particular item in the model using a key like array or map do. Models are meant to be used with a MVC view...

TheBootroo
  • 7,408
  • 2
  • 31
  • 43
0

An alternative to @TheBootroo's fine solution, that avoids the redundant tmp variable: turns out you can call the "changed" signal yourself.

For example: mainwindow.numColsPerRowChanged()

There's no need to use emit and the technique can be used within a QML component. For example:

Item {
    id: root

    property int selectedSetting: 0
    property var selectedOptions: [1, 0, 3, 1]

    Keys.onPressed: {
        event.accepted = true
        switch (event.key) {
            case Qt.Key_Left:
                selectedOptions[selectedSetting] -= 1
                root.selectedOptionsChanged(); //force the signal so bound properties will pick up the change
                break;
            case Qt.Key_Right:
                selectedOptions[selectedSetting] += 1
                root.selectedOptionsChanged() //force the signal so bound properties will pick up the change
                break;
        }
    }
}

For another application of this technique, see: QML: How to trigger onChanged in custom Component?

Heath Raftery
  • 3,643
  • 17
  • 34