[844] | 1 | /****************************************************************************
|
---|
| 2 | **
|
---|
| 3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
---|
| 4 | ** All rights reserved.
|
---|
| 5 | ** Contact: Nokia Corporation ([email protected])
|
---|
| 6 | **
|
---|
| 7 | ** This file is part of the documentation of the Qt Toolkit.
|
---|
| 8 | **
|
---|
| 9 | ** $QT_BEGIN_LICENSE:FDL$
|
---|
| 10 | ** Commercial Usage
|
---|
| 11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
| 12 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
| 13 | ** Software or, alternatively, in accordance with the terms contained in a
|
---|
| 14 | ** written agreement between you and Nokia.
|
---|
| 15 | **
|
---|
| 16 | ** GNU Free Documentation License
|
---|
| 17 | ** Alternatively, this file may be used under the terms of the GNU Free
|
---|
| 18 | ** Documentation License version 1.3 as published by the Free Software
|
---|
| 19 | ** Foundation and appearing in the file included in the packaging of this
|
---|
| 20 | ** file.
|
---|
| 21 | **
|
---|
| 22 | ** If you have questions regarding the use of this file, please contact
|
---|
| 23 | ** Nokia at [email protected].
|
---|
| 24 | ** $QT_END_LICENSE$
|
---|
| 25 | **
|
---|
| 26 | ****************************************************************************/
|
---|
| 27 |
|
---|
| 28 | /*!
|
---|
| 29 | \page qdeclarativejavascript.html
|
---|
| 30 | \title Integrating JavaScript
|
---|
| 31 |
|
---|
| 32 | QML encourages building UIs declaratively, using \l {Property Binding} and the
|
---|
| 33 | composition of existing \l {QML Elements}. To allow the implementation of more
|
---|
| 34 | advanced behavior, QML integrates tightly with imperative JavaScript code.
|
---|
| 35 |
|
---|
| 36 | The JavaScript environment provided by QML is stricter than that in a web browser.
|
---|
| 37 | In QML you cannot add, or modify, members of the JavaScript global object. It
|
---|
| 38 | is possible to do this accidentally by using a variable without declaring it. In
|
---|
| 39 | QML this will throw an exception, so all local variables should be explicitly
|
---|
| 40 | declared.
|
---|
| 41 |
|
---|
| 42 | In addition to the standard JavaScript properties, the \l {QML Global Object}
|
---|
| 43 | includes a number of helper methods that simplify building UIs and interacting
|
---|
| 44 | with the QML environment.
|
---|
| 45 |
|
---|
| 46 | \section1 Inline JavaScript
|
---|
| 47 |
|
---|
| 48 | Small JavaScript functions can be written inline with other QML declarations.
|
---|
| 49 | These inline functions are added as methods to the QML element that contains
|
---|
| 50 | them.
|
---|
| 51 |
|
---|
| 52 | \code
|
---|
| 53 | Item {
|
---|
| 54 | function factorial(a) {
|
---|
| 55 | a = parseInt(a);
|
---|
| 56 | if (a <= 0)
|
---|
| 57 | return 1;
|
---|
| 58 | else
|
---|
| 59 | return a * factorial(a - 1);
|
---|
| 60 | }
|
---|
| 61 |
|
---|
| 62 | MouseArea {
|
---|
| 63 | anchors.fill: parent
|
---|
| 64 | onClicked: console.log(factorial(10))
|
---|
| 65 | }
|
---|
| 66 | }
|
---|
| 67 | \endcode
|
---|
| 68 |
|
---|
| 69 | As methods, inline functions on the root element in a QML component can be
|
---|
| 70 | invoked by callers outside the component. If this is not desired, the method
|
---|
| 71 | can be added to a non-root element or, preferably, written in an external
|
---|
| 72 | JavaScript file.
|
---|
| 73 |
|
---|
| 74 | \section1 Separate JavaScript files
|
---|
| 75 |
|
---|
| 76 | Large blocks of JavaScript should be written in separate files. These files
|
---|
| 77 | can be imported into QML files using an \c import statement, in the same way
|
---|
| 78 | that \l {Modules}{modules} are imported.
|
---|
| 79 |
|
---|
| 80 | For example, the \c {factorial()} method in the above example for \l {Inline JavaScript}
|
---|
| 81 | could be moved into an external file named \c factorial.js, and accessed like this:
|
---|
| 82 |
|
---|
| 83 | \code
|
---|
| 84 | import "factorial.js" as MathFunctions
|
---|
| 85 | Item {
|
---|
| 86 | MouseArea {
|
---|
| 87 | anchors.fill: parent
|
---|
| 88 | onClicked: console.log(MathFunctions.factorial(10))
|
---|
| 89 | }
|
---|
| 90 | }
|
---|
| 91 | \endcode
|
---|
| 92 |
|
---|
| 93 | Both relative and absolute JavaScript URLs can be imported. In the case of a
|
---|
| 94 | relative URL, the location is resolved relative to the location of the
|
---|
| 95 | \l {QML Document} that contains the import. If the script file is not accessible,
|
---|
| 96 | an error will occur. If the JavaScript needs to be fetched from a network
|
---|
| 97 | resource, the component's \l {QDeclarativeComponent::status()}{status} is set to
|
---|
| 98 | "Loading" until the script has been downloaded.
|
---|
| 99 |
|
---|
| 100 | Imported JavaScript files are always qualified using the "as" keyword. The
|
---|
| 101 | qualifier for JavaScript files must be unique, so there is always a one-to-one
|
---|
| 102 | mapping between qualifiers and JavaScript files. (This also means qualifiers cannot
|
---|
| 103 | be named the same as built-in JavaScript objects such as \c Date and \c Math).
|
---|
| 104 |
|
---|
| 105 |
|
---|
| 106 | \section2 Code-Behind Implementation Files
|
---|
| 107 |
|
---|
| 108 | Most JavaScript files imported into a QML file are stateful, logic implementations
|
---|
| 109 | for the QML file importing them. In these cases, for QML component instances to
|
---|
| 110 | behave correctly each instance requires a separate copy of the JavaScript objects
|
---|
| 111 | and state.
|
---|
| 112 |
|
---|
| 113 | The default behavior when importing JavaScript files is to provide a unique, isolated
|
---|
| 114 | copy for each QML component instance. The code runs in the same scope as the QML
|
---|
| 115 | component instance and consequently can can access and manipulate the objects and
|
---|
| 116 | properties declared.
|
---|
| 117 |
|
---|
| 118 | \section2 Stateless JavaScript libraries
|
---|
| 119 |
|
---|
| 120 | Some JavaScript files act more like libraries - they provide a set of stateless
|
---|
| 121 | helper functions that take input and compute output, but never manipulate QML
|
---|
| 122 | component instances directly.
|
---|
| 123 |
|
---|
| 124 | As it would be wasteful for each QML component instance to have a unique copy of
|
---|
| 125 | these libraries, the JavaScript programmer can indicate a particular file is a
|
---|
| 126 | stateless library through the use of a pragma, as shown in the following example.
|
---|
| 127 |
|
---|
| 128 | \code
|
---|
| 129 | // factorial.js
|
---|
| 130 | .pragma library
|
---|
| 131 |
|
---|
| 132 | function factorial(a) {
|
---|
| 133 | a = parseInt(a);
|
---|
| 134 | if (a <= 0)
|
---|
| 135 | return 1;
|
---|
| 136 | else
|
---|
| 137 | return a * factorial(a - 1);
|
---|
| 138 | }
|
---|
| 139 | \endcode
|
---|
| 140 |
|
---|
| 141 | The pragma declaration must appear before any JavaScript code excluding comments.
|
---|
| 142 |
|
---|
| 143 | As they are shared, stateless library files cannot access QML component instance
|
---|
| 144 | objects or properties directly, although QML values can be passed as function
|
---|
| 145 | parameters.
|
---|
| 146 |
|
---|
| 147 |
|
---|
| 148 | \section2 Importing One JavaScript File From Another
|
---|
| 149 |
|
---|
| 150 | If a JavaScript file needs to use functions defined inside another JavaScript file,
|
---|
| 151 | the other file can be imported using the \l {QML:Qt::include()}{Qt.include()}
|
---|
| 152 | function. This imports all functions from the other file into the current file's
|
---|
| 153 | namespace.
|
---|
| 154 |
|
---|
| 155 | For example, the QML code below left calls \c showCalculations() in \c script.js,
|
---|
| 156 | which in turn can call \c factorial() in \c factorial.js, as it has included
|
---|
| 157 | \c factorial.js using \l {QML:Qt::include()}{Qt.include()}.
|
---|
| 158 |
|
---|
| 159 | \table
|
---|
| 160 | \row
|
---|
| 161 | \o {1,2} \snippet doc/src/snippets/declarative/integrating-javascript/includejs/app.qml 0
|
---|
| 162 | \o \snippet doc/src/snippets/declarative/integrating-javascript/includejs/script.js 0
|
---|
| 163 | \row
|
---|
| 164 | \o \snippet doc/src/snippets/declarative/integrating-javascript/includejs/factorial.js 0
|
---|
| 165 | \endtable
|
---|
| 166 |
|
---|
| 167 | Notice that calling \l {QML:Qt::include()}{Qt.include()} imports all functions from
|
---|
| 168 | \c factorial.js into the \c MyScript namespace, which means the QML component can also
|
---|
| 169 | access \c factorial() directly as \c MyScript.factorial().
|
---|
| 170 |
|
---|
| 171 |
|
---|
| 172 | \section1 Running JavaScript at Startup
|
---|
| 173 |
|
---|
| 174 | It is occasionally necessary to run some imperative code at application (or
|
---|
| 175 | component instance) startup. While it is tempting to just include the startup
|
---|
| 176 | script as \e {global code} in an external script file, this can have severe limitations
|
---|
| 177 | as the QML environment may not have been fully established. For example, some objects
|
---|
| 178 | might not have been created or some \l {Property Binding}s may not have been run.
|
---|
| 179 | \l {QML JavaScript Restrictions} covers the exact limitations of global script code.
|
---|
| 180 |
|
---|
| 181 | The QML \l Component element provides an \e attached \c onCompleted property that
|
---|
| 182 | can be used to trigger the execution of script code at startup after the
|
---|
| 183 | QML environment has been completely established. For example:
|
---|
| 184 |
|
---|
| 185 | \code
|
---|
| 186 | Rectangle {
|
---|
| 187 | function startupFunction() {
|
---|
| 188 | // ... startup code
|
---|
| 189 | }
|
---|
| 190 |
|
---|
| 191 | Component.onCompleted: startupFunction();
|
---|
| 192 | }
|
---|
| 193 | \endcode
|
---|
| 194 |
|
---|
| 195 | Any element in a QML file - including nested elements and nested QML component
|
---|
| 196 | instances - can use this attached property. If there is more than one \c onCompleted()
|
---|
| 197 | handler to execute at startup, they are run sequentially in an undefined order.
|
---|
| 198 |
|
---|
| 199 | Likewise, the \l {Component::onDestruction} attached property is triggered on
|
---|
| 200 | component destruction.
|
---|
| 201 |
|
---|
| 202 |
|
---|
| 203 | \section1 Property Assignment vs Property Binding
|
---|
| 204 |
|
---|
| 205 | When working with both QML and JavaScript, it is important to differentiate between
|
---|
| 206 | QML \l {Property Binding} and JavaScript value assignment. In QML, a property
|
---|
| 207 | binding is created using the \e {property: value} syntax:
|
---|
| 208 |
|
---|
| 209 | \code
|
---|
| 210 | Rectangle {
|
---|
| 211 | width: otherItem.width
|
---|
| 212 | }
|
---|
| 213 | \endcode
|
---|
| 214 |
|
---|
| 215 | The \c width of the above \l Rectangle is updated whenever \c otherItem.width changes. On the other
|
---|
| 216 | hand, take the following JavaScript code snippet, that runs when the \l Rectangle is created:
|
---|
| 217 |
|
---|
| 218 | \code
|
---|
| 219 | Rectangle {
|
---|
| 220 |
|
---|
| 221 | Component.onCompleted: {
|
---|
| 222 | width = otherItem.width;
|
---|
| 223 | }
|
---|
| 224 | }
|
---|
| 225 | \endcode
|
---|
| 226 |
|
---|
| 227 | The \c width of this \l Rectangle is \e assigned the value of \c otherItem.width using the
|
---|
| 228 | \e {property = value} syntax in JavaScript. Unlike the QML \e {property: value} syntax, this
|
---|
| 229 | does not invoke QML property binding; the \c rectangle.width property is set to the value
|
---|
| 230 | of \c otherItem.width at the time of the assignment and will not be updated if that value
|
---|
| 231 | changes.
|
---|
| 232 |
|
---|
| 233 | See \l {Property Binding} for more information.
|
---|
| 234 |
|
---|
| 235 |
|
---|
| 236 | \section1 Receiving QML Signals in JavaScript
|
---|
| 237 |
|
---|
| 238 | To receive a QML signal, use the signal's \c connect() method to connect it to a JavaScript
|
---|
| 239 | function.
|
---|
| 240 |
|
---|
| 241 | For example, the following code connects the MouseArea \c clicked signal to the \c jsFunction()
|
---|
| 242 | in \c script.js:
|
---|
| 243 |
|
---|
| 244 | \table
|
---|
| 245 | \row
|
---|
| 246 | \o \snippet doc/src/snippets/declarative/integrating-javascript/connectjs.qml 0
|
---|
| 247 | \o \snippet doc/src/snippets/declarative/integrating-javascript/script.js 0
|
---|
| 248 | \endtable
|
---|
| 249 |
|
---|
| 250 | The \c jsFunction() will now be called whenever MouseArea's \c clicked signal is emitted.
|
---|
| 251 |
|
---|
| 252 | See \l {Connecting signals to methods and other signals} for more information.
|
---|
| 253 |
|
---|
| 254 |
|
---|
| 255 | \section1 QML JavaScript Restrictions
|
---|
| 256 |
|
---|
| 257 | QML executes standard JavaScript code, with the following restrictions:
|
---|
| 258 |
|
---|
| 259 | \list
|
---|
| 260 | \o JavaScript code cannot modify the global object.
|
---|
| 261 |
|
---|
| 262 | In QML, the global object is constant - existing properties cannot be modified or
|
---|
| 263 | deleted, and no new properties may be created.
|
---|
| 264 |
|
---|
| 265 | Most JavaScript programs do not intentionally modify the global object. However,
|
---|
| 266 | JavaScript's automatic creation of undeclared variables is an implicit modification
|
---|
| 267 | of the global object, and is prohibited in QML.
|
---|
| 268 |
|
---|
| 269 | Assuming that the \c a variable does not exist in the scope chain, the following code
|
---|
| 270 | is illegal in QML.
|
---|
| 271 |
|
---|
| 272 | \code
|
---|
| 273 | // Illegal modification of undeclared variable
|
---|
| 274 | a = 1;
|
---|
| 275 | for (var ii = 1; ii < 10; ++ii)
|
---|
| 276 | a = a * ii;
|
---|
| 277 | console.log("Result: " + a);
|
---|
| 278 | \endcode
|
---|
| 279 |
|
---|
| 280 | It can be trivially modified to this legal code.
|
---|
| 281 |
|
---|
| 282 | \code
|
---|
| 283 | var a = 1;
|
---|
| 284 | for (var ii = 1; ii < 10; ++ii)
|
---|
| 285 | a = a * ii;
|
---|
| 286 | console.log("Result: " + a);
|
---|
| 287 | \endcode
|
---|
| 288 |
|
---|
| 289 | Any attempt to modify the global object - either implicitly or explicitly - will
|
---|
| 290 | cause an exception. If uncaught, this will result in an warning being printed,
|
---|
| 291 | that includes the file and line number of the offending code.
|
---|
| 292 |
|
---|
| 293 | \o Global code is run in a reduced scope
|
---|
| 294 |
|
---|
| 295 | During startup, if a QML file includes an external JavaScript file with "global"
|
---|
| 296 | code, it is executed in a scope that contains only the external file itself and
|
---|
| 297 | the global object. That is, it will not have access to the QML objects and
|
---|
| 298 | properties it \l {QML Scope}{normally would}.
|
---|
| 299 |
|
---|
| 300 | Global code that only accesses script local variable is permitted. This is an
|
---|
| 301 | example of valid global code.
|
---|
| 302 |
|
---|
| 303 | \code
|
---|
| 304 | var colors = [ "red", "blue", "green", "orange", "purple" ];
|
---|
| 305 | \endcode
|
---|
| 306 |
|
---|
| 307 | Global code that accesses QML objects will not run correctly.
|
---|
| 308 |
|
---|
| 309 | \code
|
---|
| 310 | // Invalid global code - the "rootObject" variable is undefined
|
---|
| 311 | var initialPosition = { rootObject.x, rootObject.y }
|
---|
| 312 | \endcode
|
---|
| 313 |
|
---|
| 314 | This restriction exists as the QML environment is not yet fully established.
|
---|
| 315 | To run code after the environment setup has completed, refer to
|
---|
| 316 | \l {Running JavaScript at Startup}.
|
---|
| 317 |
|
---|
| 318 | \o The value of \c this is currently undefined in QML
|
---|
| 319 |
|
---|
| 320 | The value of \c this is undefined in QML. To refer to any element, provide
|
---|
| 321 | an \c id. For example:
|
---|
| 322 |
|
---|
| 323 | \qml
|
---|
| 324 | Item {
|
---|
| 325 | width: 200; height: 100
|
---|
| 326 | function mouseAreaClicked(area) {
|
---|
| 327 | console.log("Clicked in area at: " + area.x + ", " + area.y);
|
---|
| 328 | }
|
---|
| 329 | // This will not work because this is undefined
|
---|
| 330 | MouseArea {
|
---|
| 331 | height: 50; width: 200
|
---|
| 332 | onClicked: mouseAreaClicked(this)
|
---|
| 333 | }
|
---|
| 334 | // This will pass area2 to the function
|
---|
| 335 | MouseArea {
|
---|
| 336 | id: area2
|
---|
| 337 | y: 50; height: 50; width: 200
|
---|
| 338 | onClicked: mouseAreaClicked(area2)
|
---|
| 339 | }
|
---|
| 340 | }
|
---|
| 341 | \endqml
|
---|
| 342 |
|
---|
| 343 | \endlist
|
---|
| 344 |
|
---|
| 345 | */
|
---|