3. チュートリアル1 : TODOアプリの実装¶
ここではチュートリアル1の実装について解説します。
3.1. ディレクトリ構成¶
チュートリアル1 のソースは以下のようなディレクトリ構成になっています。
- index.html : メインの HTML ファイル
 - js
- baas.js : NEC BaaS JavaScript ライブラリ
 - config.js : 設定用 JavaScript
 - application.js : Todoアプリ JavaScript
 
 - css
- style.css : スタイルシート
 
 
3.2. HTMLファイル¶
HTMLファイルは以下のようになっています。
<!DOCTYPE html>
<html>
<head lang="ja">
    <meta charset="UTF-8">
    <title>Tutorial 1</title>
    <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
    <script src="js/baas.js"></script>
    <script src="js/config.js"></script>
    <script src="js/application.js"></script>
    <link rel="stylesheet" href="css/style.css"/>
</head>
<body>
<h1 id="title">TODO</h1>
<div id="app">
    <ul id="todos">
    </ul>
    <div id="new-todo">
        <input type="text" id="todoText" placeholder="What needs to be done?" autofocus/>
    </div>
</div>
</body>
</html>
- head セクションで、必要な JavaScript ファイルをすべてロードします。
- NEC BaaS JavaScript ライブラリは jQuery を必要とするので、最初に jQuery ライブラリをロードします。
 - ついで、NEC BaaS JavaScript ライブラリ、設定ファイル、アプリケーション JavaScript ファイルをロードします。
 
 - <div id="app"> タグ内にアプリケーションの本体が表示されます。
 - <ul id="todos"> タグ内に TODO リスト、<div id="new-todo"> タグ内に新規Todo入力用のinput タグを格納します。
 
