How to define a "template" with child placeholders in QML?

leemes picture leemes · Sep 18, 2012 · Viewed 12.4k times · Source

I really like QML. I like how I can define components (comparable to classes) and their properties, and instantiate them from somewhere else (comparable to objects).

I can define, let's say, a button, having some look and feel, and a label text on it. This could be done, for example, using this component definition (Button.qml):

Item {
    id: button
    property string label

    anchors.fill: parent

    Rectangle {
        anchors.fill: parent
        radius: 10
        color: "gray"

        Text {
            anchors.centerIn: parent
            font.pixelSize: 20
            text: button.label
            color: "white"
        }
    }
}

and instanciated in this main file (main.qml):

Rectangle {
    width: 300
    height: 200

    Button {
        anchors.centerIn: parent
        anchors.margins: 50
        label: "Hello button!"
    }
}

But I see the following restriction: I can only define a button template with some properties, not with some placeholder. All children defined in the instance will be direct children, at least per default, and I want to change this behavior.

Let's say I want to place an item (let's say an image, but I don't want to tell the definition of Button that it will be an image) in the button. I imagine something like this:

Item {
    id: button
    property Item contents   <-- the client can set the placeholder content here

    anchors.fill: parent

    Rectangle {
        anchors.fill: parent
        radius: 10
        color: "gray"

        Item {
            id: placeholder     <-- where the placeholder should be inserted
        }
    }

    Component.onCompleted: {
        // move the contents into the placeholder...
    }
}

How can I achieve this? I don't know if using Component.onCompleted is the correct way. Note that, however, that in my case the contents will never change afterwards (at least in my current design of the application...).

Also, I want anchoring to work within the placeholder. For example, if I define the contents to be a Text element, being centered in its parent (which will first be the template itself). Then my code moves this Text instance into the placeholder and the parent anchors should then be those of the placeholder item, not the template item.

Answer

leemes picture leemes · Oct 7, 2012

I found a much nicer answer to this question, suggested in a presentation of the Qt Developer Days 2011 "Qt Quick Best Practices and Design Patterns".

They use default property alias ... to alias the child items to any property of any item. If you don't want to alias the children but give the alias property a name, just remove default. (Literal children are per QML definition the value of the default property.)

Item {
    id: button
    default property alias contents: placeholder.children

    anchors.fill: parent

    Rectangle {
        anchors.fill: parent
        radius: 10
        color: "gray"

        Item {
            id: placeholder     <-- where the placeholder should be inserted
        }
    }
}