データ一覧画面の実装

次に、データ一覧画面の実装を説明します。

ログイン画面と同様に、データ一覧画面は index.html ファイルの <div> 要素内に定義されており、以下のように ID と JavaScript 関数が設定されています。

このページの JavaScript の実装は、list-page-callback.js ファイルに含まれています。

この画面では、HTML の <ul> タグを使用して、データの一覧表示を行います。index.html ファイルに空の <ul> 要素を定義しておき、ページの切り替え時やデータの更新時に DOM を使って <li> 要素を編集します。

データ構造

この画面では、JSON 形式のキーと値のペアを保持する KiiObject を作成し、それを Kii Cloud とやりとりします。データはログイン中のユーザーのスコープの Bucket myBucket に格納されます。

ここでは、KiiObject に格納するデータの作成、一覧取得(表示)、削除の各処理を実装します。

格納するデータ

Kii Cloud では任意の値や型を持ったデータを JSON 形式のキーと値のペアとして扱うことができます。

Hello Kii で扱うデータのキー名はプログラムで myObjectValue と指定されるため、上の図のような JSON 文字列として処理されます。

実際の開発時には、キー名も値の型もモバイルアプリの仕様に合わせて自由に決めることができます(ネストした JSON データも扱えます)。

値の型をスキーマなどの形で事前に指定する必要はありません。プログラムから KiiObject を作成するだけで、データの格納と取得を実行できます。また、JSON データの 1 階層目に格納された値からは自動的にインデックスが作成されるため、高速な検索を行うことができます。

データの一覧処理

ログイン画面でユーザーの作成またはログインが行われると、データ一覧画面に切り替わります。

ここでは、次の処理を順に行います。

  1. 画面の切り替え
  2. データの取得
  3. 取得したデータの処理

それぞれの処理について説明します。

画面の切り替え

ログイン処理やユーザー作成処理が成功すると、openListPage() 関数が呼び出されます。この関数では、まず画面の初期化を行うため、画面を切り替えて一覧をクリアします。

この部分のコードを抜粋します。

function openListPage() {
  document.getElementById("login-page").style.display = "none";
  document.getElementById("list-page").style.display = "block";

  // Clear the existing objects from the list.
  var listView = document.getElementById("list-view");
  listView.innerHtml = "";

  ......
}

Hello Kii では、DOM の style オブジェクトの display プロパティの値を切り替えることで画面遷移を実現しています。ログイン画面を非表示、データ一覧画面を表示に設定しています。

ページの切り替え後、念のため、一覧を初期化します。<ul> 要素 listViewinnerHtml プロパティを空にすることで、子要素を全件削除します。

データの取得

次に、表示する KiiObject を Kii Cloud から取得します。

一覧取得は、Bucket 内データの全件取得のクエリーを実行することで実現します。先ほどの openListPage() 関数の続きは、以下のようになります。

function openListPage() {
  ......

  // Create an empty KiiQuery. This query will retrieve all results sorted by the creation date.
  var queryObject = KiiQuery.queryWithClause(null);
  queryObject.sortByDesc("_created");

  // Get the defined bucket belonging to the user.
  var bucket = KiiUser.getCurrentUser().bucketWithName(BUCKET_NAME);

  // Perform the query asynchronously.
  bucket.executeQuery(queryObject, {
    // If the query succeeded
    success: function(queryPerformed, resultSet, nextQuery) {
      ......
    },
    // If the query failed
    failure: function(queryPerformed, errorString) {
      alert("Unable to execute query: " + errorString);
      console.log("Unable to execute query: " + errorString);
    }
  });
}

上のサンプルコードに示す BUCKET_NAME は以下のように宣言されています。

var BUCKET_NAME = "myBucket";

これを使って、次の処理を行っています。

  1. クエリーの準備

    全件取得のクエリーを準備します。

    まず、queryWithClause() メソッドによってクエリーオブジェクト queryObject を作成します。ここでは、全件取得を意味する空のクエリー(null)としています。

    さらに、取得時のソート条件を sortByDesc("_created") で指定しています。これは、KiiObject の作成日時(_created フィールド)の降順でのソートを意味します。データの作成時には新しい KiiObject を先頭に追加する仕様のため、降順でのソートとします。

    検索 API では、文字列や数値の比較によるクエリーや、And や Or などの検索条件の組み合わせもサポートしています。SQL ではなく、オブジェクトの構造によって条件式を表現します。

  2. Bucket の用意

    次に、検索対象の Bucket を用意します。データ作成時と同様に、取得したい Bucket 名(ログイン中のユーザーのスコープの myBucket)を保持する変数 BUCKET_NAME を引数に指定します。

  3. クエリーの実行

    手順 1 で作成した queryObjectbucketexecuteQuery() メソッドの第 1 引数に指定して、クエリーを実行します。

    今まで見てきた処理と同様に、クエリーの実行も Kii Cloud へのアクセスが必要なため、ノンブロッキング API で実行します。

    executeQuery() メソッドには成功と失敗のコールバック関数を指定します。成功時の処理は次のセクションで説明します。失敗時はメッセージを画面とログに出力します。

