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

QML assign model attribute data dynamically to delegate component.



  • Hi,
    i have the following scenario.
    I have a ListView with a custom delegate.
    Inside the delegate i have N components of the same type which should display a dynamically assigned attribute from the model.

    In my delegate if i do something like this:

     GridLayout {
                id: customColumnsLayout
                columns: 2
                DataField { id: dataField0 }
                DataField { id: dataField1 }
                DataField { id: dataField2 }
                DataField { id: dataField3 }
                DataField { id: dataField4 }
                DataField { id: dataField5 }
    }
    

    and in the Component.onCompleted method i want to assign each data field dynamically an attribute from my model.
    Here my pseudo code:

    if (condition 1) {
        dataField1.value = MODEL_ATTRIBUTE_A
    } else if (condition 2) {
      dataField1.value = MODEL_ATTRIBUTE_B
    }
    

    MODEL_ATTRIBUTE_A + MODEL_ATTRIBUTE_B are official model attribute names.
    If i debug MODEL_ATTRIBUTE_A and MODEL_ATTRIBUTE_B correct model values are there.

    Now to the problem.
    If i change the model the data gets not updated in the dataFields....

    If i assign a fixed model attribute to a dataField it works perfectly, like so:

     DataField { id: dataField20; value:  MODEL_ATTRIBUTE_A }
    

    Now if the model changes the value get updated. But if i dynamically assign a MODEL_ATTRIBUTE_A to a dataFields the updates get not reflected.

    My DataField looks like:

    import QtQuick 2.0
    
    Rectangle {
    
            id: root
    
            property alias value: valueText.text
        
            Text {
                id: valueText
                anchors.fill: parent         
                text: root.value
            }
    }
    

    Any ideas?

    Greetings
    Nando


  • Moderators

    @Nando said in QML assign model attribute data dynamically to delegate component.:

    if (condition 1) {
    dataField1.value = MODEL_ATTRIBUTE_A
    } else if (condition 2) {
    dataField1.value = MODEL_ATTRIBUTE_B
    }

    Using equals sign = breaks the binding, so values stop updating. You can use Binding component to manually set up a new binding when necessary.

    Or, you can prepare a different initial binding, like this:

    DataField { id: dataField20; value:  (condition 1)? MODEL_ATTRIBUTE_A : MODEL_ATTRIBUTE_B }
    

    Then it should work and there will be no need for Component.onCompleted.



  • @sierdzio Hey and thank you.
    I have a lot of conditions so i think i have to use the Binding component method.

    Now i tried this. And initially all is fine. But then when i change the model ALL components get updated with the same model data (the last one in the list)...
    It seems somehow the binding applies also to all previous binded component... But why?

    
    function getRoleByOfficialColumnName(officialColumnName)
    {
            if        (officialColumnName === "Column_Name_A") {
                return MODEL_ATTRIBUTE_A
            }
            else if   (officialColumnName === "Column_Name_B") {
                return MODEL_ATTRIBUTE_B
            }
            else if   (officialColumnName === "Column_Name_n") {
                return MODEL_ATTRIBUTE_n
            }
    }
    
    var component = Qt.createComponent("DataField.qml");
    
    for (var i = 0; i < 12; ++i) {
                var column = getColumnDataByPosition(i)
    
                var officialColumnName = column.officialName
                
                var options = {
                    "value": Qt.binding(function() { return getRoleByOfficialColumnName(officialColumnName) }),
                    "title": column.customName,
                    "visible": column.visibleInList,
                };
                component.createObject(customColumnsLayout, options);
    }
    

    Could you please give a short example on how to use the Binding component?

    Greets
    Nando


  • Moderators

    I don't understand your code. Aren't you using a subclass of QAbstractItemModel for this? You definitely should.

    Generally speaking, delegates are created and removed by QML views on the fly. You have to relay on values passed to the delegate from the model at the time they are created. If a piece of data changes, the model should fire dataChanged() signal, then the UI will update automatically (your delegate will be redrawn). If you are doing it any other way to save time, you'll end up spending much longer on it ;-)



  • @sierdzio Hi,
    yes i do use QAbstractItemModel on the C++ side and all the stuff dataChanged and so on.
    And in general when using a ListView with my custom delegate and my model (to paint the list items individually) it works fine.

    Now i need the following: I want the user to be able to configure the delegate in that way that there are N DataFields in a GridLayout 1-10 and the user can configure in which "DataField cell 1-10" which model data gets displayed. So i need a way to dynamically change the binding from the underlying model attribute (role) to the DataFields in the GridLayout.
    For example on position 0,0 in the grid layout the DataField should display the model role data MODEL_ATTRIBUTE_C and in the cell 0,1 it should display MODEL_ATTRIBUTE_A
    and maybe later the user decides to change it so that in cell 0,0 will be MODEL_ATTRIBUTE_F and in cell 0,1 MODEL_ATTRIBUTE_A and so on...
    So i need a way to make this binding which model attribute (role) is bind to which of the 10 cells dataFields....

    I hope you understand what i mean...


  • Moderators

    Sounds like it's something you should implement in the model. The view, and the delegates, should be as dumb as possible. They should only show what the model tells them to. So I think all the user-made changes should be sent to model, processed and then send back to QML as dataChanged() so the delegate can redraw itself (if that is not enough you can remove row + add row, or reset whole model).

    In order to get a flexible structure to read data from in QML, use QVariantMap, or a custom QObject or Q_GADGET. This way you return one "thing" for one role, but it can be unwrapped and QML can get more info out of it.


Log in to reply