Skip to content
This repository was archived by the owner on Nov 17, 2022. It is now read-only.

Commit 0c6182f

Browse files
author
Nedyalko Nikolov
committed
Merge pull request #21 from NativeScript/nnikolov/EventsDocumentation
Adding documentation about events.
2 parents 5dc5b0f + 76ebe0e commit 0c6182f

File tree

2 files changed

+308
-3
lines changed

2 files changed

+308
-3
lines changed

bindings.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Generally almost every UI control (since all controls are created with data bind
1919

2020
* Target object should be a successor of **Bindable** class.
2121
* Target property should be a **dependency property** in order to use data binding from target to source (or two way data binding). A plain property could be used if there is no need of **twoWay** binding.
22-
* Data (business) object should raise **propertyChanged** event for every change in the value of the property.
22+
* Data (business) object should raise **propertyChange** event for every change in the value of the property.
2323

2424
##Direction of data flow
2525

@@ -34,7 +34,7 @@ In order to use this option binding options should set **twoWay as true**. Follo
3434

3535
* Creating binding in code.
3636

37-
1. In order to create a working binding first we should have a source object. Source object should raise **propertyChanged** event for every change of any property. NativeScript has a built-in class that fulfils that requirement (Observable). Following is a code snippet that creates an observable object instance.
37+
1. In order to create a working binding first we should have a source object. Source object should raise **propertyChange** event for every change of any property. NativeScript has a built-in class that fulfils that requirement (Observable). Following is a code snippet that creates an observable object instance.
3838

3939
``` JavaScript
4040
var observableModule = require("data/observable");
@@ -105,7 +105,7 @@ With an xml declaration we set only properies names both for target (text) and s
105105

106106
##Binding source
107107

108-
The important part of the data binding is setting the source object. NativeScript data binding works with any object that emits a **propertyChanged** event. On the process of creating binding source can be set as second parameter of the bind(bindingOptions, source) or could be omitted. In that case for source is used a special property named **bindingContext** of the Bindable class. The special about this property is that it is inheritable across the visual tree. This means that control can use the **bindingContext** (as source) of the first **parent** element with a explicitly set **bindingContext**. With the previous example **bindingContext** can be set either on Page instance or StackPanel instance and TextField will have a proper source for its "text" property binding.
108+
The important part of the data binding is setting the source object. NativeScript data binding works with any object that emits a **propertyChange** event. On the process of creating binding source can be set as second parameter of the bind(bindingOptions, source) or could be omitted. In that case for source is used a special property named **bindingContext** of the Bindable class. The special about this property is that it is inheritable across the visual tree. This means that control can use the **bindingContext** (as source) of the first **parent** element with a explicitly set **bindingContext**. With the previous example **bindingContext** can be set either on Page instance or StackPanel instance and TextField will have a proper source for its "text" property binding.
109109

110110
``` JavaScript
111111
page.bindingContext = source;

events.md

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
---
2+
nav-title: "NativeScript Events"
3+
title: "Events"
4+
description: "NativeScript Documentation: Events"
5+
position: 66
6+
---
7+
8+
#Events
9+
10+
##Overview
11+
12+
Event is a message sent from the event emitter for the occurrence of a specific action. This action could be generated by user action (such as finger tap), or by some program logic (indicates that downloading an image from a server is completed). The object that raises event is called **event sender** or just **sender**. The object that consumes the event is called **event listener** or just **listener**. Being a TypeScript (JavaScript) framework NativeScript cannot benefit from any language built-in event handling mechanism. Since events are very critical for any modern mobile application development NativeScript provides a class that powers the process of working with events. This class is called Observable ([API-Ref](./ApiReference/data/observable/observable.md)).
13+
14+
##How events work
15+
16+
Generally events are very similar to a radio station: **sender** plays music (fires an event) and **listener** dances (executes an action). As mentioned, NativeScript streamlines the events task with a class called Observable. It is one of the very basic classes within NativeScript framework so almost every NativeScript object (component) has an option for dealing with events.
17+
18+
##Adding an event listener
19+
20+
Adding an **event listener** means setting a function (method) which should be executed when event is raised. The example below shows how a function that prints a "Hello World!" message on the console is set to be executed when **button** is tapped.
21+
22+
* Adding an event handler with a short-hand syntax
23+
24+
This syntax is used to write an in-line function which should be executed when the **testButton** is tapped.
25+
26+
``` JavaScript
27+
var buttonModule = require("ui/button");
28+
var testButton = new buttonModule.Button();
29+
testButton.text = "Test";
30+
31+
testButton.on(buttonModule.knownEvents.tap, function (eventData) {
32+
console.log("Hello World!");
33+
});
34+
```
35+
``` TypeScript
36+
import buttonModule = require("ui/button");
37+
var testButton = new buttonModule.Button();
38+
testButton.text = "Test";
39+
40+
testButton.on(buttonModule.knownEvents.tap, function (eventData) {
41+
console.log("Hello World!");
42+
});
43+
```
44+
45+
Even though this short-hand syntax is very handy to assign some very simple functions it is missing some important features available in the original syntax:
46+
47+
* Adding an event handler
48+
49+
``` JavaScript
50+
var buttonModule = require("ui/button");
51+
var testButton = new buttonModule.Button();
52+
testButton.text = "Test";
53+
54+
var onTap = function (eventData) {
55+
console.log("Hello World!");
56+
};
57+
58+
testButton.addEventListener(buttonModule.knownEvents.tap, onTap, this);
59+
```
60+
``` TypeScript
61+
import buttonModule = require("ui/button");
62+
var testButton = new buttonModule.Button();
63+
testButton.text = "Test";
64+
65+
var onTap = function (eventData) {
66+
console.log("Hello World!");
67+
};
68+
69+
testButton.addEventListener(buttonModule.knownEvents.tap, onTap, this);
70+
```
71+
The only difference here is the third optional parameter which represents the **this** argument which will be used as **event handler** context (closure). In other words, if you need a **this** argument in your event handler function you have to use the long syntax, otherwise the **sender** object is used as the **this**.
72+
73+
* Adding an event handler with an xml declaration
74+
75+
Another option to set an event handler is via an xml declaration.
76+
77+
``` XML
78+
<Page>
79+
<StackPanel>
80+
<Button tap="onTap" />
81+
</StackPanel>
82+
</Page>
83+
```
84+
Of course we need a code behind file in order to write the body of the function. (Code behind file has same name as **xml** with **js** or **ts** extension).
85+
86+
``` JavaScript
87+
function onTap(eventData) {
88+
console.log("Hello World!");
89+
}
90+
exports.listViewItemTap = listViewItemTap;
91+
```
92+
``` TypeScript
93+
export function onTap(eventData) {
94+
console.log("Hello World!");
95+
}
96+
```
97+
98+
##Removing an event listener
99+
100+
Usually there is no need to remove the **event listener**. However there are some scenarios where we need to receive the event just once, or just remove the event listener in order to free resources.
101+
102+
* Remove an event listener with a short-hand syntax
103+
104+
``` JavaScript
105+
testButton.off(buttonModule.knownEvents.tap);
106+
```
107+
``` TypeScript
108+
testButton.off(buttonModule.knownEvents.tap);
109+
```
110+
This will remove all listeners for the tap event of the testButton instance. If there are more than one **event listener** objects, a second parameter with the name of the callback function could be set so that only the referenced event listener will be removed.
111+
112+
* Remove an event listener, long syntax
113+
114+
``` JavaScript
115+
testButton.removeEventListener(buttonModule.knownEvents.tap, onTap);
116+
```
117+
``` TypeScript
118+
testButton.removeEventListener(buttonModule.knownEvents.tap, onTap);
119+
```
120+
The difference between short and long syntax is the option of the long syntax to specify the **this** argument (which was used to add the event listener) if there are many event listeners with different **this** arguments.
121+
122+
> There is no syntax to remove an event listener via an xml declaration.
123+
124+
##PropertyChange event
125+
126+
Observable class has a built-in event called **propertyChange** which is called when any property has been changed. Subscribing to this event is very similar to previous examples.
127+
128+
``` JavaScript
129+
var observableModule = require("data/observable");
130+
var observableObject = new observableModule.Observable();
131+
132+
observableObject.on(observableModule.knownEvents.propertyChange, function(propertyChangeData){
133+
console.log(propertyChangeData.propertyName + " has been changed and new value is: " + propertyChangeData.value);
134+
});
135+
```
136+
``` TypeScript
137+
import observableModule = require("data/observable");
138+
var observableObject = new observableModule.Observable();
139+
140+
observableObject.on(observableModule.knownEvents.propertyChange, function(propertyChangeData){
141+
console.log(propertyChangeData.propertyName + " has been changed and new value is: " + propertyChangeData.value);
142+
});
143+
```
144+
What is important about this event is that it is very critical for the entire [data binding](./bindings.md) system to work. In order to take advantage of the data binding mechanism it is enough to simply make your business object **inherit** of the Observable class. The following example demonstrates how to do that:
145+
146+
``` JavaScript
147+
var observableModule = require("data/observable");
148+
var MyClass = (function (_super) {
149+
__extends(MyClass, _super);
150+
function MyClass() {
151+
_super.apply(this, arguments);
152+
}
153+
Object.defineProperty(MyClass.prototype, "myProperty", {
154+
get: function () {
155+
return this.get("myProperty");
156+
},
157+
set: function (value) {
158+
this.set("myProperty", value);
159+
},
160+
enumerable: true,
161+
configurable: true
162+
});
163+
return MyClass;
164+
})(observableModule.Observable);
165+
exports.MyClass = MyClass;
166+
```
167+
``` TypeScript
168+
import observableModule = require("data/observable");
169+
170+
export class MyClass extends observableModule.Observable {
171+
public get myProperty(): number {
172+
return this.get("myProperty");
173+
}
174+
175+
public set myProperty(value: number) {
176+
this.set("myProperty", value);
177+
}
178+
}
179+
```
180+
This code snippet will fire the **propertyChange** event when the value of the property is changed out-of-the-box.
181+
182+
##Creating a custom event
183+
184+
Using Observable, methods **get** and **set** works just great for the **propertyChange** event. In many cases, according to some business logic, there is a need to create a custom event. To fire (raise or emit) an event all we have to do is to Observable.notify() method with proper event data when the action is completed. By proper event data we refer to any **implementer** of the [EventData interface](./ApiReference/data/observable/EventData.md). This is the very basic information about an event (its name as **eventName** and instance of the **event sender** as **object**).
185+
186+
``` JavaScript
187+
var eventData = {
188+
eventName: "myCustomEventName",
189+
object: this
190+
};
191+
this.notify(eventData);
192+
```
193+
``` TypeScript
194+
var eventData: observableModule.EventData = {
195+
eventName: "myCustomEventName",
196+
object: this
197+
}
198+
this.notify(eventData);
199+
```
200+
The bare minimum needed to raise an event is the eventName - it will be used to execute all **event handlers** associated with this event.
201+
202+
Next step is to hook for this event.
203+
204+
``` JavaScript
205+
var myCustomObject = new MyClass();
206+
myCustomObject.on("myCustomEventName", function(eventData){
207+
console.log(eventData.eventName + " has been raised! by: " + eventData.object);
208+
})
209+
```
210+
Almost the same logic is implemented for the **propertyChange** event, so if your business logic has some special requirements **propertyChange** could be emitted manually via **notify()** method (without using Observable.set() method which also fires the **propertyChange** event).
211+
212+
##Avoiding memory leaks
213+
214+
Though the radio station comparison is convenient for understanding the concept, the situation is a bit more complicated in the inside. In order to be able to notify the listener, the sender has a pointer to the listener. This means that even if that listener object gets set to `null` or `undefined`, it is not eligible for garbage collection, since the sender is alive and has a live reference to the listener object. This could result in a memory leak in some scenarios when object lifetimes of the sender and the listener are quite different. Here's an example scenario: a UI element creates a lot of child controls, where every control hooks for an event of the parent and then a child control gets released (very similar to list view scrolling). In order to prevent such memory leaks it is a good practice to remove your event listener handler before releasing the listener object. Unfortunately in some cases we could define the exact time when we should call (off or removeEventListener) functions. Here comes another option in NativeScript framework **weakEvents**.
215+
216+
##Working with weak events
217+
218+
* Adding a weak event listener
219+
220+
Weak events as its name suggests creates an weak reference to the **listener** object, which allows this listener object to be released without removing the **event listener** pointer. Using weak event listeners is very similar to *normal* events. Here is the example with comments for each line.
221+
222+
``` JavaScript
223+
var weakEventListenerModule = require("ui/core/weakEventListener");
224+
var buttonModule = require("ui/button");
225+
var observableModule = require("data/observable");
226+
227+
var testButton = new buttonModule.Button();
228+
testButton.text = "Test";
229+
testButton.on(buttonModule.knownEvents.tap, function () {
230+
source.set("testProperty", "change" + counter);
231+
});
232+
233+
var source = new observableModule.Observable();
234+
235+
var counter = 0;
236+
var handlePropertyChange = function () {
237+
counter++;
238+
this.text = counter + "";
239+
};
240+
241+
var weakEL = weakEventListenerModule.WeakEventListener;
242+
var weakEventListenerOptions: weakEventListenerModule.WeakEventListenerOptions = {
243+
// a weak reference of the event listener object
244+
targetWeakRef: new WeakRef(this),
245+
// a weak reference if the event sender object
246+
sourceWeakRef: new WeakRef(this.source),
247+
// the name of the event
248+
eventName: observable.knownEvents.propertyChange,
249+
// the handler of the event
250+
handler: handlePropertyChange,
251+
// the context which will be used to execute the handler (optional)
252+
handlerContext: testButton,
253+
// a special property which is used for a extra event recognition (optional)
254+
key: this.options.targetProperty
255+
}
256+
weakEL.addWeakEventListener(this.weakEventListenerOptions);
257+
```
258+
``` TypeScript
259+
import weakEventListenerModule = require("ui/core/weakEventListener");
260+
import buttonModule = require("ui/button");
261+
import observableModule = require("data/observable");
262+
263+
var testButton = new buttonModule.Button();
264+
testButton.text = "Test";
265+
testButton.on(buttonModule.knownEvents.tap, function () {
266+
source.set("testProperty", "change" + counter);
267+
});
268+
269+
var source = new observableModule.Observable();
270+
271+
var counter = 0;
272+
var handlePropertyChange = function () {
273+
counter++;
274+
this.text = counter + "";
275+
};
276+
277+
var weakEL = weakEventListenerModule.WeakEventListener;
278+
var weakEventListenerOptions: weakEventListenerModule.WeakEventListenerOptions = {
279+
// a weak reference of the event listener object
280+
targetWeakRef: new WeakRef(this),
281+
// a weak reference if the event sender object
282+
sourceWeakRef: new WeakRef(this.source),
283+
// the name of the event
284+
eventName: observable.knownEvents.propertyChange,
285+
// the handler of the event
286+
handler: handlePropertyChange,
287+
// the context which will be used to execute the handler (optional)
288+
handlerContext: testButton,
289+
// a special property which is used for a extra event recognition (optional)
290+
key: this.options.targetProperty
291+
}
292+
weakEL.addWeakEventListener(this.weakEventListenerOptions);
293+
```
294+
295+
Previous example shows how to attach a **weak event listener** to an observable object instance. A closer look to the **handlePropertyChange** function shows that **text** property of the **this** object is changed when the **propertyChange** event is raised (via the button tap event). It demonstrates how to use the **handlerContext** property - the value of this property is taken as a **this** argument inside the **event handler** function.
296+
Generally **targetWeakRef** and **key** properties are not needed to invoke a function when an event is raised. However these properties support another useful option of the weak event listener (a possibility to remove an event listener) both properties are used as keys for a key-value pair that stores weak event listeners.
297+
298+
* Removing weak event listener
299+
300+
``` JavaScript
301+
weakEL.removeWeakEventListener(this.weakEventListenerOptions);
302+
```
303+
``` TypeScript
304+
weakEL.removeWeakEventListener(this.weakEventListenerOptions);
305+
```

0 commit comments

Comments
 (0)