取得したデータの処理

クエリーを実行し、Bucket 内のデータを全件取得できた場合、success ブロックのコールバック関数でデータを画面に表示します。

bucket.executeQuery(queryObject, {
  // If the query succeeded
  success: function(queryPerformed, result, nextQuery) {
    console.log("Execute query: got " + result.length + " objects");

    // Iterate through the result set.
    for(var i = 0; i < result.length; i++) {
      // Create a row element with the object.
      var row = createListItem(result[i]);
      listView.appendChild(row);
    }
  },
  ......
});

取得に成功した場合、次の処理を行います。

success ブロックのコールバック関数の引数 result には、取得できた KiiObject が配列として格納されています。これをループによって 1 件ずつ取り出して、listView の要素として追加していきます。listView には、<ul> 要素に相当する DOM 要素がすでに取得されています。

追加する項目は createListItem() 関数で作成します。詳細は次に示します。

この実装は、取得件数が多くなると正しく動作しません。件数が多い場合、executeQuery() メソッドは ページネーション の機能によって、結果を複数のページに分割して返します。Hello Kii では実装の複雑化の防止のため、初めの 1 ページだけを処理しています。実装方法は、ページ最後の「より詳しく学びたい方へ」をご覧ください。

一覧の要素の表示

createListItem() 関数では一覧の項目 1 つを作成します。KiiObject からデータを取得して、<li> 要素に相当する DOM 要素を返します。

ここでは、次の図のような構造を作成します。

DOM の各要素を作成し、一覧に表示するテキストを保持する innerText プロパティと、スタイルシートの class 属性を保持する className プロパティを設定します。"Delete" ボタンには、削除処理のハンドラーも割り当てます。

要素の作成処理の実装は以下のとおりです。フレームワークを利用すると、これらの処理はフレームワークに依存した処理に置き換わるはずです。

function createListItem(object) {
  // Create elements of the list item.
  var elementItem = document.createElement('li');
  var elementTitle = document.createElement('div');
  var elementSubtitle = document.createElement('div');
  var elementDelete = document.createElement('div');

  // Set the value of each element.
  elementTitle.innerText = object.get(OBJECT_KEY);
  elementTitle.className = "item-title";

  elementSubtitle.innerText = object.objectURI();
  elementSubtitle.className = "item-subtitle";

  elementDelete.innerText = "Delete";
  elementDelete.className = "item-button";
  elementDelete.onclick = function (e) { deleteItem(this.parentNode, object); };

  // Set the elements as children of the list item.
  elementItem.appendChild(elementTitle);
  elementItem.appendChild(elementDelete);
  elementItem.appendChild(elementSubtitle);

  return elementItem;
}

特に重要な点は以下のとおりです。

  • タイトルとして、OBJECT_KEY(KiiObject の myObjectValue フィールド)の値を設定します。KiiObject の get() メソッドによって、MyObject 1 のような値を文字列で取得できます。

  • サブタイトルとして、KiiObject の URI を設定します。URI は Kii Cloud SDK において、アプリケーション内の KiiObject を一意に表現する文字列です(REST API では URI による表現を使用できません)。

  • "Delete" ボタンの onclick イベントのハンドラーとして、deleteItem() 関数と削除対象の KiiObject である object を指定しています。この object は、createListItem() 関数に渡された object です。

  • 最終的に、作成された <li> 要素である elementItem を関数の戻り値として返します。

このコードは、onclick イベントのハンドラーとして関数を直接設定する簡易実装のため、要素の削除時にメモリリークが発生します。

データの作成

次にデータの作成処理を説明します。

"Add Item" ボタンがクリックされると、addItem() 関数が呼び出されます。この関数のコードを抜粋すると、以下のようになります。

