From 7ee8156116581e08466ebc23b31e2b76c127e742 Mon Sep 17 00:00:00 2001 From: Chris Adams Date: Fri, 6 Jul 2012 16:02:46 +1000 Subject: [PATCH] Improve documentation for JavaScript expressions and imports This commit splits the import documentation out of the expression documentation, and corrects various ambiguities or errors in the JavaScript-related documentation. Change-Id: I351b0676f7271efba7cbff90c133dfe008321fb8 Reviewed-by: Michael Brasser --- .../doc/src/javascript/dynamicobjectcreation.qdoc | 230 ++++++++++ .../doc/src/javascript/dynamicobjectcreation.qodc | 211 ---------- src/qml/doc/src/javascript/expressions.qdoc | 439 +++++++++----------- src/qml/doc/src/javascript/hostenvironment.qdoc | 2 +- src/qml/doc/src/javascript/imports.qdoc | 155 +++++++ 5 files changed, 585 insertions(+), 452 deletions(-) create mode 100644 src/qml/doc/src/javascript/dynamicobjectcreation.qdoc delete mode 100644 src/qml/doc/src/javascript/dynamicobjectcreation.qodc diff --git a/src/qml/doc/src/javascript/dynamicobjectcreation.qdoc b/src/qml/doc/src/javascript/dynamicobjectcreation.qdoc new file mode 100644 index 0000000..bd8e50c --- /dev/null +++ b/src/qml/doc/src/javascript/dynamicobjectcreation.qdoc @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms +** and conditions contained in a signed written agreement between you +** and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page qtqml-javascript-dynamicobjectcreation.html +\title Dynamic QML object creation from JavaScript +\brief instantiating and managing QML objects from JavaScript + +QML supports the dynamic creation of objects from within JavaScript. This is +useful to delay instantiation of objects until necessary, thereby improving +application startup time. It also allows visual objects to be dynamically +created and added to the scene in reaction to user input or other events. + +See the \l {declarative/toys/dynamicscene}{Dynamic Scene example} for a +demonstration of the concepts discussed on this page. + + +\section1 Creating Objects Dynamically + +There are two ways to create objects dynamically from JavaScript. You can +either call \l {QML:Qt::createComponent()}{Qt.createComponent()} to +dynamically create a \l Component object, or use \l{QML:Qt::createQmlObject()} +{Qt.createQmlObject()} to create an object from a string of QML. Creating a +component is better if you have an existing component defined in a QML document +and you want to dynamically create instances of that component. Otherwise, +creating an object from a string of QML is useful when the object QML itself is +generated at runtime. + + +\section2 Creating a Component Dynamically + +To dynamically load a component defined in a QML file, call the +\l {QML:Qt::createComponent()}{Qt.createComponent()} function in the +\l {QmlGlobalQtObject}{Qt object}. +This function takes the URL of the QML file as its only argument and creates +a \l Component object from this URL. + +Once you have a \l Component, you can call its \l {Component::createObject()} +{createObject()} method to create an instance of the component. This function +can take one or two arguments: +\list +\li The first is the parent for the new object. The parent can be a graphical + object (QtQuick item) or non-graphical object (QtQml QtObject or C++ + QObject). Only graphical objects with graphical parent objects will be + rendered to the QtQuick visual canvas. If you wish to set the parent later + you can safely pass \c null to this function. +\li The second is optional and is a map of property-value pairs that define + initial any property values for the object. Property values specified by + this argument are applied to the object before its creation is finalized, + avoiding binding errors that may occur if particular properties must be + initialized to enable other property bindings. Additionally, there are + small performance benefits when compared to defining property values and + bindings after the object is created. +\endlist + +Here is an example. First there is \c Sprite.qml, which defines a simple QML component: + +\snippet qml/Sprite.qml 0 + +Our main application file, \c main.qml, imports a \c componentCreation.js +JavaScript file that will create \c Sprite objects: + +\snippet qml/createComponent.qml 0 + +Here is \c componentCreation.js. Notice it checks whether the component +\l{Component::status}{status} is \c Component.Ready before calling +\l {Component::createObject()}{createObject()} in case the QML file is loaded +over a network and thus is not ready immediately. + +\snippet qml/componentCreation.js vars +\codeline +\snippet qml/componentCreation.js func +\snippet qml/componentCreation.js remote +\snippet qml/componentCreation.js func-end +\codeline +\snippet qml/componentCreation.js finishCreation + +If you are certain the QML file to be loaded is a local file, you could omit +the \c finishCreation() function and call \l {Component::createObject()} +{createObject()} immediately: + +\snippet qml/componentCreation.js func +\snippet qml/componentCreation.js local +\snippet qml/componentCreation.js func-end + +Notice in both instances, \l {Component::createObject()}{createObject()} is +called with \c appWindow passed as the parent argument, since the dynamically +created object is a visual (QtQuick) object. The created object will become a +child of the \c appWindow object in \c main.qml, and appear in the scene. + +When using files with relative paths, the path should +be relative to the file where \l {QML:Qt::createComponent()} +{Qt.createComponent()} is executed. + +To connect signals to (or receive signals from) dynamically created objects, +use the signal \c connect() method. See +\l{Signal and Handler Event System#Connecting Signals to Methods and Signals} +{Connecting Signals to Methods and Signals} for more information. + +It is also possible to instantiate components without blocking via the +\l {Component::incubateObject()}{incubateObject()} function. + +\section2 Creating an Object from a String of QML + +If the QML is not defined until runtime, you can create a QML object from +a string of QML using the \l{QML:Qt::createQmlObject()}{Qt.createQmlObject()} +function, as in the following example: + +\snippet qml/createQmlObject.qml 0 + +The first argument is the string of QML to create. Just like in a new file, +you will need to import any types you wish to use. The second argument is the +parent object for the new object, and the parent argument semantics which apply +to components are similarly applicable for \c createQmlObject(). +The third argument is the file path to associate with the new object; this is +used for error reporting. + +If the string of QML imports files using relative paths, the path should be +relative to the file in which the parent object (the second argument to the +method) is defined. + + +\section1 Maintaining Dynamically Created Objects + +When managing dynamically created objects, you must ensure the creation context +outlives the created object. Otherwise, if the creation context is destroyed +first, the bindings in the dynamic object will no longer work. + +The actual creation context depends on how an object is created: + +\list +\li If \l {QML:Qt::createComponent()}{Qt.createComponent()} is used, the + creation context is the QQmlContext in which this method is called +\li If \l{QML:Qt::createQmlObject()}{Qt.createQmlObject()} is called, the + creation context is the context of the parent object passed to this method +\li If a \c {Component{}} object is defined and \l {Component::createObject()} + {createObject()} or \l {Component::incubateObject()}{incubateObject()} is + called on that object, the creation context is the context in which the + \c Component is defined +\endlist + +Also, note that while dynamically created objects may be used the same as other +objects, they do not have an id in QML. + + +\section1 Deleting Objects Dynamically + +In many user interfaces, it is sufficient to set a visual object's opacity to 0 +or to move the visual object off the screen instead of deleting it. If you have +lots of dynamically created objects, however, you may receive a worthwhile +performance benefit if unused objects are deleted. + +Note that you should never manually delete objects that were dynamically +created by convenience QML object factories (such as \l Loader and +\l Repeater). Also, you should avoid deleting objects that you did not +dynamically create yourself. + +Items can be deleted using the \c destroy() method. This method has an optional +argument (which defaults to 0) that specifies the approximate delay in +milliseconds before the object is to be destroyed. + +Here is an example. The \c application.qml creates five instances of the +\c SelfDestroyingRect.qml component. Each instance runs a NumberAnimation, +and when the animation has finished, calls \c destroy() on its root object to +destroy itself: + +\table +\row +\li \c application.qml +\li \c SelfDestroyingRect.qml + +\row +\li \snippet qml/dynamicObjects-destroy.qml 0 +\li \snippet qml/SelfDestroyingRect.qml 0 + +\endtable + +Alternatively, the \c application.qml could have destroyed the created object +by calling \c object.destroy(). + +Note that it is safe to call destroy() on an object within that object. Objects +are not destroyed the instant destroy() is called, but are cleaned up sometime +between the end of that script block and the next frame (unless you specified a +non-zero delay). + +Note also that if a \c SelfDestroyingRect instance was created statically like +this: + +\qml +Item { + SelfDestroyingRect { + // ... + } +} +\endqml + +This would result in an error, since objects can only be dynamically +destroyed if they were dynamically created. + +Objects created with \l{QML:Qt::createQmlObject()}{Qt.createQmlObject()} +can similarly be destroyed using \c destroy(): + +\snippet qml/createQmlObject.qml 0 +\snippet qml/createQmlObject.qml destroy + +*/ diff --git a/src/qml/doc/src/javascript/dynamicobjectcreation.qodc b/src/qml/doc/src/javascript/dynamicobjectcreation.qodc deleted file mode 100644 index 4244263..0000000 --- a/src/qml/doc/src/javascript/dynamicobjectcreation.qodc +++ /dev/null @@ -1,211 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** GNU Free Documentation License -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms -** and conditions contained in a signed written agreement between you -** and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! -\page qtqml-javascript-dynamicobjectcreation.html -\title Dynamic QML object creation from JavaScript -\brief instantiating and managing QML objects from JavaScript - -QML also supports the dynamic creation of objects from within JavaScript -code. This is useful if the existing QML elements do not fit the needs of your -application, and there are no C++ components involved. - -See the \l {declarative/toys/dynamicscene}{Dynamic Scene example} for a demonstration -of the concepts discussed on this page. - - -\section1 Creating Objects Dynamically - -There are two ways to create objects dynamically from JavaScript. You can either call -\l {QML:Qt::createComponent()}{Qt.createComponent()} to dynamically create -a \l Component object, or use \l{QML:Qt::createQmlObject()}{Qt.createQmlObject()} -to create an item from a string of QML. -Creating a component is better if you have an existing component defined in a \c .qml -file, and you want to dynamically create instances of that component. Otherwise, -creating an item from a string of QML is useful when the item QML itself is generated -at runtime. - - -\section2 Creating a Component Dynamically - -To dynamically load a component defined in a QML file, call the -\l {QML:Qt::createComponent()}{Qt.createComponent()} function in the -\l {QmlGlobalQtObject}{Qt object}. -This function takes the URL of the QML file as its only argument and creates -a \l Component object from this URL. - -Once you have a \l Component, you can call its \l {Component::createObject()}{createObject()} method to create an instance of -the component. This function can take one or two arguments: -\list -\li The first is the parent for the new item. Since graphical items will not appear on the scene without a parent, it is - recommended that you set the parent this way. However, if you wish to set the parent later you can safely pass \c null to - this function. -\li The second is optional and is a map of property-value items that define initial any property values for the item. - Property values specified by this argument are applied to the object before its creation is finalized, avoiding - binding errors that may occur if particular properties must be initialized to enable other property bindings. - when certain properties have been bound to before they have been set by the code. Additionally, there are small - performance benefits when compared to defining property values and bindings after the object is created. -\endlist - -Here is an example. First there is \c Sprite.qml, which defines a simple QML component: - -\snippet qml/Sprite.qml 0 - -Our main application file, \c main.qml, imports a \c componentCreation.js JavaScript file -that will create \c Sprite objects: - -\snippet qml/createComponent.qml 0 - -Here is \c componentCreation.js. Notice it checks whether the component \l{Component::status}{status} is -\c Component.Ready before calling \l {Component::createObject()}{createObject()} -in case the QML file is loaded over a network and thus is not ready immediately. - -\snippet qml/componentCreation.js vars -\codeline -\snippet qml/componentCreation.js func -\snippet qml/componentCreation.js remote -\snippet qml/componentCreation.js func-end -\codeline -\snippet qml/componentCreation.js finishCreation - -If you are certain the QML file to be loaded is a local file, you could omit the \c finishCreation() -function and call \l {Component::createObject()}{createObject()} immediately: - -\snippet qml/componentCreation.js func -\snippet qml/componentCreation.js local -\snippet qml/componentCreation.js func-end - -Notice in both instances, \l {Component::createObject()}{createObject()} is called with -\c appWindow passed as an argument so that the created object will become a child of the -\c appWindow item in \c main.qml. Otherwise, the new item will not appear in the scene. - -When using files with relative paths, the path should -be relative to the file where \l {QML:Qt::createComponent()}{Qt.createComponent()} is executed. - -To connect signals to (or receive signals from) dynamically created objects, -use the signal \c connect() method. See -\l{Signal and Handler Event System#Connecting Signals to Methods and Signals} -{Connecting Signals to Methods and Signals} for more information. - -It is also possible to instantiate components without blocking via the -\l {Component::incubateObject()}{incubateObject()} function. - -\section2 Creating an Object from a String of QML - -If the QML is not defined until runtime, you can create a QML item from -a string of QML using the \l{QML:Qt::createQmlObject()}{Qt.createQmlObject()} function, as in the following example: - -\snippet qml/createQmlObject.qml 0 - -The first argument is the string of QML to create. Just like in a new file, you will need to -import any types you wish to use. The second argument is the parent item for the new item; -this should be an existing item in the scene. The third argument is the file path to associate -with the new item; this is used for error reporting. - -If the string of QML imports files using relative paths, the path should be relative -to the file in which the parent item (the second argument to the method) is defined. - - -\section1 Maintaining Dynamically Created Objects - -When managing dynamically created items, you must ensure the creation context -outlives the created item. Otherwise, if the creation context is destroyed first, -the bindings in the dynamic item will no longer work. - -The actual creation context depends on how an item is created: - -\list -\li If \l {QML:Qt::createComponent()}{Qt.createComponent()} is used, the creation context - is the QQmlContext in which this method is called -\li If \l{QML:Qt::createQmlObject()}{Qt.createQmlObject()} - if called, the creation context is the context of the parent item passed to this method -\li If a \c {Component{}} item is defined and \l {Component::createObject()}{createObject()} - or \l {Component::incubateObject()}{incubateObject()} is called on that item, - the creation context is the context in which the \c Component is defined -\endlist - -Also, note that while dynamically created objects may be used the same as other objects, they -do not have an id in QML. - - -\section1 Deleting Objects Dynamically - -In many user interfaces, it is sufficient to set an item's opacity to 0 or -to move the item off the screen instead of deleting the item. If you have -lots of dynamically created items, however, you may receive a worthwhile -performance benefit if unused items are deleted. - -Note that you should never manually delete items that were dynamically created -by QML elements (such as \l Loader and \l Repeater). Also, you should avoid deleting -items that you did not dynamically create yourself. - -Items can be deleted using the \c destroy() method. This method has an optional -argument (which defaults to 0) that specifies the approximate delay in milliseconds -before the object is to be destroyed. - -Here is an example. The \c application.qml creates five instances of the \c SelfDestroyingRect.qml -component. Each instance runs a NumberAnimation, and when the animation has finished, calls -\c destroy() on its root item to destroy itself: - -\table -\row -\li \c application.qml -\li \c SelfDestroyingRect.qml - -\row -\li \snippet qml/dynamicObjects-destroy.qml 0 -\li \snippet qml/SelfDestroyingRect.qml 0 - -\endtable - -Alternatively, the \c application.qml could have destroyed the created object -by calling \c object.destroy(). - -Note that it is safe to call destroy() on an object within that object. Objects are not destroyed the -instant destroy() is called, but are cleaned up sometime between the end of that script block and the next frame -(unless you specified a non-zero delay). - -Note also that if a \c SelfDestroyingRect instance was created statically like this: - -\qml -Item { - SelfDestroyingRect { - // ... - } -} -\endqml - -This would result in an error, since items can only be dynamically -destroyed if they were dynamically created. - -Objects created with \l{QML:Qt::createQmlObject()}{Qt.createQmlObject()} -can similarly be destroyed using \c destroy(): - -\snippet qml/createQmlObject.qml 0 -\snippet qml/createQmlObject.qml destroy - -*/ diff --git a/src/qml/doc/src/javascript/expressions.qdoc b/src/qml/doc/src/javascript/expressions.qdoc index 73b27bd..3300794 100644 --- a/src/qml/doc/src/javascript/expressions.qdoc +++ b/src/qml/doc/src/javascript/expressions.qdoc @@ -30,43 +30,130 @@ \brief Description of where JavaScript expressions are valid in QML documents -\code - -This article is a work-in-progress. - -\endcode - -JavaScript adds logic to QML components. Properties can bind -to JavaScript expressions or reside inline in functions or signal handlers. The -\l{QQmlEngine}{QML engine} will then interpret the expression to calculate new -property values or to execute a routine. - -The \l{JavaScript Host Environment} can run valid standard +The \l{JavaScript Host Environment} provided by QML can run valid standard JavaScript constructs such as conditional operators, arrays, variable setting, loops. In addition to the standard JavaScript properties, the \l {QML Global Object} includes a number of helper methods that simplify building UIs and interacting with the QML environment. The JavaScript environment provided by QML is stricter than that in a web -browser. In QML you cannot add, or modify, members of the JavaScript global -object. In regular JavaScript, it is possible to do this accidentally by using a -variable without declaring it. In QML this will throw an exception, so all local -variables should be explicitly declared. +browser. For example, in QML you cannot add, or modify, members of the +JavaScript global object. In regular JavaScript, it is possible to do this +accidentally by using a variable without declaring it. In QML this will throw +an exception, so all local variables should be explicitly declared. A complete +description of the limitations of JavaScript code in QML is included in an +\l{JavaScript Expression Restrictions in QML}{upcoming section}. + +There are various ways in which JavaScript expressions may be defined and used +in QML, including property bindings, signal handlers, custom methods and +JavaScript imports. + + + + +\section1 JavaScript Expressions in QML Objects + +QML \l{QML Object Types}{object types} defined in \l{QML Documents} +{QML documents} can make use of JavaScript expressions which implement program +logic. There are four ways that JavaScript can be used in a QML document: + +\list + \li \l{QML Object Attributes#property-initialization}{properties} can be + assigned \l{Property Binding}{bindings} which are defined with + JavaScript expressions, and which are automatically evaluated by the + \l{QQmlEngine}{QML engine} when any properties accessed in the binding + change, in order to ensure always-up-to-date property values. Binding + expressions can also perform function evaluation as an explicit side + effect + \li \l{QML Object Attributes#signal-handlers}{signal handlers} can be defined + which are automatically evaluated when the object emits the associated + signal + \li \l{QML Object Attributes#custom-methods}{custom methods} can be defined + in QML files as JavaScript functions + \li JavaScript files providing functions and variables can be + \l{Importing JavaScript Files In QML Documents}{imported} in a QML + document +\endlist + + + +\section2 Property Bindings + +During startup, the QML engine will set up and initialize the property +bindings. The JavaScript conditional operator is a valid property binding. + +\qml +import QtQuick 2.0 + +Rectangle { + id: colorbutton + width: 200; height: 80; + + color: mousearea.pressed ? "steelblue" : "lightsteelblue" + + MouseArea { + id: mousearea + anchors.fill: parent + } +} +\endqml + +In fact, any JavaScript expression (no matter how complex) may be used in a +property binding definition, as long as the result of the expression is a +value whose type can be assigned to the property. + +There are two ways to define a property binding: the first (and most common) +is, as previously shown, in a \l{QML Object Attributes#property-initialization} +{property initialization}. The second (and much rarer) way is to assign the +property a function returned from the \l{Qt::binding()}{Qt.binding()} function, +from within imperative JavaScript code, as shown below: + +\qml +import QtQuick 2.0 + +Rectangle { + id: colorbutton + width: 200; height: 80; + color: "red" + + MouseArea { + id: mousearea + anchors.fill: parent + } + + Component.onCompleted: { + color = Qt.binding(function() { return mousearea.pressed ? "steelblue" : "lightsteelblue" }); + } +} +\endqml -\section1 Adding Logic +See the \l{Property Binding}{property bindings} documentation for more +information about how to define property bindings, and see the documentation +about \l{qml-javascript-assignment} +{Property Assignment versus Property Binding} for information about how +bindings differ from value assignments. -The \l {QML Elements} provide a declarative way of creating and managing the -interface layout and scene. Binding properties or signal handlers to JavaScript -expressions adds logic to the QML application. + + +\section2 Signal Handlers + +QML object types can emit signals in reaction to certain events occurring. +Those signals can be handled by signal handler functions, which can be defined +by clients to implement custom program logic. Suppose that a button represented by a Rectangle element has a MouseArea and a -Text label. The MouseArea will call its \l{MouseArea::}{onPressed} handler when the user presses the defined interactive area. The QML engine will execute the -contents bound to the onPressed and onReleased handlers. Typically, a signal -handler is bound to JavaScript expressions to initiate other events or to simply -assign property values. +Text label. The MouseArea will emit its "pressed" signal when the user presses +the defined interactive area, which will automatically trigger the +\l{MouseArea::}{onPressed} handler, which can be defined by clients. The QML +engine will execute the JavaScript expressions defined in the onPressed and +onReleased handlers, as required. Typically, a signal handler is bound to +JavaScript expressions to initiate other events or to simply assign property +values. + +\qml +import QtQuick 2.0 -\code Rectangle { id: button width: 200; height: 80; color: "lightsteelblue" @@ -76,9 +163,11 @@ Rectangle { anchors.fill: parent onPressed: { + // arbitrary JavaScript expression label.text = "I am Pressed!" } onReleased: { + // arbitrary JavaScript expression label.text = "Click Me!" } @@ -90,32 +179,36 @@ Rectangle { text: "Press Me!" } } -\endcode +\endqml -During startup, the QML engine will set up and initialize the property -bindings. The JavaScript conditional operator is a valid property binding. +Please see the \l{Signal and Handler Event System} documentation for in-depth +discussion of signals and signal handlers, and see the +\l{QML Object Attributes} documentation for in-depth discussion of how +to define the implementation of signal handlers in QML with JavaScript. -\code -Rectangle { - id: colorbutton - width: 200; height: 80; - color: mousearea.pressed ? "steelblue" : "lightsteelblue" - MouseArea { - id: mousearea - anchors.fill: parent - } -} -\endcode +\section2 JavaScript Expressions in Functions -\section2 Inline JavaScript +Program logic can also be defined in JavaScript functions. These functions can +be defined inline in QML documents (as custom methods) or externally in +imported JavaScript files. -Small JavaScript functions can be written inline with other QML declarations. -These inline functions are added as methods to the QML element that contains -them. -\code + +\section3 Custom Methods + +Custom methods can be defined in QML documents and may be called from signal +handlers, property bindings, or functions in other QML objects. Methods +defined in this way are often referred to as "inline JavaScript functions" as +their implementation is included in the QML object type definition +(QML document), as opposed to an external JavaScript file. + +An example of an inline custom method is as follows: + +\qml +import QtQuick 2.0 + Item { function factorial(a) { a = parseInt(a); @@ -130,72 +223,59 @@ Item { onClicked: console.log(factorial(10)) } } -\endcode +\endqml The factorial function will run whenever the MouseArea detects a clicked signal. -As methods, inline functions on the root element in a QML component can be -invoked by callers outside the component. If this is not desired, the method -can be added to a non-root element or, preferably, written in an external -JavaScript file. +Importantly, custom methods defined inline in a QML document are exposed to +other objects, and therefore inline functions on the root object in a QML +component can be invoked by callers outside the component. If this is not +desired, the method can be added to a non-root object or, preferably, written +in an external JavaScript file. -\section2 JavaScript files +See the \l{QML Object Attributes} documentation for in-depth discussion of how +to define custom methods in QML with JavaScript code implementations. -Large blocks of JavaScript should be written in separate files. These files -can be imported into QML files using an \c import statement, in the same way -that \l {QML Modules}{modules} are imported. -For example, the \c {factorial()} method in the above example for \l {Inline JavaScript} -could be moved into an external file named \c factorial.js, and accessed like this: -\code +\section3 Functions in Imported JavaScript Files + +Non-trivial program logic is best separated into external JavaScript files. +These files can be imported into QML files using an \c import statement, in +the same way that \l {QML Modules}{modules} are imported. + +For example, the \c {factorial()} method in the above example for +\l{JavaScript Expressions in Custom Methods} could be moved into an external +file named \c factorial.js, and accessed like this: + +\qml import "factorial.js" as MathFunctions + Item { MouseArea { anchors.fill: parent onClicked: console.log(MathFunctions.factorial(10)) } } -\endcode +\endqml For more information about loading external JavaScript files into QML, read the section about \l{Importing JavaScript into QML}. -\section1 JavaScript Expressions -The \l{JavaScript Runtime}{JavaScript runtime} run regular JavaScript -expressions as defined by the - -\section2 Variables and Properties - --variables --basic data types --values and assigning --relate to property binding -\section2 Conditional Loops -- for loops et al. -- conditional operator -\section2 Data Structures -- arrays -- object -- relate to the content below about valid JS scope, objects, etc. -- more advanced data types such as accessing QML list +\section3 Connecting Signals to JavaScript Functions -\section2 Functions -- function declaration -- function assignment (return values) -- function parameters -- connecting functions -- importing libraries, functions -- difference between JS functions and signals and QML methods -\section3 Receiving QML Signals in JavaScript +QML object types which emit signals also provide default signal handlers for +their signals, as described in a previous section. Sometimes, however, a +client will want to cause a signal emitted from one object to trigger a +function defined in another object; and in that case, a signal connection +is often preferable. -To receive a QML signal, use the signal's \c connect() method to connect it to a JavaScript -function. - -For example, the following code connects the MouseArea \c clicked signal to the \c jsFunction() -in \c script.js: +A signal emitted by a QML object may be connected to a JavaScript function +by calling the signal's \c connect() method and passing the JavaScript function +as an argument. For example, the following code connects the MouseArea +\c clicked signal to the \c jsFunction() in \c script.js: \table \row @@ -203,133 +283,12 @@ in \c script.js: \li \snippet qml/integrating-javascript/script.js 0 \endtable -The \c jsFunction() will now be called whenever MouseArea's \c clicked signal is emitted. +The \c jsFunction() will now be called whenever MouseArea's \c clicked signal +is emitted. See \l{QML Signal and Handler Event System#Connecting Signals to Methods and Signals} {Connecting Signals to Methods and Signals} for more information. -\section2 Advanced Usage -- using JS to access QML scene -- using JS for algorithms - - sorting, reordering lists -- how to modify other QML entities with JS - - - -\section1 Importing JavaScript into QML -Both relative and absolute JavaScript URLs can be imported. In the case of a -relative URL, the location is resolved relative to the location of the -\l {QML Document} that contains the import. If the script file is not accessible, -an error will occur. If the JavaScript needs to be fetched from a network -resource, the component's \l {QQmlComponent::status()}{status} is set to -"Loading" until the script has been downloaded. - -Imported JavaScript files are always qualified using the "as" keyword. The -qualifier for JavaScript files must be unique, so there is always a one-to-one -mapping between qualifiers and JavaScript files. (This also means qualifiers cannot -be named the same as built-in JavaScript objects such as \c Date and \c Math). - -\section2 Importing One JavaScript File From Another - -If a JavaScript file needs to use functions defined inside another JavaScript file, -the other file can be imported using the \l {QML:Qt::include()}{Qt.include()} -function. This imports all functions from the other file into the current file's -namespace. - -For example, the QML code below left calls \c showCalculations() in \c script.js, -which in turn can call \c factorial() in \c factorial.js, as it has included -\c factorial.js using \l {QML:Qt::include()}{Qt.include()}. - -\table -\row -\li {1,2} \snippet qml/integrating-javascript/includejs/app.qml 0 -\li \snippet qml/integrating-javascript/includejs/script.js 0 -\row -\li \snippet qml/integrating-javascript/includejs/factorial.js 0 -\endtable - -Notice that calling \l {QML:Qt::include()}{Qt.include()} imports all functions from -\c factorial.js into the \c MyScript namespace, which means the QML component can also -access \c factorial() directly as \c MyScript.factorial(). - -In QtQuick 2.0, support has been added to allow JavaScript files to import other -JavaScript files and also QML modules using a variation of the standard QML import -syntax (where all of the previously described rules and qualifications apply). - -A JavaScript file may import another in the following fashion: -\code -.import "filename.js" as UniqueQualifier -\endcode -For example: -\code -.import "factorial.js" as MathFunctions -\endcode - -A JavaScript file may import a QML module in the following fashion: -\code -.import Module.Name MajorVersion.MinorVersion as UniqueQualifier -\endcode -For example: -\code -.import Qt.test 1.0 as JsQtTest -\endcode -In particular, this may be useful in order to access functionality provided -via a module API; see qmlRegisterModuleApi() for more information. - -Due to the ability of a JavaScript file to import another script or QML module in -this fashion in QtQuick 2.0, some extra semantics are defined: -\list -\li a script with imports will not inherit imports from the QML file which imported it (so accessing Component.error will fail, for example) -\li a script without imports will inherit imports from the QML file which imported it (so accessing Component.error will succeed, for example) -\li a shared script (i.e., defined as .pragma library) does not inherit imports from any QML file even if it imports no other scripts -\endlist - -The first semantic is conceptually correct, given that a particular script -might be imported by any number of QML files. The second semantic is retained -for the purposes of backwards-compatibility. The third semantic remains -unchanged from the current semantics for shared scripts, but is clarified here -in respect to the newly possible case (where the script imports other scripts -or modules). -\section2 Code-Behind Implementation Files - -Most JavaScript files imported into a QML file are stateful implementations -for the QML file importing them. In these cases, for QML component instances to -behave correctly each instance requires a separate copy of the JavaScript objects -and state. - -The default behavior when importing JavaScript files is to provide a unique, isolated -copy for each QML component instance. The code runs in the same scope as the QML -component instance and consequently can can access and manipulate the objects and -properties declared. - -\section2 Stateless JavaScript libraries - -Some JavaScript files act more like libraries - they provide a set of stateless -helper functions that take input and compute output, but never manipulate QML -component instances directly. - -As it would be wasteful for each QML component instance to have a unique copy of -these libraries, the JavaScript programmer can indicate a particular file is a -stateless library through the use of a pragma, as shown in the following example. - -\code -// factorial.js -.pragma library - -function factorial(a) { - a = parseInt(a); - if (a <= 0) - return 1; - else - return a * factorial(a - 1); -} -\endcode - -The pragma declaration must appear before any JavaScript code excluding comments. - -As they are shared, stateless library files cannot access QML component instance -objects or properties directly, although QML values can be passed as function -parameters. @@ -338,16 +297,22 @@ parameters. It is occasionally necessary to run some imperative code at application (or component instance) startup. While it is tempting to just include the startup -script as \e {global code} in an external script file, this can have severe limitations -as the QML environment may not have been fully established. For example, some objects -might not have been created or some \l {Property Binding}s may not have been run. -\l {QML JavaScript Restrictions} covers the exact limitations of global script code. +script as \e {global code} in an external script file, this can have severe +limitations as the QML environment may not have been fully established. For +example, some objects might not have been created or some +\l {Property Binding}s may not have been run. \l {QML JavaScript Restrictions} +covers the exact limitations of global script code. + +Every QML object has an \e attached \l Component property that references the +component within which the object was instantiated. Every \l Component +will emit a \c completed signal, and thus every object can define an +implementation for the \c onCompleted() handler which can be used to trigger the +execution of script code at startup after the QML environment has been +completely established. For example: -The QML \l Component element provides an \e attached \c onCompleted property that -can be used to trigger the execution of script code at startup after the -QML environment has been completely established. For example: +\qml +import QtQuick 2.0 -\code Rectangle { function startupFunction() { // ... startup code @@ -355,41 +320,37 @@ Rectangle { Component.onCompleted: startupFunction(); } -\endcode +\endqml -Any element in a QML file - including nested elements and nested QML component -instances - can use this attached property. If there is more than one \c onCompleted() -handler to execute at startup, they are run sequentially in an undefined order. +Any object in a QML file - including nested objects and nested QML component +instances - can use this attached property. If there is more than one +\c onCompleted() handler to execute at startup, they are run sequentially in +an undefined order. -Likewise, the \l {Component::onDestruction} attached property is triggered on -component destruction. +Likewise, the \l {Component::onDestruction} handler definitions are triggered +on component destruction. -\section1 JavaScript and Property Binding -Property bindings can be created in JavaScript by assigning the property the value returned -by calling Qt.binding() where the parameter to Qt.binding() is a \c function -that returns the required value. -See \l {qml-javascript-assignment}{Property Assignment versus Property Binding} for details. -\section1 QML JavaScript Restrictions +\section1 JavaScript Expression Restrictions in QML QML executes standard JavaScript code, with the following restrictions: \list \li JavaScript code cannot modify the global object. -In QML, the global object is constant - existing properties cannot be modified or -deleted, and no new properties may be created. +In QML, the global object is constant - existing properties cannot be modified +or deleted, and no new properties may be created. -Most JavaScript programs do not intentionally modify the global object. However, -JavaScript's automatic creation of undeclared variables is an implicit modification -of the global object, and is prohibited in QML. +Most JavaScript programs do not intentionally modify the global object. +However, JavaScript's automatic creation of undeclared variables is an implicit +modification of the global object, and is prohibited in QML. -Assuming that the \c a variable does not exist in the scope chain, the following code -is illegal in QML. +Assuming that the \c a variable does not exist in the scope chain, the +following code is illegal in QML: \code // Illegal modification of undeclared variable @@ -559,5 +520,3 @@ the references will be affected. \snippet qml/integrating-javascript/scarceresources/avatarExample.cpp 5 */ - -*/ diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc index 01c55a0..ad2b905 100644 --- a/src/qml/doc/src/javascript/hostenvironment.qdoc +++ b/src/qml/doc/src/javascript/hostenvironment.qdoc @@ -77,7 +77,7 @@ QML implements the following restrictions for JavaScript code: \li The value of \c this is undefined in QML in the majority of contexts. \endlist -See \l {QML JavaScript Restrictions} for more details on these restrictions. +See \l {JavaScript Expression Restrictions in QML} for more details on these restrictions. */ diff --git a/src/qml/doc/src/javascript/imports.qdoc b/src/qml/doc/src/javascript/imports.qdoc index 1381203..9e71368 100644 --- a/src/qml/doc/src/javascript/imports.qdoc +++ b/src/qml/doc/src/javascript/imports.qdoc @@ -28,4 +28,159 @@ \page qtqml-javascript-imports.html \title Importing JavaScript Files In QML Documents \brief Description of how to import and use JavaScript files in QML documents + +Both relative and absolute JavaScript URLs can be imported in QML documents. +In the case of a relative URL, the location is resolved relative to the +location of the \l {QML Document} that contains the import. If the script +file is not accessible, an error will occur. If the JavaScript needs to be +fetched from a network resource, the component's +\l {QQmlComponent::status()}{status} is set to "Loading" until the script has +been downloaded. + +Imported JavaScript files are always qualified using the "as" keyword. The +qualifier for JavaScript files must be unique, so there is always a one-to-one +mapping between qualifiers and JavaScript files. (This also means qualifiers +cannot be named the same as built-in JavaScript objects such as \c Date and +\c Math). + +The functions defined in an imported JavaScript file are available to objects +defined in the importing QML document, via the \c{"Qualifier.functionName()"} +syntax. + +\section1 Importing One JavaScript File From Another + +In QtQuick 2.0, support has been added to allow JavaScript files to import +other JavaScript files and also QML type namespaces using a variation of the +standard QML import syntax (where all of the previously described rules and +qualifications apply). + +A JavaScript file may import another in the following fashion: +\code +.import "filename.js" as Qualifier +\endcode +For example: +\code +.import "factorial.js" as MathFunctions +\endcode + +A JavaScript file may import a QML type namespace in the following fashion: +\code +.import TypeNamespace MajorVersion.MinorVersion as Qualifier +\endcode + +For example: +\code +.import Qt.test 1.0 as JsQtTest +\endcode + +In particular, this may be useful in order to access functionality provided +via a module API; see qmlRegisterModuleApi() for more information. + +Due to the ability of a JavaScript file to import another script or QML module +in this fashion in QtQuick 2.0, some extra semantics are defined: +\list +\li a script with imports will not inherit imports from the QML file which imported it (so accessing Component.error will fail, for example) +\li a script without imports will inherit imports from the QML file which imported it (so accessing Component.error will succeed, for example) +\li a shared script (i.e., defined as .pragma library) does not inherit imports from any QML file even if it imports no other scripts +\endlist + +The first semantic is conceptually correct, given that a particular script +might be imported by any number of QML files. The second semantic is retained +for the purposes of backwards-compatibility. The third semantic remains +unchanged from the current semantics for shared scripts, but is clarified here +in respect to the newly possible case (where the script imports other scripts +or modules). + + +\section2 Code-Behind Implementation Files + +Most JavaScript files imported into a QML file are stateful implementations +for the QML file importing them. In these cases, for QML component instances +to behave correctly each instance requires a separate copy of the JavaScript +objects and state. + +The default behavior when importing JavaScript files is to provide a unique, +isolated copy for each QML component instance. If that JavaScript file does +not import any other JavaScript files or QML type namespaces, its code will run +in the same scope as the QML component instance and consequently can can access +and manipulate the objects and properties declared in that QML component. +Otherwise, it will have its own unique scope, and objects and properties of the +QML component should be passed to the functions of the JavaScript file as +parameters if they are required. + +\section2 Shared JavaScript Files (Libraries) + +Some JavaScript files act more like libraries - they provide a set of helper +functions that take input and compute output, but never manipulate QML +component instances directly. + +As it would be wasteful for each QML component instance to have a unique copy of +these libraries, the JavaScript programmer can indicate a particular file is a +shared library through the use of a pragma, as shown in the following example. + +\code +// factorial.js +.pragma library + +var factorialCount = 0; + +function factorial(a) { + a = parseInt(a); + + // factorial recursion + if (a > 0) + return a * factorial(a - 1); + + // shared state + factorialCount += 1; + + // recursion base-case. + return 1; +} + +function factorialCallCount() { + return factorialCount; +} +\endcode + +The pragma declaration must appear before any JavaScript code excluding comments. + +Note that multiple QML documents can import \c{"factorial.js"} and call the +factorial and factorialCallCount functions that it provides. The state of the +JavaScript import is shared across the QML documents which import it, and thus +the return value of the factorialCallCount function may be non-zero when called +within a QML document which never calls the factorial function. + +As they are shared, .pragma library files cannot access QML component instance +objects or properties directly, although QML values can be passed as function +parameters. + +\section1 Including One JavaScript File From Another + +When a JavaScript file is imported, it must be imported with a qualifier. The +functions in that file are then accessible from the importing script via the +qualifier (that is, as \tt{Qualifier.functionName(params)}). Sometimes it is +desirable to have the functions made available in the importing context without +needing to qualify them, and in this circumstance the \l{QML:Qt::include()} +{Qt.include()} function may be used to include one JavaScript file from another. +This copies all functions from the other file into the current file's +namespace, but ignores all pragmas and imports defined in that file. + +For example, the QML code below left calls \c showCalculations() in \c script.js, +which in turn can call \c factorial() in \c factorial.js, as it has included +\c factorial.js using \l {QML:Qt::include()}{Qt.include()}. + +\table +\row +\li {1,2} \snippet qml/integrating-javascript/includejs/app.qml 0 +\li \snippet qml/integrating-javascript/includejs/script.js 0 +\row +\li \snippet qml/integrating-javascript/includejs/factorial.js 0 +\endtable + +Notice that calling \l {QML:Qt::include()}{Qt.include()} copies all functions +from \c factorial.js into the \c MyScript namespace, which means the QML +component can also access \c factorial() directly as \c MyScript.factorial(). + + */ -- 1.7.2.5