Menu
Home
Log in / Register
 
Home arrow Computer Science arrow Learn BlackBerry 10 App Development
< Prev   CONTENTS   Next >

Attributes

Let's go over the attributes used in the example.

The id Attribute

As mentioned previously, object declarations can specify an id attribute that must start with a lowercase letter or an underscore. You will usually assign a value to the id attribute whenever you want to uniquely reference that object instance in your QML document.

Property Attributes

Let us now flesh out our PersonEntry type by adding some properties to it. We want to be able to add extra information such as a person's surname, first name, login, e-mail, and so forth. We will also eventually have to implement business rules such as “a person's login is the first letter of his first name concatenated to his surname with all letters in lowercase” and “a person's e-mail is his login followed by the at symbol and the company's domain name.”

In QML, this kind of information is provided by object properties.

A property is an attribute that can be assigned a static value or bound to a dynamic expression provided by some JavaScript code. Properties are used to expose to the “outside world” an object's state by hiding its implementation at the same time. The syntax for defining properties is given by

property <propertyType> <propertyName>

Listing 2-7 adds the surname, first name, login, and e-mail properties to PersonEntry.qml.

Listing 2-7. PersonEntry.qml

import bb.cascades 1.0 Container{

id: root

property int employeeNumber property string surname property string firstname property string login property string email

}

You can in turn set the properties in main.qml as shown in Listing 2-8.

Listing 2-8. main.qml

import bb.cascades 1.0 Page {

PersonEntry {

employeeNumber: 100 surname: "Smith" firstname: "John" login: "jsmith"

email:" This email address is being protected from spam bots, you need Javascript enabled to view it "

}

}

The PersonEntry control is visually not very interesting at the moment. The most glaring problem is that it's missing a screen representation. If you try to build the project and run it on the simulator, you will just get a blank screen (you will notice the same thing in the “Cascades builder” design view if you try to display main.qml). What we need to do is display the properties on the screen after they have been set. In order to achieve this, let's use Cascades Labels (a Label is a core control with a displayable text property on the screen). Listing 2-9 gives an updated version of PersonEnty.qml using Labels for displaying the object's properties.

Listing 2-9. PersonEntry.qml

import bb.cascades 1.0 Container{

id: root

property int employeeNumber property string surname property string firstname property string login property string email

Label{

text: "MyCompany Employee Details"

textStyle.base: SystemDefaults.TextStyles.TitleText horizontalAlignment: HorizontalAlignment.Center

}

Label{

text: "Employee number: " + employeeNumber;

}

Label{

text: "Last name: "+surname;

}

Label{

text: "First name:"+ firstname;

}

Label{

text: "Login: "+ login;

}

Label{

text: "Email: "+ email;

}

}

You can now build the CorpDir project and run on it on the simulator (see Figure 2-1).

Figure 2-1. Employee view

So far so good, but we still need to be able to set the person's job title. This kind of information usually comes from a list of predefined values such as Software Engineer, Manager, Director, Consultant, Technician, and so forth. In order to achieve this, we can use a Cascades DropDown control. The control will be selectable so that if a person's entry needs to be updated, a new job title can be selected from the list. See Listing 2-10 for the updated PersonEntry.qml control.

Listing 2-10. PersonEntry.qml

import bb.cascades 1.0

Container {

id: root

property int employeeNumber property string surname property string firstname property string login property string email property string jobTitle

Label{

text: "Employee Details"

textStyle.base: SystemDefaults.TextStyles.TitleText horizontalAlignment: HorizontalAlignment.Center

}

Label {

text: "Employee number: " + employeeNumber;

}

Label {

text: "Last name: " + surname;

}

Label {

text: "First name:" + firstname;

}

Label {

text: "Login: " + login;

}

Label {

text: "Email: " + email;

}

DropDown {

id: jobs

title: "Job Title" enabled: true Option{

text: "Unknown"

}

Option {

text: "Software Engineer"

}

Option {

text: "Manager"

}

Option {

text: "Director"

}

Option {

text: "Technician"

}

}

}

Listing 2-11 shows main.qml with the job property set.

Listing 2-11. main.qml

import bb.cascades 1.0

Page {

PersonEntry {

employeeNumber: 100 surname: "Smith" firstname: "John" login: "jsmith"

email:" This email address is being protected from spam bots, you need Javascript enabled to view it " jobTitle: "Software Engineer"

}

}

At this point, we are still facing a couple of issues. For one thing, we need to be able to synch the jobTitle property with the corresponding option value in the DropDown control. Also, instead of setting the e-mail and login properties, they should be generated using the business rules described at the start of this section. Whenever you need to add this kind of programmatic logic, you will have to rely on JavaScript.

