開発者ブログ

ObservableArray

Last modified by fukudayasuo on 2014/03/05, 10:13

概要

ObservableArray(オブザーバブルアレイ)は、配列への操作を検出しイベントを送出する配列(※)です。
通常の配列と同じように使うことができ、その際の関数呼び出しを検出して要素の追加、削除、変更等を監視することができます。

(※厳密には、配列と同等の関数を持つオブジェクトです。)

ObservableArrayのAPIドキュメントは、こちらをご覧ください。

ObservableArrayは、関数の呼び出しをフックすることで変更を検出しています。
そのため、メソッドを使わず直接値を変更すると、変更を検出できません。
ObservableArrayを使用する際は、必ずObservableArrayのメソッドを使って操作するようにしてください。

インスタンスの生成・使い方

ObservableArrayは、h5.core.data.createObservableArray()メソッドを使って作成します。

// ObservableArrayインスタンスの作成
var obsAry = h5.core.data.createObservableArray();

// Arrayが持つメソッドを使うことができる
obsAry.push('A');     // ['A']
obsAry.unshift('B');  // ['B', 'A']

// ObservableArrayが独自に持つメソッド
obsArray.copyFrom(['A', 'B', 'C']);
  //obsArrayの中身は['A', 'B', 'C']になる(既に要素が入っていた場合は全て取り除かれる)
  //要素はシャローコピーされる
obsArray.equals(['A', 'B', 'C']);

使用上の注意

配列の要素(値)を取得するときは必ずget()メソッドを使用する

以下のようなコードでは、値を取得できません。

// 添え字アクセスでは値を取得できない
var val = sampleObsArray[2];

以下のようにget()メソッドを使用して下さい。

var val = sampleObsArray.get(2);

配列に要素をセットするときは必ずメソッドを使用する

以下のようなコードでは、変更を検出できません(配列に正しく値がセットされません)。

// この方法はNG
sampleObsArray[2] = 'newValue';

配列中の特定のインデックスの値を変えたい場合は、set()メソッドを使用してください(ver.1.1.3以降)。

var obsArray = h5.core.data.createObservableArray();
obsArray.push('A', 'B', 'C');
obsArray.set(1, 'newValue');  // ['A', 'newValue', 'C'] になる

配列を要求する関数にインスタンスを渡す際は、必要に応じてtoArray()を呼んで通常の配列にする

たとえばネイティブの配列はconcat()メソッドを持ち、引数に連結する配列をとります。
このとき、以下のようにすると、newArrayは意図した(連結された)配列になりません。

var newArray = nativeArray.concat(obsArray);

これは、ObservableArrayが実際には(配列ではなく)オブジェクトとして認識されるためです。
意図した通りに動作させるためには、obsArray.slice(0) または obsArray.toArray() を呼んで、通常の配列にしてください。
(※toArray()は、ver.1.1.2で追加されました。)

var newArray = nativeArray.concat(observableArray.toArray());
 //newArrayはネイティブの配列インスタンス

イベントリスナの登録

ObservableArrayにイベントリスナを登録することで、メソッドによる操作を監視することができます。
addEventListener()メソッドを使い、changeBeforechangeイベントに、それぞれ処理実行前処理実行後のイベントリスナを登録します。

changeBeforeイベント、changeイベントは、自分のインスタンスが変更される場合に限り発生します。
つまり、push()やsplice()関数を呼び出した場合はイベントが発生しますが、
concat()やslice()関数は新しく配列を作って返すだけで、自分のインスタンス自身は変更されないのでイベントは発生しません

function beforeListener(ev){
   // ここに配列操作前に行いたい処理を記述
   alert('メソッド"' + ev.method + '"が呼ばれました');
}

function changeListener(ev){
   // ここに配列操作後に行いたい処理を記述
   alert('メソッド"' + ev.method + '"の処理を実行しました');
}

var obsAry = h5.core.data.createObservableArray();

obsAry.addEventListener('changeBefore', beforeListener);
obsAry.addEventListener('change', changeListener);

// メソッド呼び出すと、beforeListener→メソッドの実行→changeListenerの順で実行される
obsArray.copyFrom([1, 2, 3]);

イベントリスナに渡されるイベントオブジェクトは以下のような構造です。

{
    type:             /* "changeBefore" または "change" */,
    method:           /* メソッド名 */,
    args:             /* メソッド呼び出し時に渡された引数 */,
    returnValue:      /* changeイベントの場合のみ。メソッドが返す値 */,
    target:           /* ObservableArrayインスタンス */,
    preventDefault:   /* changeBeforeイベントの時に実行すると配列操作をキャンセルする関数 */,
    stopImmediatePropagation: /* 以降のハンドラを、全て実行しないようにする関数 */
}

ただし、データアイテムが持つObservableArrayで、データアイテムが属しているデータモデルマネージャのbeginUpdate()/endUpdate()によってアップデートセッションが管理されている場合、changeイベントはendUpdate()のタイミングで発生し、イベントオブジェクトのmethod、args、returnValueはnullになります。

以下のコードでは、配列へのnull、undefinedが代入されたら警告を出します。


function beforeListener(ev){
   // 配列に要素が追加される可能性のあるメソッドなら、その引数をチェックする
   if ($.inArray(ev.method, ['unshift', 'push', 'splice', 'copyFrom'])) {
       // argumentsオブジェクトを配列に変換
       var args = h5.u.obj.argsToArray(ev.args);
       if (ev.method === 'splice') {
   if (args.length <= 2) {
       // spliceに引数が2つ以下なら要素追加はないので、チェックしない
return true;
            }
           // spliceの場合、第3引数以降をチェック対象にする
           args.shift();
            args.shift();
        }
       // nullまたはundefinedが含まれているかどうかチェックする
       for (var i = 0, l = args.length; i < l; i++){
           if (args[i] == null){
               // null,undefinedが含まれていたら、警告を出す
               alert('nullまたはundefinedが入れられました');
            }
        }
    }
}

function changeListener(ev){
   // ここに配列操作後に行いたい処理を記述
   alert('メソッド"' + ev.method + '"の処理を実行しました');
}
var notNullArray = h5.core.data.createObservableArray();

notNullArray.addEventListener('changeBefore', beforeListener);
notNullArray.addEventListener('change', changeListener);


notNullArray.copyFrom([1, 2, 3]);     // 'メソッド"copyFrom"の処理を実行しました'
notNullArray.copyFrom([1, 2, null]);  // 'nullまたはundefinedは入れられました' 'メソッド"copyFrom"の処理を実行しました'

Copyright (C) 2012-2017 NS Solutions Corporation, All Rights Reserved.