function addItem() {
  var value = "MyObject " + (++objectCount);

  var bucket = KiiUser.getCurrentUser().bucketWithName(BUCKET_NAME);

  // Create a new KiiObject instance and set the key-value pair.
  var obj = bucket.createObject();
  obj.set(OBJECT_KEY, value);

  // Save the object asynchronously.
  obj.save({
    // If the object was saved
    success: function(theSavedObject) {
      console.log("Save succeeded: " + JSON.stringify(theSavedObject));

      // Insert the object at the top of the list.
      var row = createListItem(theSavedObject);
      var listView = document.getElementById("list-view");
      listView.insertBefore(row, listView.firstChild);
    },
    // If the object was not saved
    failure: function(theObject, errorString) {
      alert("Unable to create object: " + errorString);
      console.log("Unable to create object: " + errorString);
    }
  });
}

上のサンプルコードに示す objectCountBUCKET_NAMEOBJECT_KEY は以下のように宣言されています。

var objectCount = 0;
var BUCKET_NAME = "myBucket";
var OBJECT_KEY = "myObjectValue";

ここでは、次の処理を順に行います。

  1. 格納するデータの作成

    KiiObject へ格納するデータ値を value に作成します。ここでは、連番を振り出して、MyObject 1 のような適当な文字列を作成しています。

  2. Bucket の準備

    ログイン中のユーザーのスコープの Bucket を準備します。

    前のページに示したように、getCurrentUser() メソッドによってログイン中のユーザーの KiiUser インスタンスを取得できます。このインスタンスに対して bucketWithName() メソッドを実行すると、このユーザーのスコープの Bucket を取得できます。取得したい Bucket 名を保持する変数 BUCKET_NAME を引数に指定します。

    Bucket が存在しない場合は、データ作成のタイミングで新規に作成されます。既存の Bucket がある場合はその Bucket が取得されます。

  3. KiiObject の準備

    createObject() メソッドによって、Bucket 内に KiiObject を作成します。

    set() メソッドによって KiiObject の JSON データの第 1 階層にキーと値のペアを設定します。以下のような JSON データが作成されます。

    {
      "myObjectValue" : "MyObject 1"
    }
    
  4. KiiObject の保存

    save() メソッドで KiiObject を保存します。手順 3 までの処理はデバイス上で実行されていましたが、ここで Kii Cloud と通信して Kii Cloud 上に KiiObject を保存します。通信を行うため、ここでもノンブロッキング API を使用しています。

    保存処理が完了すると、コールバック関数が呼び出されます。成功時にはすでに示した createListItem() 関数によって <li> 要素を作成し、DOM の操作によって一覧の先頭に追加します。

    保存処理に失敗した場合は、画面とコンソールにエラーメッセージを出力します。

データの削除

一覧内の "Delete" ボタンをクリックすると、その項目を削除できます。

画面を作成する際、"Delete" ボタン、deleteItem() 関数、および削除対象の KiiObject が関連付けられます。

"Delete" ボタンがクリックされると、deleteItem() 関数が呼び出されます。引数として、elementItem に DOM の <li> 要素が、object に削除対象の KiiObject が渡されます。

function deleteItem(elementItem, object) {
  // Delete the object asynchronously.
  object.delete({
    // If the object was deleted
    success: function(theDeletedObject) {
      console.log("Delete succeeded: " + JSON.stringify(theDeletedObject));

      var listView = document.getElementById("list-view");
      listView.removeChild(elementItem);
    },
    // If the object was not deleted
    failure: function(theObject, errorString) {
      alert("Unable to delete object: " + errorString);
      console.log("Unable to delete object: " + errorString);
    }
  });
}

実行している処理はここまで見てきた内容と同様です。引数として渡された KiiObject の delete() メソッドを呼び出して、ノンブロッキング API で Kii Cloud 上のデータを削除します。成功時は、DOM の操作によって <li> 要素を画面から削除します。


コールバックを使ったサンプルコードの説明は以上です。

Kii Cloud SDK の API を呼び出すだけでクラウド上のデータを操作できる点や、実装方法にはノンブロッキング API の呼び出しを利用するという特定のパターンがある点を理解できれば、他の機能も容易に実装できるはずです。


次は...

次は、リファレンスガイドを参照しながら機能を追加する手順を説明します。

プログラムの変更 に移動してください。

より詳しく学びたい方へ

  • Object の作成方法の詳細は KiiObject の作成 を、検索方法の詳細は KiiObject の検索 をご覧ください。特に、検索方法の詳細では、ページネーションの実装方法を案内しています。データ件数が多い場合の実装方法はこちらをご覧ください。
  • Object の URI と Object との関係は オブジェクトの ID と URI をご覧ください。たとえば、設定情報を 1 つの Object に格納して参照するような場合、URI を保存しておいて URI から Object を生成 するような使い方もできます。