JavaScript

JavaScript is not an object attribute per se, but is still tightly integrated and can be used in the following scenarios:

n A JavaScript expression can be bound to QML object properties. The expression will be reevaluated every time the property is accessed in order to ensure that its value stays up-to-date. Typically, the one-liners you have seen until now for setting a Label's property are JavaScript expressions (for example, "Login" + login;). The expressions can be as complex as you wish as long as their result is a value whose type can be assigned to the corresponding property. You can even include multiple expressions between open and close braces.

n Signal handlers can contain JavaScript code that is automatically evaluated every time the corresponding QML object emits the corresponding signal.

n You can define custom JavaScript methods within a QML object (this can be considered as an object attribute).

n You can import JavaScript files as modules that you can use in your QML document.

n And finally, you can wire a signal directly to a JavaScript function.

You have already encountered the first two methods of using JavaScript, and by time you finish this chapter, you will have seen all the different ways of incorporating JavaScript in your QML documents.

JavaScript Host Environment

The QML engine includes a JavaScript host environment, giving you the possibility of building extremely complex applications using JavaScript/QML only. There are some restrictions, however; for example, the environment does not provide the DOM API commonly available in browsers. If you think about it, this makes complete sense since a QML application is certainly not an HTML browser app and the DOM would be irrelevant. Also, the environment is quite different from

server-side technologies such as Node.js. The runtime does, however, implement the ECMAScript language specification, so this effectively means that you have a complete JavaScript programming environment at your disposal. The host environment also provides a set of global objects and functions that you can use in your QML documents:

n The Qt object, which provides string utility functions for localization, date formatting functions, and object factories for dynamically instantiating Qt types in QML.

n The qsTr() family of functions for providing translations in QML.

n The console object for generating logs and debug messages from QML (using console.log() and console.debug()).

n And finally, the XMLHttpRequest object. This basically opens the door to asynchronous HTTP requests directly from QML!

Let us now return to our PersonEntry type and spice it up with some JavaScript behavior (see Listing 2-12).

Listing 2-12. PersonEntry.qml

import bb.cascades 1.0 Container {

id: root

property int employeeNumber property string surname property string firstname

property string jobTitle function getLogin(){

return root.firstname.charAt(0).toLowerCase() + root.surname.toLowerCase();

}

function getEmail(){

return root.firstname.toLowerCase() +"."+root.surname.toLowerCase()+"@mycompany.com";

}

onCreationCompleted: { switch (jobTitle) {

case"Software Engineer": jobs.selectedIndex = 1; break;

case"Manager": jobs.selectedIndex = 2; break;

case"Director": jobs.selectedIndex = 3; break;

case"Technician": jobs.selectedIndex = 4; break;

default:

jobs.selectedIndex = 0; break;

}

}

Label{

text: "Employee Details"

textStyle.base: SystemDefaults.TextStyles.TitleText horizontalAlignment: HorizontalAlignment.Center

}

Label {

text: "Employee number: " + employeeNumber;

}

Label {

text: "Last name: " + surname;

}

Label {

text: "First name:" + firstname;

}

Label {

text: "Login: " + root.getLogin();

}

Label {

text: "Email: " + root.getEmail();

}

DropDown {

id: jobs

title: "Job Title" enabled: true

onSelectedIndexChanged: {

console.debug("SelectedIndex was changed to " + selectedIndex); console.debug("Selected option is: " + selectedOption.text); root.jobTitle = selectedOption.text;

}

Option{

text: "Unknown"

}

Option {

text: "Software Engineer"

}

Option {

text: "Manager"

}

Option {

text: "Director"

}

Option {

text: "Technician"

}

}

}

Listing 2-13 is the updated version of main.qml.

Listing 2-13. main.qml

import bb.cascades 1.0

Page {

PersonEntry {

employeeNumber: 100 surname: "Smith" firstname: "John"

jobTitle: "Jack of All Trades"

}

}

You will notice that login and email are no longer settable properties. Instead, the getLogin() and getEmail() JavaScript functions are used in order to update the corresponding labels using the business rules for generating logins and e-mails respectively. Another interesting point is that in order to synchronize the jobFunction property with the DropDown control's selected index, the onCreationCompleted: signal handler is used (the body of the handler is simply a switch statement that sets the selected index). The QML engine automatically calls this handler after a QML object has been successfully constructed. This is the ideal place to set up additional validation or initialization logic (in the example given in Listing 2-13, “Jack of All Trades” is not a valid job title and the selectedIndex will be set to 0, which corresponds to the “Unknown” job title).

Signal Attributes

