QML provides a convenient way to break code called "Components." The easiest way to create a component that can later be used repeatedly is to add a new file to the working directory of the main QML file.
Example.qml:import QtQuick 1.0 Rectangle { }
main.qml: import QtQuick 1.0 Example { }
Also, components can be packaged as modules (Qt Components are such a module) and published as plug-ins. This post focuses on using components to write clean and easily maintained QML code.
')
Creating new components
The first example showed the simplicity of creating additional components, so do not be afraid to use them.
Do not write reusable code as your first priority. Aim to encapsulate implementation details and reduce decoupling of components. Components should be small. Following these rules you will automatically come to the code, which later can be used repeatedly.
Let's look at this example of a simple analog clock:
download an example /
view onlinemain.qml: import QtQuick 1.0
Clock.qml: import QtQuick 1.0
This code contains the Clock component, which at startup looks like the one shown in the screenshot below. Despite the fact that it is used in the application once, it made sense to select it from the main file.

First, it makes the logic that remains in the main.qml file simple and understandable: the timer that updates the hours, minutes, and seconds of the Clock component is all that the developer needs to see in main.qml if he wants to add functionality to it.
Secondly, our Clock component may not worry about its own location in the window. Suppose there is a Row element that uses our Clock component N times. If the root element of the Clock component had the code 'anchors.fill: parent', we could not use its instances in the Row element: each instance of Clock would occupy the entire width_row, instead of width_row / N. That is why QML prohibits the use of most anchors (anchors ) in the elements placed in the element Row. If we want our Clock component to remain reusable, we should not make many assumptions about its future use. Summarizing, the root element of the component should not contain anchors to its parent or use hard-coded placement managers (layouts).
At the same time, the Clock component sets fixed values to its length and width. From the semantic point of view, these are the dimensions of our default component. They do not limit the use of our component, because the size of its copies can be changed if necessary. Zero-sized components are considered invisible elements, so setting non-zero sizes by default allows you to avoid silly mistakes.
There are also other, less obvious advantages, such as creating composite elements and encapsulating implementation details.
Creating a composite element (let's call it ComposedElement) from the simple elements ElementA, ElementB and ElementC makes it easy to add new properties and actions to the elements. We can add new ElementD and ElementE elements to our ComposedElement without having to change ElementA, ElementB or ElementC. Our simple elements are isolated from each other and, therefore, cannot just break down if one of them suddenly changes.
Components can be divided into public and private parts, just as is done in C ++ and Java classes. The public API of a component is the sum of all its properties and methods defined in the root element (including the properties inherited by it). This means that such properties can be changed, and such methods can be called by users of the component.
Any property or method defined in a nested element (not root) can be considered a completely private API. This allows you to encapsulate implementation details and should, as a result, become commonplace when developers create components.
To prove the use of encapsulation, we can remove the inner element 'impl' from Clock.qml and start the application again (for example, through $ qmiviewer main.qml). No new errors will be visible, since the public API of the Clock component has not been modified. This means that we can freely change 'impl', knowing that no third-party effects from such changes for other components will appear.
We can even extend this idea and allow Clock.qml to load any 'impl' element dynamically, depending on the situation. This introduces the concept of polymorphism in QML; the implementation of such a mechanism remains to the reader as an exercise.
If we have a well-designed, minimal public API for each component, we can focus on developing the interfaces, and not the specific implementation.
Component reuse
Check if we can actually reuse the Clock.qml component. It is perfect for creating clocks that display world time. In this case, we do not need a second hand on them. We can slightly change the behavior of Clock.qml so that the latter does not display the hours, minutes or seconds if their value is less than zero. For the Image element with id = thinhand, we can associate the visible property with the number of seconds:
visible: root.seconds > -1
We did not change the public API. We also believe that using negative values for seconds in an analog clock still makes no sense. Therefore, we are confident that this change will not affect the operation of existing applications that use the Clock component.
In our world clock example, we can, among other things, display local weather information. We can use one of the available weather services APIs along with the XmlListModel, which allows you to retrieve data from the API declaratively. The timer, which was previously used only to update the time on the clock, will now be used to update weather data once an hour. Notice how the update signal is entered, which is connected to the XmlListModel data update function.
The modified example displays the world clock and local weather information for the three cities.

The Clock component integrates trivially. We disabled the second hand and used UTC for time values along with the offset for a particular city. This worked because Clock was designed as a simple interface element. If we had a timer inside the component (instead of letting the hours, minutes, and seconds change as the properties of the component), this would complicate the work. Unfortunately, main.qml has grown significantly in size. The Repeater element has contributed to the complexity of the project, as well as the utcOffsets and cities arrays.
download an example /
view onlinemain.qml: Rectangle { ... property variant cities: ["Berlin", "Helsinki", "San Francisco"] property variant utcOffsets: [1, 2, -8] property variant now: new Date() signal refresh() Timer { ... property int hours onTriggered: { hours = root.now.getHours() root.now = new Date()
Our goal is to make main.qml easy to understand. At the same time, we do not want to make Clock.qml unnecessarily complicated, because, in this case, it will be difficult to use it as a simple analogue clock. Therefore, we created a new component consisting of weather data collection and the Clock component. It contains a timer, update logic, XmlListModel, and Clock integration. Instead of directly setting the utcOffsets and cities arrays here, we add new public properties for the WeatherWorldClock component:
We can remove these arrays and Repeater from main.qml.
download an example /
view onlinemain.qml: import QtQuick 1.0
The WeatherWorldClock component is isolated from changes in main.qml. We can supplement and change its properties in any file and not worry about something going wrong. If WeatherWorldClock becomes too complex to support, it can be divided into more components. That is why it is very important that the main logic of our application now looks extremely simple: we initialize the components of WeatherWorldClock and specify the city and its UTC offset. That's all!
Conclusion
This article showed how it is possible to maintain the purity and consistency of the code of an evolving and complicating example using components and applying the well-known principles of object-oriented design. At one point — when we added the ability to view the weather to our example — we stopped following our components, so the application logic became more complicated. It was demonstrated in such a simple way that maintaining cleanliness and order in a QML code is a serious work and requires a certain discipline from the developer.