Qt Reference Documentation

[Previous: Signal and Handler Event System] [Next: States]

# Importing Reusable Components

A component is an instantiable QML definition, typically contained in a `.qml` file. For instance, a Button component may be defined in `Button.qml`. The QML runtime may instantiate this Button component to create Button objects. Alternatively, a component may be defined inside a Component element.

Moreover, the Button definition may also contain other components. A Button component could use a Text element for its label and other components to implement its functions. Compounding components to form new components (and effectively new interfaces) is the emphasis in QML.

## Defining New Components

Any snippet of QML code may become a component, by placing the code in a QML file (extension is `.qml`). A complete Button component that responds to user input may be in a Button.qml file.

     //contents of Button.qml
     import QtQuick 1.0

     Rectangle {
         id: button
         width: 145; height: 60
         color: "blue"
         smooth: true; radius: 9
         property alias text: label.text
         border {color: "#B9C5D0"; width: 1}

         gradient: Gradient {
             GradientStop {color: "#CFF7FF"; position: 0.0}
             GradientStop {color: "#99C0E5"; position: 0.57}
             GradientStop {color: "#719FCB"; position: 0.9}
         }

         Text {
             id: label
             anchors.centerIn: parent
             text: "Click Me!"
             font.pointSize: 12
             color: "blue"
         }

         MouseArea {
             anchors.fill: parent
             onClicked: console.log(text + " clicked")
         }
     }

Alternatively, a Component element may encapsulate a QML object to form a component.

     Rectangle {
         Component {
             id: inlinecomponent
             Rectangle {
                 id: display
                 width: 50; height: 50
                 color: "blue"
             }
         }
     }

## Loading a Component

The initialization of inline components is different from loading a component from a `.qml` file.

### Importing a Component

A component defined in a `.qml` file is directly usable by declaring the name of the component. For example, a button defined in `Button.qml` is created by declaring a `Button`. The button is defined in the Defining New Components section.

     import QtQuick 1.0

     Rectangle {
         width: 175; height: 350
         color: "lightgrey"

         Column {
             anchors.centerIn: parent
             spacing: 15
             Button {}
             Button {text: "Me Too!"}
             Button {text: "Me Three!"}
         }
     }

Note that the component name, `Button`, matches the QML filename, `Button.qml`. Also, the first character is in upper case. Matching the names allow components in the same directory to be in the direct import path of the application.

For flexibility, a `qmldir` file is for dictating which additional components, plugins, or directories should be imported. By using a `qmldir` file, component names do not need to match the filenames. The `qmldir` file should, however, be in an imported path.

     Button ./Button.qml
     FocusButton ./focusbutton.qml

### Loading an Inline Component

A consequence of inline components is that initialization may be deferred or delayed. A component may be created during a MouseArea event or by using a Loader element. The component can create an object, which is addressable in a similar way as an id property. Thus, the created object may have its bindings set and read like a normal QML object.

         Component {
             id: inlinecomponent
             Rectangle {
                 id: display
                 width: 50; height: 50
                 color: "blue"
             }
         }
         MouseArea {
             anchors.fill: parent
             onClicked: {
                 inlinecomponent.createObject(parent)

                 var second = inlinecomponent.createObject(parent)

                 var third = inlinecomponent.createObject(parent)
                 third.x = second.width + 10
                 third.color = "red"
             }
         }

## Component Properties

Initializing a component, either from a .qml file or initializing an inline component, have several properties to facilitate component execution. Specifically, there are attached properties and attached signal handlers for setting properties during the lifetime of a component.

The `Component.onCompleted` attached signal handler is called when the component completes initialization. It is useful for executing any commands after component initialization. Similarly, the `Component.onDestruction` signal handler executes when the component finishes destruction.

## Top-Level Component

Choosing the top-level or the root object of components is an important design aspect because the top-level object dictates which properties are accessible outside the component. Some elements are not visual elements and will not have visual properties exposed outside the component. Likewise, some elements add functionality that are not available to visual elements.

Consider the Button component from the Defining New Components section; it's top-level object is a Rectangle. When imported, the Button component will possess the Rectangle's properties, methods, signals, and any custom properties.

     Rectangle {
         //...
         width: 145; height: 60
         color: "blue"
         smooth: true; radius: 9
         property alias text: label.text
         //...
     }

The Button's `text` alias is accessible from outside the component as well as the Rectangle's visual properties and signals such as `x`, `y`, `anchors`, and `states`.

Alternatively, we may choose a FocusScope as our top-level object. The FocusScope element manage keyboard focus for its children which is beneficial for certain types of interfaces. However, since `FocusScopes` are not visual elements, the visual properties of its child need to be exposed.

     //contents of focusbutton.qml
     import QtQuick 1.0

     FocusScope {

         //FocusScope needs to bind to visual properties of the children
         property alias color: button.color
         x: button.x; y: button.y
         width: button.width; height: button.height

         Rectangle {
             id: button
             width: 145; height: 60
             color: "blue"
             smooth: true; radius: 9
             property alias text: label.text
             border {color: "#B9C5D0"; width: 1}

             gradient: Gradient {
                 GradientStop {color: "#CFF7FF"; position: 0.0}
                 GradientStop {color: "#99C0E5"; position: 0.57}
                 GradientStop {color: "#719FCB"; position: 0.9}
             }

             Text {
                 id: label
                 anchors.centerIn: parent
                 text: "Click Me!"
                 font.pointSize: 12
                 color: "blue"
             }

             MouseArea {
                 anchors.fill: parent
                 onClicked: console.log(text + " clicked")
             }
         }
     }