In Chapter 1, you declared signals in C++ using the signals: annotation. Declaring your own signals in QML is just as simple and is given by the following syntax:

signal <signalName>[([<type> <parameter name>[, ...]])]

If your signal does not take any parameters, you can safely ignore the “()” brackets in the declaration.

Here are two examples:

n signal clicked

n signal salaryChanged(double newSalary)

There are also a couple of things that the QML engine provides you “for free:”

n The QML engine generates a slot for every signal emitted by your controls. For example, the onSalaryChanged slot will be generated for the salarayChanged signal (you will see this in action in Listing 2-15).

n Property change signals. The QML engine automatically generates these signals for your custom control's properties. They are emitted whenever a control's property value is updated.

n Property change signal handlers. For a given property <Property>, they take the form on<Property>Change. This is where you can define your own business logic when the property change signals are emitted.

Let's add the salaryChanged signal to the PersonEntry control and the corresponding handler in main.qml. The signal will be emitted with an updated salary whenever a person's job title changes. The first step is to define the signal in the root QML object. You can then emit the signal using root.salaryChanged() from the DropDown control's onSelectedIndexChanged handler. The final version of the PersonEntry custom control also includes a new property for setting the person's picture. (Note that I am using a property alias in this case. A property alias is a reference to

an existing property. In other words, the picture property is a reference to the employeeImage.imageSource property, and by setting the picture property, you are actually updating the referenced property.)

Listing 2-14. PersonEntry.qml Final

import bb.cascades 1.0 Container {

id: root

property int employeeNumber property string surname property string firstname property string jobTitle

property alias picture: employeeImage.imageSource

signal salaryChanged(double newSalary) function getLogin(){

return root.firstname.charAt(0).toLowerCase() + root.surname.toLowerCase();

}

function getEmail(){

return root.firstname.toLowerCase() +"."+root.surname.toLowerCase()+"@mycompany.com";

}

onCreationCompleted: { switch (jobTitle) {

case"Software Engineer": jobs.selectedIndex = 1; break;

case"Manager": jobs.selectedIndex = 2; break;

case"Director": jobs.selectedIndex = 3; break;

case"Technician": jobs.selectedIndex = 4; break;

default:

jobs.selectedIndex = 0;

}

}

ImageView {

id: employeeImage

horizontalAlignment: HorizontalAlignment.Center

}

Label{

text: "Employee Details"

textStyle.base: SystemDefaults.TextStyles.TitleText horizontalAlignment: HorizontalAlignment.Center

}

Label {

text: "Employee number: " + employeeNumber;

}

Label {

text: "Last name: " + surname;

}

Label {

text: "First name:" + firstname;

}

Label {

text: "Login: " + root.getLogin();

}

Label {

text: "Email: " + root.getEmail();

}

DropDown {

id: jobs

title: "Job Title" enabled: true

onSelectedIndexChanged: {

console.debug("SelectedIndex was changed to " + selectedIndex); console.debug("Selected option is: "+selectedOption.text); root.jobTitle = selectedOption.text;

switch (selectedOption.text){ case "Software Engineer":

root.salaryChanged(90000); break;

case"Manager": root.salaryChanged(100000); break;

case"Director": root.salaryChanged(150000); break;

case "Technician":

// yes technicians should be more rewared than Managers

// as they are more useful. root.salaryChanged(160000); break;

default:

root.salaryChanged(0.0);

}

}

Option{

text: "Unknown"

}

Option {

text: "Software Engineer"

}

Option {

text: "Manager"

}

Option {

text: "Director"

}

Option {

text: "Technician"

}

}

}

And Listing 2-15 gives the final version of main.qml. You will notice that now the root control is no longer a PersonEntry object but a Container. The reason for this is because we have also added a Label that will display a person's updated salary whenever the salaryChanged signal is emitted.

Listing 2-15. main.qml Final

import bb.cascades 1.0

Page {

Container{

PersonEntry {

employeeNumber: 100 surname: "Smith" firstname: "John"

jobTitle: "Jack of All Trades" picture: "asset:///johnsmith.png"

onSalaryChanged: {

salaryLabel.text = "Salary: "+newSalary;

}

}

Label{

id: salaryLabel

} // Label

} // Container

} // Page

You can now finally build the CorpDir application and run it on the simulator (see Figure 2-2).

Figure 2-2. Employee view

 
Found a mistake? Please highlight the word and press Shift + Enter  
< Prev   CONTENTS   Next >
 
Subjects
Accounting
Business & Finance
Communication
Computer Science
Economics
Education
Engineering
Environment
Geography
Health
History
Language & Literature
Law
Management
Marketing
Philosophy
Political science
Psychology
Religion
Sociology
Travel