イベントは、特定のアクションの発生を示すためにイベントエミッタから送信されるメッセージです。 この動作は、ユーザの動作(タップなど)またはプログラムロジック(たとえば、サーバからの画像のダウンロードが完了したことを示すなど)によって生成されます。 イベントを発生させるオブジェクトは、イベント送信者(sender)またはイベント発生者(event raiser)と呼ばれます。 イベントを消費するオブジェクトは、イベントリスナー(listener)またはイベントハンドラ(event handler)と呼ばれます。
NativeScriptフレームワークは、イベントを扱うプロセスを強化するObservableクラスを提供します。 NativeScriptフレームワーク内の基本クラスの1つであるため、ほとんどすべてのNativeScriptオブジェクト(コンポーネント)には、イベントを処理するためのオプションがあります。
イベントハンドラを追加することは、イベントが発生したときに実行される関数(メソッド)を設定することを意味します。
以下の例は、短縮構文(on)と完全な構文(addEventListener)を使用してイベントリスナーを追加する方法を示しています。 thisで表される3番目のオプション引数があります。このコードは、 "Hello World!"を表示する関数を設定する方法を示しています。 ボタンをタップするとコンソールにメッセージが表示されます。短縮構文と完全構文のどちらかを選択することも、XMLでイベントハンドラを宣言することもできます。
const Button = require("tns-core-modules/ui/button").Button;
const testButton = new Button();
testButton.text = "Test";
let onTap = function(args) {
console.log("Hello World!");
};
// Adding a listener with the short syntax
testButton.on(Button.tapEvent, onTap, this);
// Adding a listener with the full syntax
testButton.addEventListener(Button.tapEvent, onTap, this);
import { Button } from "tns-core-modules/ui/button";
import { GestureEventData } from "tns-core-modules/ui/gestures";
const testButton = new Button();
testButton.text = "Test";
export function onTap(args: GestureEventData) {
console.log("Tap arguments: ", args);
};
// Adding a listener with the short syntax
testButton.on(Button.tapEvent, onTap, this);
// Adding a lister with the full syntax
testButton.addEventListener(Button.tapEvent, onTap, this);
イベントハンドラを設定するもう1つの選択肢は、XML宣言を使用することです。 関数本体を作成するには、分離コードファイルが必要です(分離コードファイルのファイル名は同じですが、拡張子は.jsまたは.tsです)。
<!-- main-page.xml -->
<Page>
<StackLayout>
<Label touch="onTouch" />
</StackLayout>
</Page>
// main-page.js
function onTouch(args) {
console.log("Touch arguments: ", args);
}
exports.onTouch = onTouch;
// main-page.ts
export function onTouch(args: TouchGestureEventData) {
console.log("Touch arguments", args);
}
多くの場合、NativeScriptではMVVMパターンはビューのバインディングコンテキストを提供する独立したビューモデルと共に使用されます。 そのような場合、イベントハンドラはバインディングコンテキスト構文を介して提供されなければなりません。
JavaScriptの例
// main-view-model.js
const observableModule = require("tns-core-modules/data/observable");
function HomeViewModel() {
var viewModel = observableModule.fromObject({
onButtonTap: function (args) {
console.log("Button was pressed");
},
});
return viewModel;
}
module.exports = HomeViewModel;
// main-page.js
const HomeViewModel = require("./home-view-model");
function pageLoaded(args) {
const page = args.object;
const homeViewModel = new HomeViewModel();
page.bindingContext = homeViewModel;
}
exports.pageLoaded = pageLoaded;
<StackLayout class="home-panel">
<Button text="Button" tap="" />
</StackLayout>
TypeScriptの例
// main-view-model.ts
import { Observable } from 'tns-core-modules/data/observable';
import { GestureEventData } from "tns-core-modules/ui/gestures";
export class HomeViewModel extends Observable {
onButtonTap(args: GestureEventData): void {
console.log("Button was pressed");
}
constructor() {
super();
}
}
// main-page.ts
import { EventData } from 'tns-core-modules/data/observable';
import { Page } from 'tns-core-modules/ui/page';
import { HomeViewModel } from './home-view-model';
export function pageLoaded(args: EventData) {
const page = <Page>args.object;
page.bindingContext = new HomeViewModel();
}
<StackLayout class="home-panel">
<Button text="Button" tap="" />
</StackLayout>
通常は、イベントリスナーを削除する必要はありません。ただし、イベントを1回だけ受信したい場合やリソースを解放したい場合に、削除する必要があります。
以下の例では、testButtonインスタンスのtapイベントに対するすべてのリスナーを削除するために、短縮構文と完全構文を使用しています。 複数のオブジェクトがイベントリスナーの場合は、2番目のパラメータにコールバック関数の名前を設定できます。 このようにして、参照されたイベントリスナーだけが削除されます。異なるthis引数を持つ複数のイベントリスナが利用可能な場合、3番目のオプションパラメータが使用されます。
ボタンタップイベントリスナーの削除
const Button = require("tns-core-modules/ui/button").Button;
// Removing a listener with the short syntax
testButton.off(Button.tapEvent);
// Removing a listener with the full syntax
testButton2.removeEventListener(Button.tapEvent);
import { Button } from "tns-core-modules/ui/button";
// Removing a listener with the short syntax
testButton.off(Button.tapEvent);
// Removing a listener with the full syntax
testButton2.removeEventListener(Button.tapEvent);
イベントの引数の基本型はEventData型です。これは2つの共通のプロパティを提供します。
object
- イベントを発生させたObservableインスタンスeventName
- 発生したイベントの名前// example for using EventData interface
exports.onPageLoaded = function(args) {
let page = args.object;
}
// example for using EventData interface
export function onPageLoaded(args: EventData) {
let page = <Page>args.object;
}
<Page loaded="onPageLoaded">
NativeScriptには、EventDataインターフェースを拡張して特定のイベントに追加の機能を提供する特定のインターフェースが多数あります。 たとえば、TouchGestureEventDataは、 action,android,ios,type,view などの追加のプロパティを持つTouchイベントに提供されるインタフェースです。 特定のイベントを処理するときは、処理しているイベントデータの特定の引数についてAPIリファレンスを確認してください。
このObservableクラスは、プロパティが変更されたときに呼び出される組み込みイベントpropertyChangeを提供します。
以下のデモは、propertyChangeイベントを取得する方法を示しています。
const Observable = require("tns-core-modules/data/observable").Observable;
const observableObject = new Observable();
observableObject.on(Observable.propertyChangeEvent, function(propertyChangeData){
console.log(propertyChangeData.propertyName + " has been changed and the new value is: " + propertyChangeData.value);
});
import { Observable } from "tns-core-modules/data/observable";
const observableObject = new Observable();
observableObject.on(Observable.propertyChangeEvent, function(propertyChangeData: PropertyChangeData){
console.log(propertyChangeData.propertyName + " has been changed and the new value is: " + propertyChangeData.value);
});
このpropertyChangeイベントはデータバインディングシステム全体にとって重要です。 データバインディングメカニズムを利用するには、ビジネスオブジェクトにObservableクラスを継承させるだけです。
const observableModule = require("tns-core-modules/data/observable");
var MyClass = (function (_super) {
__extends(MyClass, _super);
function MyClass() {
_super.apply(this, arguments);
}
Object.defineProperty(MyClass.prototype, "myProperty", {
get: function () {
return this._myProperty;
},
set: function (value) {
this._myProperty = value;
},
enumerable: true,
configurable: true
});
return MyClass;
})(observableModule.Observable);
exports.MyClass = MyClass;
import { Observable } from "tns-core-modules/data/observable";
export class MyClass extends Observable {
private _myProperty:number;
get myProperty(): number {
return this._myProperty;
}
set myProperty(value: number) {
this._myProperty = value;
}
}
propertyChangeイベントが発生した後に受け取る引数はPropertyChangeData型です。このインタフェースは、5つの共通のプロパティを提供します。
object
- イベントを発生させたObservableインスタンスeventName
- 発生したイベントの名前oldValue
- プロパティの古い値propertyName
- 更新されたプロパティの名前value
- プロパティの新しい値ビジネスロジックで要求されている場合は、特定のアクションに対してカスタムイベントを発生させる(または発行する)ことができます。 そのためにはアクションが完了したときにObservable.notify()メソッドを呼び出します。 このメソッドは、EventDataインタフェースのすべての実装者をイベントデータとして受け取ります。 それには、イベントに関する基本的な情報(eventNameとしての名前、およびobjectとしてのイベント送信者のインスタンス)が含まれます。
let eventData = {
eventName: "myCustomEventName",
object: this
};
this.notify(eventData);
let eventData: EventData = {
eventName: "myCustomEventName",
object: this
}
this.notify(eventData);
イベントを発生させるのに必要な最小限の情報はeventNameです。これは、このイベントに関連するすべてのイベントハンドラを実行するために使用されます。
次のステップはこのイベントにフックすることです:
let myCustomObject = new MyClass();
myCustomObject.on("myCustomEventName", function(eventData){
console.log(eventData.eventName + " has been raised! by: " + eventData.object);
})
propertyChangeイベントにも同様のロジックが実装されているため、ビジネスロジックでそれが必要な場合は、 (propertyChangeイベントを発生するObservable.set()メソッドを使用せずに)notify()メソッドを使用してpropertyChangeを手動で発行できます。
ラジオ局との比較は概念を理解するのに便利ですが、イベントは内部ではもう少し複雑です。 リスナーに通知できるように、送信者にはリスナーへのポインタが含まれています。 リスナーオブジェクトをnullまたはundefinedに設定した場合でも、 送信者は生きていてリスナーオブジェクトへのライブ参照を持っているため、ガベージコレクションには適していません。 送信者とリスナーのオブジェクトの有効期間が大幅に異なる場合、これによりメモリリークが発生する可能性があります。
このシナリオを考えます:UI要素はたくさんの子コントロールを作成します。 それぞれが親のイベントにフックします。 その後、子コントロールが解放され(リストビューのスクロール中など)、メモリリークが発生します。
これらのメモリリークを防ぐために、リスナオブジェクトを解放する前にイベントリスナハンドラを削除することをお勧めします。 残念ながら、しばしばoffまたはremoveEventListener関数を呼び出す正確なタイミングを判断できないことがあります。 そのような場合は、NativeScriptフレームワークの別のオプション(弱いイベント)を使用してください。
弱いイベントは、その名前が示すように、リスナーオブジェクトへの弱い参照を作成します。これは、イベントリスナーポインタを削除せずにリスナーオブジェクトを解放するのに役立ちます。
弱いイベントリスナーを使用することは、通常のイベントと非常によく似ています。以下のデモは、弱いイベントリスナーを追加する方法を示しています(コードのコメントは明確さのためのものも含まれています)。
var weakEventListenerModule = require("tns-core-modules/ui/core/weak-event-listener");
var Button = require("tns-core-modules/ui/button").Button;
var Observable = require("tns-core-modules/data/observable").Observable;
var testButton = new Button();
testButton.text = "Test";
testButton.on(Button.tapEvent, function () {
source.set("testProperty", "change" + counter);
});
const source = new Observable();
let counter = 0;
let handlePropertyChange = function () {
counter++;
this.text = counter + "";
};
let weakEL = weakEventListenerModule.WeakEventListener;
let weakEventListenerOptions: weakEventListenerModule.WeakEventListenerOptions = {
// create a weak reference to the event listener object
targetWeakRef: new WeakRef(this),
// create a weak reference to the event sender object
sourceWeakRef: new WeakRef(this.source),
// set the name of the event
eventName: observable.Observable.propertyChangeEvent,
// set the event handler
handler: handlePropertyChange,
// (optional) set the context in which to execute the handler
handlerContext: testButton,
// (optional) set a specialized property used for extra event recognition
key: this.options.targetProperty
}
weakEL.addWeakEventListener(this.weakEventListenerOptions);
import * as weakEventListenerModule from "tns-core-modules/ui/core/weak-event-listener";
import { Button } from "tns-core-modules/ui/button";
import { Observable } from "tns-core-modules/data/observable";
const testButton = new Button();
testButton.text = "Test";
testButton.on(Button.tapEvent, function () {
source.set("testProperty", "change" + counter);
});
const source = new Observable();
let counter = 0;
let handlePropertyChange = function () {
counter++;
this.text = counter + "";
};
let weakEL = weakEventListenerModule.WeakEventListener;
let weakEventListenerOptions: weakEventListenerModule.WeakEventListenerOptions = {
// create a weak reference to the event listener object
targetWeakRef: new WeakRef(this),
// create a weak reference to the event sender object
sourceWeakRef: new WeakRef(this.source),
// set the name of the event
eventName: observable.Observable.propertyChangeEvent,
// set the event handler
handler: handlePropertyChange,
// specialized property used for extra event recognition
key: this.options.targetProperty,
// (optional) set the context in which to execute the handler
handlerContext: testButton
}
weakEL.addWeakEventListener(this.weakEventListenerOptions);
イベントで関数を呼び出すときは、targetWeakRefおよびkeyプロパティはオプションです。 ただし、イベントリスナを削除することはできます。プロパティは、弱いイベントリスナーを格納するキーと値のペアのキーとして使用されます。
weakEL.removeWeakEventListener(this.weakEventListenerOptions);
weakEL.removeWeakEventListener(this.weakEventListenerOptions);