JavaScript JavaScript Class
The JavaScript
tag is used to run JavaScript and assigns its module.exports
as data context for the parent visual.
Note: ECMAScript 5.1 is the only JavaScript version that is supported across all platforms. While newer JavaScript features might work on some devices, this can't be guaranteed (particularly for earlier iOS versions).
Getting started
JavaScript can be used in UX markup through the JavaScript
class, either by pointing to external JavaScript files, like this:
<JavaScript File="SomeCode.js" />
Or by inlining the JavaScript code in the tag, like this:
<JavaScript>
console.log("Hello, FuseJS!");
</JavaScript>
About FuseJS
FuseJS is a JavaScript framework for writing cross-platform mobile app business logic. It consists of a bunch of classes that covers the basic functionality required for creating native mobile apps, as well as the Observable class which lets you expose data to the UI in a functional reactive way.
Modules
FuseJS implements the CommonJS module system. Each code file or inline snippet is a module.
In order to expose data and functions to other modules, one can add them to the module.exports
object:
<JavaScript>
module.exports = {
exportedSymbol: "Hello, rest of the world!"
};
</JavaScript>
Failing to export from modules will make it impossible to reach defined data inside the module:
<JavaScript>
var data = [1, 2, 3];
var invisible = "I'm invisible";
module.exports = {
data: data
};
</JavaScript>
This is good for hiding implementation details from other calling JavaScript modules and UX code.
Importing modules
Each code file (or inline snippet) defines a module.
You can import JavaScript modules by their file name. To do this, make sure your JavaScript files are included in your .unoproj file as "Bundle" files:
"Includes": [
"yourJavaScriptFile.js:Bundle"
..other files ..
]
or if you want to make all JavaScript files be includes as bundled files:
"Includes": [
"**.js:Bundle"
]
Then, you can require using the JavaScript file name:
var myModule = require('/someJavaScriptFile.js');
Note that prefixing the file name with a "/" means that we are looking for the file relative to the project root directory. To name a file relative to the current file, prefix with "./". By omitting the prefixes, the file name is relative to the project root, or the global module it's in.
var relativeToProjectRoot = require('/SomeComponent');
var relativeFile = require('./MainView');
var relativeToRootOrGlobalModule = require('SomeOtherComponent.js');
Note that you may omit the .js file extension in the file name if you wish
Module instancing
Fuse's treatment of the <JavaScript>
tag has some important differences from how modules work in the CommonJS module system.
A module inside a <JavaScript>
tag (or pointed to in an external file) will be instantiated once for each time the surrounding UX scope is instantiated. This means that if the <JavaScript>
tag is part of a component, each instance of that component will initialise the code and have a separate set of the local variables and exports.
Cleaning up after modules
In Fuse, a JavaScript module can correspond to multiple module instances that get created and destroyed on the fly. If your module allocates resources that need manual cleanup, such as creating explicit Observable
subscriptions, you can assign a handler to module.disposed
and clean up after yourself there.
Example:
var foo = getSomeGlobalObservable();
function fooChanged() { ... }
foo.addSubscriber(fooChanged);
...
module.disposed = function () {
foo.removeSubscriber(fooChanged)
}
Design and motivation
The key design goal of FuseJS is to keep your JavaScript code small, clean and only concerned with the practical functions of your application. Meanwhile all things related to UX, such as layout, data presentation, animation and gesture response, is left to declarative UX markup and native UI components.
The way Fuse separates JavaScript business logic from UX markup presentation has some clear benefits:
- Performance - all the performance critical bits are handled in native code and based on native UI components.
- Easy - declarative code is easy to read, write and understand even with limited programming knowledge
- Less error prone - fewer states means fewer things can go wrong
- Visual tooling - UX markup can be edited by Fuse tools such as inspectors, timelines and generally cool drag & droppy stuff.
Note that Fuse has tons of declarative APIs (designed for UX markup) that replace the need for controlling animation from JavaScript (i.e. imperatively).
Many other JavaScript frameworks mix imperative UI code, animation and performance critical tasks into JavaScript, hence many people new to FuseJS tend to try doing things this way in the beginning. While most of these things are technically possible in FuseJS, it is discouraged. We recommend taking some time to study the Fuse examples to get a feel for the new way of doing things.
Purifying your code by separating view and logic into UX markup and JavaScript can shrink your code base significantly, make it more maintainable, and allow more effective collaboration between UX designers and developers.
If you need to write performance-critical business logic, we recommend doing that in native code or alternatively in Uno code instead of in JavaScript.
Location
- Namespace
- Fuse.Reactive
- Package
- Fuse.Scripting.JavaScript 2.9.1
Interface of JavaScript
Code : string ux
The inline JavaScript code.
Dependencies : IList of Dependency ux
DisposeModuleInstance uno
File : FileSource ux
The JavaScript file to read the code from.
JavaScript(NameTable) Constructor uno
Names : ScriptModuleNames ux
OnBeforeSubscribeToDependenciesAndDispatchEvaluate uno
Inherited from Node
Add(Binding) uno
Bindings : IList of Binding ux
The list of bindings belonging to this node.
ContextParent : Node uno
The context parent is the semantic parent of this node. It is where non-UI structure should be resolved, like looking for the DataContext, a Navigation, or other semantic item.
FindByType<T> : T uno
FindNodeByName(Selector, Predicate<Node> (Node)) : Node uno
Finds the first node with a given name that satisfies the given acceptor. The serach algorithm works as follows: Nodes in the subtree are matched first, then it matches the nodes in the subtrees ofthe ancestor nodes by turn all the way to the root. If no matching node is found, the function returns null.
GetNearestAncestorOfType<T> : T uno
Insert(int, Binding) uno
IsRootingCompleted : bool uno
Whether rooting for this node is completed. Returns false if unrooting has started.
IsRootingStarted : bool uno
Whether rooting of this node has started. Note that even if this property returns true, rooting may not yet be completed for the node. See also IsRootingCompleted.
Name : Selector ux
Run-time name of the node. This property is automatically set using the ux:Name attribute.
NextSibling<T> : T uno
Returns the next sibling node of the given type.
OnDataChanged(string, object) uno
OnRooted uno
If you override OnRooted
you must call base.OnRooted()
first in your derived class. No other processing should happen first, otherwise you might end up in an undefined state.
OnUnrooted uno
Parent : Visual uno
The parent Visual of this node. Will return null if the node is not rooted.
PreviousSibling<T> : T uno
Returns the next sibling node of the given type.
Properties : Properties uno
A linked list holding data for extrinsic properties.
Remove(Binding) : bool uno
SoftDispose uno
SourceFileName : string ux
hide
SourceLineNumber : int ux
hide
SubtreeToString : string uno
SubtreeToString(StringBuilder, int) uno
TryGetResource(string, Predicate<object> (object), object) : bool uno
VisitSubtree(Action<Node> (Node)) uno
Inherited from PropertyObject
AddPropertyListener(IPropertyListener) uno
OnPropertyChanged(Selector, IPropertyListener) uno
OnPropertyChanged(Selector) uno
RemovePropertyListener(IPropertyListener) uno
Inherited from object
Equals(object) : bool uno
GetHashCode : int uno
GetType : Type uno
ToString : string uno
Attached UX Attributes
GlobalKey (attached by Resource) : string ux
Implemented Interfaces
IModuleProvider uno
ISiblingDataProvider uno
When implemented by a Node
, it indicates that the node provides data for its siblings.
hide
IContext uno
IList<Binding> uno
IScriptObject uno
Interface for objects that can have a script engine representation
IProperties uno
ISourceLocation uno
hide