Return QJSValue from C++ to qml

I have the following function that returns a javascript array as a QJSValue. But when I call this function from QML, the resulting object is always “undefined”.

class MyClass : public QObject
{
    Q_OBJECT
    Q_PROPERTY (QJSValue test READ test NOTIFY testChanged)

public:
    MyClass() {}

    QJSValue test()
    {
        QJSEngine engine;
        QJSValue l = engine.newArray(5);

        for (int i = 0; i < 5; i++) {
            l.setProperty(i, i);
        }

        return l;
    }

    static void RegisterTypes()
    {
        qmlRegisterUncreatableType<MyClass>("MyTypes", 1, 0, "MyClass", "Creation of MyClass type not supported");
    }

Q_SIGNALS:
    void testChanged();

}

int main() {
    QQmlApplicationEngine engine;

    MyClass mc;
    MyClass::RegisterTypes();
    engine.rootContext()->setContextProperty("myClass", &mc);

    // ...
}

This is almost exactly the way that it is recommended to work with arrays in the Qt Docs on QJSValue.

The following button for example always outputs “undefined”:

Button {
    onClicked: console.log(myClass.test)
}

  • Does it work if you add QML_ELEMENT as shown in this example doc.qt.io/qt-6/qtqml-integrating-with-js-values-from-cpp.html ? Could you share the code that registers the C++ class?

    – 




  • @iam_peter I believe that the QML_ELEMENT macro should not be added to this class as I don’t want MyClass to be instantiable from QML. It exists on the C++ side and then is exposed to QML through engine.setContextProperty(...);. I have added additional information.

    – 

  • @iam_peter Although I have tried it with QML_ELEMENT as well and it does not change the behavior.

    – 

  • Have you verified that code otherwise works, for example using same code except QString instead of QJSValue? Have you verified that test() returns a valid QJSValue (with debugger breakpoint or debug print in that method)?

    – 




  • 1

    @hyde Yes, for example return QJSValue(5) works perfectly. The problem always seems to be with arrays. For example, return engine.evaluate("[1, 2, 3]"); also gives undefined. As far as “Verifying that it’s a valid QJSValue”, the debugger doesn’t reveal any useful information, but l.isArray() returns true and engine.hasError() returns false.

    – 

The important part to answer your question is written in this paragraph. Arrays are stored on the heap of the engine they were created with. In your case you have two separate engines. The one that comes from QQmlApplicationEngine and the other one that you create in the test() function. Both have a heap and are separate from one another, this is why the array is undefined. Primitives aren’t stored on the heap that is why return QJSValue(5) works in your example.

To solve the issue you could forward the “main” engine to your MyClass and use it to create the array.

QQmlApplicationEngine engine;

MyClass mc(&engine);
MyClass::registerTypes();
engine.rootContext()->setContextProperty("myClass", &mc);
class MyClass : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QJSValue test READ test NOTIFY testChanged)

public:
    MyClass(QQmlEngine *engine)
        : m_engine(engine)
    {}

    QJSValue test()
    {
        QJSValue l = m_engine->newArray(5);

        for (int i = 0; i < 5; i++) {
            l.setProperty(i, i);
        }

        return l;
    }

    static void registerTypes()
    {
        qmlRegisterUncreatableType<MyClass>("MyTypes",
                                            1,
                                            0,
                                            "MyClass",
                                            "Creation of MyClass type not supported");
    }

signals:
    void testChanged();

private:
    QQmlEngine *m_engine = nullptr;
};

Here you can find the fully working example.

Leave a Comment