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