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() を呼び出し、データのリロードとレンダリング を実施します。