3.3. application.js : アプリケーション JavaScript ファイル¶
application.js に Todo アプリの本体となる JavaScript コードを記述します。
application.js の全文を掲載します。詳細についてはこの後に解説します。
$(function() {
  // 初期化
  Nebula.initialize(NebulaConfig);
  var ENTER_KEY = 13;
  var BUCKET_NAME = "TodoTutorial1";
  var App = {
      // モデル
      model: {
          todos: []
      },
      // 初期化
      init: function () {
          var self = this;
          // 入力処理
          $("#todoText").keypress(function (e) {
              if (e.which == ENTER_KEY) {
                  self.addTodo($("#todoText").val());
                  $("#todoText").val("");
                  return false;
              }
          });
          // TODO をダウンロードする
          this.bucket = new Nebula.ObjectBucket(BUCKET_NAME);
          this.fetch();
      },
      // BaaS サーバから TODO をダウンロードする
      fetch: function() {
          var self = this;
          // クエリ生成
          var query = new Nebula.ObjectQuery();
          query.setSortOrder("updatedAt", true);
          // クエリ実行
          this.bucket.query(query)
              .then(function (objects) {
                  self.model.todos = objects;
                  self.render();
              })
              .catch(function (err) {
                  console.log(JSON.stringify(err));
              });
      },
      // TODO の追加
      addTodo: function (text) {
          var self = this;
          var data = { description: text };
          this.bucket.save(data)
              .then(function (object) {
                  self.fetch();
              })
              .catch(function (err) {
                  console.log(JSON.stringify(err));
              });
      },
      // TODO 削除
      deleteTodo: function (id) {
          var self = this;
          this.bucket.delete(id)
              .then(function () {
                  self.fetch(); // リロード
              })
              .catch(function (err) {
                  console.log(JSON.stringify(err));
              })
      },
      // ビューのアップデート
      render: function () {
          var self = this;
          var $todos = $("#todos");
          $todos.empty();
          for (var i in this.model.todos) {
              var todo = this.model.todos[i];
              // 削除ボタンを生成
              var delButton = $("<button/>");
              delButton.text("✖").addClass("destroy");
              // 削除イベントハンドラを設定
              (function() {
                  var id = todo._id;
                  delButton.click(function() {
                     self.deleteTodo(id);
                  });
              })();
              // TODO 一行分を生成
              var li = $("<li/>");
              li.text(todo.description).append(delButton);
              $todos.append(li);
          }
      }
  };
  App.init();
});
3.3.1. NEC BaaS ライブラリの初期化¶
まずはじめに NEC BaaS ライブラリ初期化の処理を行います。
$(function() {
  // 初期化
  Nebula.initialize(NebulaConfig);
初期化には Nebula.initialize() を使用します。 この API は、NEC BaaS の機能を呼び出す前に必ず1度実施する必要があります。
Nebula.initialize() には、設定情報を引き渡します。 本チュートリアルでは、この値は前節で設定した通り config.js 内に記述します。
var NebulaConfig = {
    "tenant": "5657fe61240d3e15d40acd37",
    "appId": "56ea65bb240d3e27263af479",
    "appKey": "KPQOlhm949cN6s1cXNsSRFwh8qUTKh8KE81SCFC7",
    "baseUri": "https://api.example.com/api",
    "offline" : false,
    "debugMode": "debug"
};
Nebula.initialize() には、テナントID, アプリケーションID、アプリケーションキー、 BaseURI などを指定します。
3.3.2. アプリケーションの初期化¶
Todo アプリケーションクラスの定義および初期化を行います。
var App = {
    // モデル
    model: {
        todos: []
    },
    /* 中略 */
};
App.init();
本チュートリアルでは、'App' 変数にアプリケーションの全ロジックを実装しています。
App.model には、Todo リストを格納します。
App クラスの初期化は、init() で行っています。
// 初期化
init: function () {
    var self = this;
    // 入力処理
    $("#todoText").keypress(function (e) {
        if (e.which == ENTER_KEY) {
            self.addTodo($("#todoText").val());
            $("#todoText").val("");
            return false;
        }
    });
    // バケットから TODO をダウンロードする
    this.bucket = new Nebula.ObjectBucket(BUCKET_NAME);
    this.fetch();
},
ここでは以下の処理を行っています。
- 入力フィールドに Todo テキストを入力したときのイベント処理(addTodo() を呼び出す)を設定する。
 - オブジェクトバケットを生成し、Todo のロードを開始する。
 
3.3.3. オブジェクトバケットについて¶
Todo の1件分の情報は以下のような JSON で表現します。
{ description: "打ち合わせ資料を作成する" }
Todo 情報の格納には、NEC BaaS の「オブジェクトストレージ」機能を使用します。 オブジェクトストレージには、JSON 形式のデータをそのまま格納することができます。
オブジェクトストレージにデータを格納するには、オブジェクトの入れ物となる「バケット」を 用意する必要があります。
リレーショナルデータベースとの対比でいうと、バケットとオブジェクトは以下のような関係になります。
- テーブル → バケット
 - (テーブルの)行 → オブジェクト
 
オブジェクトを格納するバケットは、前節に記載したとおり、デベロッパコンソールで作成済みです。
バケットを使用するには、まず最初に new Nebula.ObjectBucket() を呼び出して バケットのインスタンス変数を用意します。この後は、バケットに対して操作を 行うことで、データの読み書きを行うことができます。
3.3.4. サーバから Todo リストをダウンロードする¶
// BaaS サーバから TODO をダウンロードする
fetch: function() {
    var self = this;
    // クエリ生成
    var query = new Nebula.ObjectQuery();
    query.setSortOrder("updatedAt", true);
    // クエリ実行
    this.bucket.query(query)
        .then(function (objects) {
            self.model.todos = objects;
            self.render();
        })
        .catch(function (err) {
            console.log(JSON.stringify(err));
        });
},
fetch() で、サーバから Todo リストをクエリ・ダウンロードします。
クエリ条件は、new Nebula.ObjectQuery() で生成します。 クエリ条件は SQL データベースでいうところの SELECT 文に相当し、 検索するオブジェクトの絞込み、ソート条件の指定、スキップ件数・件数上限の指定などが可能です。 ここでは、オブジェクトの更新時刻 (updatedAt) 昇順でクエリを実施します。
注意
件数上限はデフォルトで100件となっています。 一回のクエリで 100件以上のデータを取得したい場合は、クエリ条件で件数上限値を明示的に指定する必要があります。
クエリの実行は、バケットの query() メソッドで実行します。 query() は Promise を返します。then() に成功時の処理を記述します。
then() には、取得したJSONオブジェクトの配列が渡されます。 各オブジェクトは以下のような形式の JSON オブジェクトになっています。
{
   "_id" : "53c3ad874b5450f1fb87456b",
   "description" : "打ち合わせ資料作成",
   "createdAt" : "2014-07-14T10:14:31.998Z",
   "updatedAt" : "2014-07-14T10:14:31.998Z",
   "ACL" : { "r" : [ "g:anonymous" ], "w" : [ "g:anonymous" ], "c" : [ ], "u" : [ ], "d" : [ ], "admin" : [ ] }
}
- _id はオブジェクトを一意に識別する ID です。SQLデータベースでいうところのプライマリキーです。
 - createdAt はオブジェクトを最初に作成したときの時刻(UTC)です。
 - updatedAt はオブジェクトを最後に更新したときの時刻(UTC)です。
 - ACL はオブジェクトのアクセス権限です。この例では、全ユーザ(anonymous)が読み書き可能です。
 - これ以外のフィールド(description)は、オブジェクトを作成したときに指定した値です。
 
ここでは model.todos にオブジェクトの配列を保存し、render() を呼び出して 画面のレンダリングを行うようにしています。
3.3.5. 画面の描画処理¶
    // ビューのアップデート
    render: function () {
        var self = this;
        var $todos = $("#todos");
        $todos.empty();
        for (var i in this.model.todos) {
            var todo = this.model.todos[i];
            // 削除ボタンを生成
            var delButton = $("<button/>");
            delButton.text("✖").addClass("destroy");
            // 削除イベントハンドラを設定
            (function() {
                var id = todo._id;
                delButton.click(function() {
                   self.deleteTodo(id);
                });
            })();
            // TODO 一行分を生成
            var li = $("<li/>");
            li.text(todo.description).append(delButton);
            $todos.append(li);
        }
    }
};
画面のレンダリングは render() で行っています。
ここでは model.todos に保存された Todo オブジェクトの一覧を <li> 要素を使って <ul id="todos"> 要素以下に追加していきます。
また、この際に Todo を削除するボタンを一緒に作成し <li> 要素以下に追加しています。 削除処理本体は deleteTodo() に実装しており、deleteTodo() に引き渡す引数は オブジェクトの _id プロパティ、すなわちデータの ID を渡すようにしています。
3.3.6. Todo の追加処理¶
// TODO の追加
addTodo: function (text) {
    var self = this;
    var data = { description: text };
    this.bucket.save(data)
        .then(function (object) {
            self.fetch();
        })
        .catch(function (err) {
            console.log(JSON.stringify(err));
        });
},
Todo の追加は addTodo() で行います。 本関数は、入力フィールドで Enter キーを押した時に呼び出され、 引数には入力フィールドに入力されたテキストが渡されます。
data に保存したい JSON データを作成し、バケットの save() メソッドを呼び出すことで データを BaaS サーバに保存します。
保存が成功したら、then() 内で fetch() を呼び出し、データのリロードとレンダリングを 実施します。
3.3.7. Todo の削除処理¶
// TODO 削除
deleteTodo: function (id) {
    var self = this;
    this.bucket.remove(id)
        .then(function () {
            self.fetch(); // リロード
        })
        .catch(function (err) {
            console.log(JSON.stringify(err));
        })
},
Todo の削除処理は deleteTodo() で行います。
オブジェクトの削除には、バケットの remove() メソッドを使用します。 引数にはオブジェクトの ID を渡します。
削除が成功したら、then() 内で fetch() を呼び出し、データのリロードとレンダリング を実施します。