はじめに
本投稿は、2015/2/27に行われたAWS上で構築するRESTfulアプリ勉強会~Web開発ワークショップ~【第2回】の内容について後日まとめた資料です。
※ 参加された方へ。
当日は諸般の事情により当資料の準備が出来ず、GitHub上のソースを口頭で説明しながらとなってしまいました。
わかりにくかったと思います。申し訳ありません。
当日、もっとこう説明すればよかった、こういう手順が良かった、など反省しながら書きました。
この資料を元にもう一回やってみてフィードバックもらえると非常にありがたいです!
当勉強会は全12回、良くなかったところはどんどん改善していきますので、宜しくお願いいたします!
※ 資料の記述について
いろいろ作業手順があります。解説はけっこう細かく書いています。
作業としてやらないといけないところはで表示しています。
SSHログインのやり方等、前回やったことはAWS上で構築するRESTfulアプリ勉強会~Web開発ワークショップ~【第1回】の当日実施内容まとめ - Qiitaから確認して下さい。
2015/3/13追記 : 各lessonにGitにコミットする作業と、最後にpushする作業を追記しました。
前回までの成果確認
前回はEC2インスタンスを作成してサーバを作り、最低限のAPIを作成するところまでやりました。
まず下記2つをやって動作確認しておきましょう。
- EC2インスタンス起動
- API動作確認(POSTMAN使用)
今回の内容
今回はbackbone.jsを使用し、前回作成したサーバ側のAPIを呼び出してシンプルなTODOアプリを作成するところまでやります。
- 事前準備
- Lesson 1 ルーティング
- Lesson 2 MVC基本動作
- Lesson 3 一覧画面表示
- Lesson 4 一件追加
- Lesson 5 チェックボックス更新
- Lesson 6 一件削除
- Lesson 7 更新画面を表示
- Lesson 8 更新処理
の順に進めていきます。
その前に、backbone.jsについて基礎的なことを確認しておきましょう。
backbone.jsとは?
クライアント側のMVCフレームワークです。
今回backboneを使用しますが、そもそも何をしてくれて、使うと何が嬉しいんでしょうか?
ライブラリやフレームワークを使用しなかった場合と比較すると、
- いわゆるMVCパターンをクライアント側で実現できる
- ソースの記述量が減る
- ソースの見通しが良くなる
といったあたりかと思います。
※ backboneを使用しても、大規模開発になると気を付けないとやっぱりカオスになりますが...
なぜbackbone.js?
backbone.jsは、まず「軽い」という特徴があります。
MVCの骨組みだけを提供する、まさに背骨(backbone)ですので、比較的学習コストが低く、他のライブラリとの併用もやりやすく、本体にはない機能もいろいろなプラグインを利用して実現できます(第3回で取り上げる予定のmarionetteとか)。
今回は基礎的な内容のみですが、基礎を抑えておけば大規模開発にも対応できると思います。
巷ではAngularJSが流行ってますし、React.jsなんかもキてますが、その前に比較的シンプルであるbackboneでクライアントMVCの基礎を学んでおくことで、他のフレームワーク使用する場合でも、それらを学ぶ際の土台となるノウハウが得られると思います。
※ Reactなんかはbackboneと連携してもいいと思います(調べてみよう...)。
backboneの構成要素
backboneの構成要素について具体的に見ていきます。
MVCの各役割を、backboneでは下記のコンポーネントがそれぞれ担っています。
|M/V/C|Backboneのコンポーネント
------+-----------------------------------|
|Model|Backbone.Model, Backbone.Collection|
|View |Backbone.View, HTMLテンプレート※|
|Controller|Backbone.View, Backbone.Router|
※HTMLテンプレートはbackboneの構成要素ではありませんが、説明のため便宜的に入れています。
図にするとこんな感じです。
なんかちょっと違和感ありますね。
ひとつづつ見ていきます。
Model
Modelは、Backbone.ModelとBackbone.Collectionが担います。
コントローラからの操作により下記を行います。
- Backbone.Model
- データ一件に対して何かする。
- Backbone.Collection
- データ(Backbone.Model)をまとめて扱う。
完了するとイベントを発火します。
ともに、サーバとREST APIを通してデータのやり取りをします。
Modelに関してはわかりやすいかと思います。
View
Viewは、Backbone.ViewとHTMLテンプレートが担います。
モデルからのイベントを受け取って動くのが基本です。
- Backbone.View
- 表示のためのロジック
- HTMLテンプレート
- ブラウザに表示されるHTMLの断片
MVCにおけるViewの役割は、Modelの変更を検知して表示することがメインです。
Backbone.Viewが、HTMLテンプレートを部品として使用して表示ロジックを担う、という理解でいいと思います。
Viewも違和感はないかと思います。
Controller
Controllerは、Backbone.ViewとBackbone.Routerが担います。
違和感の正体はここですね。
Viewに入っていたBackbone.ViewがControllerも担っている?
これについて説明します。
- Backbone.View
- DOMからのイベント(ユーザの操作など)を拾って関数実行
- Backbone.Model, Backbone.Collectionからのイベントを拾って関数実行
つまり、Backbone.Viewは、「View」と言う名前ながら、
- イベントを拾って処理の流れをコントロールするというControllerの役割
- Modelの変更時に表示内容を更新するというViewの役割
の2つを兼ねているわけです。
図では、時計回りに綺麗にイベント、操作が単方向に伝播していますが、実際にはViewが2箇所に登場していたのでそんなに単純じゃなかったのです。
これは、「Backboneはそういうものだ」と理解しましょう!
Backbone.Routerは、
- URLの変更を検知して関数実行
という役割を担います。いわゆるルーティングです。
ルーティングを「コントローラ」に含めるかどうかは微妙ですが、ここではこうしておきましょう。
- Backbone.Routerが、「URL変更イベント」を拾って画面を遷移させる=Viewを消去し、新しいViewを表示する
- Backbone.Viewが「画面内のイベントを拾って処理する」
という理解でいいと思います。
Viewについては、
###Backbone.ViewはViewでもありControllerでもある
と納得してしまいましょう!
事前準備
SSHでログイン
ともかくログインします。sshログインから始まります!
-
ssh -i [秘密鍵のパス] study@[サーバのPublicIP]
ディレクトリを移動しておきます。
-
cd /var/www/study/rest-study
では、はじめましょう!
gitのブランチを整えておく
前回は、vol/01
ブランチを作成し、そこでソースを作成し、commit, pushし、GitHub上でそのvol/01
が確認できるところまでやりました。
下記の手順でブランチを整えておきます。
第1回用のbranch(vol/01
)をmasterにmergeしておく
-
master
ブランチをチェックアウト
git checkout master
-
vol/01
ブランチをmaster
にマージgit merge vol/01
[study@ip-172-31-8-2 rest-study]$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
[study@ip-172-31-8-2 rest-study]$ git merge vol/01
Updating d234729..55ef625
Fast-forward
app/Config/routes.php | 7 +++++++
app/Controller/AppController.php | 5 ++++-
app/Controller/TodoListsController.php | 42 ++++++++++++++++++++++++++++++++++++++++++
app/Model/TodoList.php | 6 ++++++
4 files changed, 59 insertions(+), 1 deletion(-)
create mode 100644 app/Controller/TodoListsController.php
create mode 100644 app/Model/TodoList.php
[study@ip-172-31-8-2 rest-study]$
これで、masterブランチに前回の成果が反映されました。
GitHubにpushしてGitHub側も最新にしておく
git push origin master
-> ユーザ名、パスワードを入力
[study@ip-172-31-8-2 rest-study]$ git push origin master
Username for 'https://github.com': ks-ocean
Password for 'https://ks-ocean@github.com':
Total 0 (delta 0), reused 0 (delta 0)
To https://github.com/ks-ocean/rest-study.git
d234729..55ef625 master -> master
GitHubの画面を確認しておく。
https://github.com/[ユーザ名]/rest-study/commits/master
で、vol/01
のブランチの作業内容がマージされていることが下図の通り確認できます。
fork元のリポジトリと同期をとっておく
fork元のリポジトリはここ
-
fork元のリポジトリを
upstream
という名前で追加します。git remote add upstream https://github.com/suzukishouten-study/rest-study.git
-
fork元リポジトリ
upstream
の内容を取得します。git fetch upstream
-
確認します。
git branch -av
[study@ip-172-31-8-2 rest-study]$ git remote add upstream https://github.com/suzukishouten-study/rest-study.git
[study@ip-172-31-8-2 rest-study]$ git fetch upstream
[study@ip-172-31-8-2 rest-study]$ git branch -av
* master 55ef625 vol/01 complete
vol/01 55ef625 vol/01 complete
remotes/origin/HEAD -> origin/master
remotes/origin/master 55ef625 vol/01 complete
remotes/origin/vol/01 55ef625 vol/01 complete
remotes/origin/vol/01-finish 59d8146 add simple api
remotes/upstream/master d234729 first commmit
remotes/upstream/vol/01-finish 59d8146 add simple api
remotes/upstream/vol/02-finish ec54cf5 8 更新処理
一番下に、remotes/upstream/vol/02-finish
というブランチが追加されています。
これは、今回の内容を全て終えた状態のものを事前にfork元リポジトリに用意しておいたものです。
このブランチをチェックアウトすれば、今回の完了状態のアプリケーションの動作を確認することができます。
GitHub上でも、
-
ここで
vol/02-finish
ブランチのコミット一覧 - [ここ](https://github.com/suzukishouten-study/rest-study/commit/
47ead20a422510a62722041940bad3d793533a52)で今回のLesson2の完成状態のソースやDiff表示
を見ることが出来ます。参考にしてください。
今回の作業用のbranchを作成
今回の作業用ブランチとして、vol/02
ブランチを作り、チェックアウトします。
-
git branch vol/02
でブランチを作って -
git checkout vol/02
でそのブランチに変更
#(確認のための git branch が入ってます)
[study@ip-172-31-8-2 rest-study]$ git branch vol/02
[study@ip-172-31-8-2 rest-study]$ git branch
* master
vol/01
vol/02
[study@ip-172-31-8-2 rest-study]$ git checkout vol/02
Switched to branch 'vol/02'
[study@ip-172-31-8-2 rest-study]$ git branch
master
vol/01
* vol/02
[study@ip-172-31-8-2 rest-study]$
これで準備は整いました。
さあ、作業を始めましょう!
まずはライブラリ取得
今回使用する、Backbone, underscore, jqueryのファイルを公式サイトから取得します。
下記の手順でやっていきましょう。
ディレクトリ作成
まずは配置先のディレクトリを作ります。
-
mkdir app/webroot/js/lib
を実行して作成先ディレクトリ作成 -
cd app/webroot/js/lib
を実行して移動し、次の手順へ進みましょう。
各ライブラリをダウンロード
wget
コマンドを使用してダウンロードします。
ダウンロードはそれぞれ下記URLからです(全て公式サイト)。
- backbone
http://backbonejs.org/backbone-min.js
- underscore
http://underscorejs.org/underscore-min.js
- jquery
http://code.jquery.com/jquery-2.1.3.min.js
コマンドは下記の通り。
-
wget http://backbonejs.org/backbone-min.js
-
wget http://underscorejs.org/underscore-min.js
-
wget http://code.jquery.com/jquery-2.1.3.min.js
[study@ip-172-31-8-2 lib]$ wget http://backbonejs.org/backbone-min.js
--2015-03-05 11:49:33-- http://backbonejs.org/backbone-min.js
backbonejs.org (backbonejs.org) をDNSに問いあわせています... 192.30.252.154, 192.30.252.153
backbonejs.org (backbonejs.org)|192.30.252.154|:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 302 Found
場所: /backbone-min.js [続く]
--2015-03-05 11:49:34-- http://backbonejs.org/backbone-min.js
backbonejs.org (backbonejs.org)|192.30.252.154|:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 19999 (20K) [application/javascript]
`backbone-min.js' に保存中
100%[================================================================>] 19,999 111KB/s 時間 0.2s
2015-03-05 11:49:34 (111 KB/s) - `backbone-min.js' へ保存完了 [19999/19999]
[study@ip-172-31-8-2 lib]$ wget http://underscorejs.org/underscore-min.js
--2015-03-05 11:49:34-- http://underscorejs.org/underscore-min.js
underscorejs.org (underscorejs.org) をDNSに問いあわせています... 192.30.252.154, 192.30.252.153
underscorejs.org (underscorejs.org)|192.30.252.154|:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 16523 (16K) [application/javascript]
`underscore-min.js' に保存中
100%[================================================================>] 16,523 89.8KB/s 時間 0.2s
2015-03-05 11:49:35 (89.8 KB/s) - `underscore-min.js' へ保存完了 [16523/16523]
[study@ip-172-31-8-2 lib]$ wget http://code.jquery.com/jquery-2.1.3.min.js
--2015-03-05 11:49:35-- http://code.jquery.com/jquery-2.1.3.min.js
code.jquery.com (code.jquery.com) をDNSに問いあわせています... 94.31.29.53, 94.31.29.230
code.jquery.com (code.jquery.com)|94.31.29.53|:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 84320 (82K) [application/x-javascript]
`jquery-2.1.3.min.js' に保存中
100%[================================================================>] 84,320 389KB/s 時間 0.2s
2015-03-05 11:49:35 (389 KB/s) - `jquery-2.1.3.min.js' へ保存完了 [84320/84320]
[study@ip-172-31-8-2 lib]$ ls -l
合計 124
-rw-r--r-- 1 study wheel 19999 2月 4 01:34 backbone-min.js
-rw-r--r-- 1 study wheel 84320 12月 19 00:17 jquery-2.1.3.min.js
-rw-r--r-- 1 study wheel 16523 2月 22 23:14 underscore-min.js
[study@ip-172-31-8-2 lib]$
ライブラリの準備まで出来ました。
これ以降、Lesson1〜8まではソースを編集していきます。
Lessonが進むに従い、少しずつ機能が追加され、lesson8で完成します。
各lessonは、
- Lesson完了後のアプリの動作
- 編集するファイル一覧
- 各ファイルの編集内容とポイント解説
というフォーマットで説明していきます。
※各ファイルの編集内容とポイント解説はかなり細かいので、頑張って読みましょう!
では、Lesson1です!
Lesson 1 ルーティング
Backbone.Routerの動きをまずは見ていきます。
Lesson完了後のアプリの動作
http://[PublicIP]/rest-study/#todo-lists
にアクセスすると、
こんな感じで表示されます。
Backbone.Routerの働きで、#todo-listsにアクセスすると「TODO一覧表示」と表示する関数が動いています。
http://[PublicIP]/rest-study/#todo-lists/1
にアクセスすると、
こんな感じで表示されます。
Backbone.Routerの働きで、#todo-listsにアクセスすると「id = X のTODO詳細表示」と表示する関数が動いています。
編集するファイル一覧
編集 | file | 役割 |
---|---|---|
修正 | app/View/Layouts/default.ctp | HTMLテンプレート |
追加 | app/webroot/js/routers/router.js | ルータ(Controller) |
追加 | app/webroot/js/app.js | アプリケーションの開始ポイント |
各ファイルの編集内容とポイント解説
default.ctp
HTMLテンプレートです。
CakePHPでは、標準でこのdefault.ctpが表示されます。
今回はこれをまるっと書き換えてHTMLテンプレートとして使用します。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>シンプルTODOアプリ</title>
</head>
<body>
<!-- ① js(library) -->
<script src="js/lib/jquery-2.1.3.min.js" type="text/javascript"></script>
<script src="js/lib/underscore-min.js" type="text/javascript"></script>
<script src="js/lib/backbone-min.js" type="text/javascript"></script>
<!-- js(application) -->
<!-- ② router -->
<script src="js/routers/router.js" type="text/javascript"></script>
<!-- ③ entry point -->
<script src="js/app.js" type="text/javascript"></script>
</body>
</html>
コメント中の①〜③でそれぞれ、
- ① jquery, underscore, backboneの各ライブラリを読み込んでいます。
- ② router.jsを読み込んでいます。
- ③ app.jsを読み込んでいます。
router.js
ルーティングを行っている本体です。
var app = app || {};
//router
(function(app) {
app.TodoRouter = Backbone.Router.extend({
routes : {
'' : 'todoLists',
'todo-lists' : 'todoLists',
'todo-lists/:id' : 'todoDetail'
},
todoLists : function() {
alert('TODO一覧表示');
},
todoDetail : function(id) {
alert('id = ' + id + ' のTODO詳細表示');
},
});
})(app);
※名前空間を使用しています。
上記の
var app = app || {};
(function(app) {
})(app);
といった記述がそれですが、ここに記載のやり方を採用しています。他のソースファイルも全て同様の方法を採用しています。
-
app.TodoRouter = Backbone.Router.extend({
この記述ですが、backboneが持っているBackbone.Routerオブジェクトを継承し、その継承した結果のオブジェクトをapp.TodoRouter
に設定する処理です。
この先、コレクション、モデル、ビューで同様の記述が出てきますが、全てbackboneが持っているオブジェクトを継承して定義する、という意味になります。 -
routes{}
変数
ここでは、URLと関数のペアを記述します。
URLがtodo-lists
ならtodoLists
関数を実行する、という意味です。 -
todoLists
関数
routes{}で記載したtodoLists
関数の本体です。ここで先ほど見たメッセージを表示するalert()関数を実行しています。 -
todoDetail
関数
同じくroutes{}で記載した関数の本体です。
app.js
先のdefault.ctpの中で最後に読み込まれていますので、必要なライブラリとrouter.jsを既に読み込んだ状態です。
var app = app || {};
//開始
(function(app) {
var todoRouter = new app.TodoRouter(); // ①
Backbone.history.start(); // ②
})(app);
①でapp.TodoRouterのインスタンスを作成しています。
②でBackbone.history.start();
とやってますが、下記に解説します。
#
付きのURL(正確には「URLフラグメント」)はページ内の位置を指す際に使用されますが、変更された場合、hashchange
イベントが発生します。素のjavascriptだと、下記のようwindow.onhashchange
にハンドラを書いてあげればhashchange
イベント発生時に関数を実行することができます。
window.onhashchange = function (e){
alert('hash change!');
};
Backbone.history.start()
は、backboneにhashchange
イベントの監視を開始させる、という意味を持ちます。これを実行することで、先ほどのrouter.jsのroutes{}
に設定した内容が生きてくる、というわけです。
実装
では、実装しましょう!
-
app/View/Layouts/default.ctp
を上記の通り作成。 -
app/webroot/js/routers/router.js
を上記の通り作成。 -
app/webroot/js/app.js
を上記の通り作成。 - 動作確認!
- Gitにコミット
Lesson2へ!
Lesson 2 MVC基本動作
Lesson1ではまずrouterの動きだけ見ました。
Lesson2では、Model, View, Controllerの動きの基本をすべて見てみます。
Lesson完了後のアプリの動作
画面上でわかりやすく見ることが難しいので、console.log()
を使用してログを出力し、内容を検証していきます。
http://[PublicIP]/rest-study/#todo-lists
にアクセスし、下図の通りchromeのデベロッパーツールを表示します。
※ここで、「ソースマップ機能」を無効にしておきます。
ダウンロードしたbackboneやunderscoreなどは、「ソースマップ」機能に対応していて、ソースはminifyされていますが、chrome上で設定すればminify前のような状態で参照することが出来ます。
設定しておかないとコンソールにエラーログが出てしまいます(動作上問題はないですが)ので、今回はこのへんは置いといて、chromeのソースマップ機能はOFFにします。
まず、デベロッパーツールの右上の歯車アイコンをクリックして設定を表示。
では動作確認。
下図の「ここをクリックしてクリア」でいったんログを削除し、ページを再ロードします。
consoleタブに、プログラム中でconsole.log()
した部分が表示されます。
ログの内容を解説します。
No. | ソース | ログ |
---|---|---|
1 | router.js:13 | Todo一覧表示用ビューにルーティング |
2 | todo-collection-view.js:8 | Todo一覧表示用ビュー初期化 |
3 | todo-collection-view.js:14 | Todo一覧表示用ビュー表示処理 |
4 | todo-collection-view.js:16 | コレクションをフェッチ |
5 | jquery-2.1.3.min.js | XHR finished loading: GET "http://[PublicIP]/rest-study/todo_lists.json". |
6 | todo-collection.js:11 | コレクションをパース |
7 | todo-model.js:9 | モデルをパース |
8 | todo-model.js:10 | ▶ Object {TodoList: Object} |
9 | todo-model.js:9 | モデルをパース |
10 | todo-model.js:10 | ▶ Object {TodoList: Object} |
11 | todo-model.js:9 | モデルをパース |
12 | todo-model.js:10 | ▶ Object {TodoList: Object} |
解説
※この例では、予めデータを3件登録してあります。
- rest-study/#todo-listsにアクセスしたことをrouterが検知し、Todo一覧表示用ビューを起動した
- Todo一覧表示用ビューの初期化処理が実行され、ビューが実体化
- Todo一覧表示用ビューの表示処理が開始される
- コレクションのフェッチ処理を実行、サーバ側APIへのアクセス開始
- jquery内でajax通信が実行され、
http://[PublicIP]/rest-study/todo_lists.json
にGetメソッドでアクセスし正常にデータを取得完了 - コレクション内で、5で取得したデータの解析(パース)処理が実行
- コレクション内の最初のデータ(モデル)の解析(パース)処理が実行
- 7の解析処理内で、取得したデータをダンプ出力(▶をクリックするとダンプの詳細が見れます。)
- コレクション内の2番目のデータの解析処理が実行
- 8同様取得したデータをダンプ
- コレクション内の3番目のデータの解析処理が実行
- 8同様取得したデータをダンプ
詳しくは、各ソースの解説で見ていきます。
編集するファイル一覧
編集 | file | 役割 |
---|---|---|
修正 | app/View/Layouts/default.ctp | HTMLテンプレート |
修正 | app/webroot/js/routers/router.js | ルータ(Controller) |
追加 | app/webroot/js/views/todo-collection-view.js | ビュー(Todoリスト一覧) |
追加 | app/webroot/js/collections/todo-collection.js | コレクション(Todo全件) |
追加 | app/webroot/js/models/todo-model.js | モデル(Todo一件) |
各ファイルの編集内容とポイント解説
これ以降、修正したファイルについては差分のDiff表示で示します。
- 「+」記号が追加行
- 「-」記号が削除行
- 何もない行は変更のなかった行(ファイル内の位置をわかりやすくするための表示)
です。
以降、各ファイルを淡々と解説しますが、上記のログと実装を確認しつつ、解析していきましょう。
default.ctp
<script src="js/lib/backbone-min.js" type="text/javascript"></script>
<!-- js(application) -->
+ <!-- model -->
+ <script src="js/models/todo-model.js" type="text/javascript"></script>
+ <!-- collection -->
+ <script src="js/collections/todo-collection.js" type="text/javascript"></script>
+ <!-- view -->
+ <script src="js/views/todo-collection-view.js" type="text/javascript"></script>
<!-- router -->
<script src="js/routers/router.js" type="text/javascript"></script>
<!-- entry point -->
追加したmodel, collection, view の各ファイル、
- todo-model.js
- todo-collection.js
- todo-collection-view.js
の各ファイルの読み込みを追加しています。
router.js
'todo-lists/:id' : 'todoDetail'
},
todoLists : function() {
- alert('TODO一覧表示');
+ //Todo一覧表示用ビューにルーティング
+ console.log("Todo一覧表示用ビューにルーティング");
+ new app.TodoCollectionView();
},
todoDetail : function(id) {
- Lesson1で表示したalertはいらないので削除
- ログ No.1で表示されていた内容はここで
console.log()
で表示 - app.TodoCollectionViewを初期化
ルーティングにより、Lesson1同様このtodoLists
関数が実行され、ログ出力とTodo一覧表示用ビューの初期化を行っています。これで、Todo一覧表示用ビューの実態であるtodo-collection-view.js
内の初期化処理が実行を開始します。
todo-collection-view.js
Todo一覧表示用ビューの実装です。
var app = app || {};
//Todo一覧表示用ビュー
(function(app) {
app.TodoCollectionView = Backbone.View.extend({
todoCollection : {},
initialize : function() {
console.log("Todo一覧表示用ビュー初期化");
//コレクションを生成
this.todoCollection = new app.TodoCollection()
this.render();
},
render : function() {
console.log("Todo一覧表示用ビュー表示処理");
//コレクションをフェッチ
console.log("コレクションをフェッチ");
this.todoCollection.fetch();
return this;
},
})
})(app);
- router内で
new app.TodoCollectionView()
としたタイミングで、initialize
関数が実行- ログ No.2を表示
-
app.TodoCollection
を初期化し、ローカル変数のtodoCollection
に格納 -
render()
関数を実行
- render()関数内の処理
- ログ No.3を表示
- todoCollectionの
fetch()
関数(サーバのAPIを呼び出す)を実行
todo-collection.js
TODOの一覧を扱う、コレクション(モデルを複数持っているイメージ)の実装です。
var app = app || {};
//Todo一覧表示用コレクション
(function(app) {
app.TodoCollection = Backbone.Collection.extend({
url : '/rest-study/todo_lists.json',
model : app.TodoModel,
parse : function(response) {
//コレクションをパース
console.log("コレクションをパース");
return response;
}
});
})(app);
-
url
変数
fetch
関数実行時にアクセスするサーバ側APIのURLを指定します。 -
model
変数
このコレクション内に含むモデルのオブジェクトを指定します。
ここでは、このコレクションに含まれるモデルは全てapp.TodoModel
であることを示しています。 -
parse
関数
fetch
関数(親のBackbone.Collectionが持っている)を実行し、正しくサーバからデータが取得できれば、parse
関数が実行されます。
ここでは、そのparse関数をオーバーライドし、ログNo.6を表示しています。
※この直前、parse関数が実行される前に、jquery内の処理でログNo5が表示されています。
todo-model.js
TODOの一件を扱う、モデルの実装です。
var app = app || {};
//Todoデータ1件を表すモデル
(function(app) {
app.TodoModel = Backbone.Model.extend({
urlRoot : '/rest-study/todo_lists',
parse : function(response) {
//モデルをパース
console.log("モデルをパース");
console.log(response);
return response.TodoList;
}
});
})(app);
-
urlRoot
変数
fetch
関数実行時にアクセスするサーバ側APIのURLを指定します。 -
parse
関数
fetch
関数を実行し、正しくサーバからデータが取得できれば、この関数が実行されます。
ここでは、ログNo.7を表示しています。この直前、parse関数が実行される前に、jquery内の処理でログNo5が表示されています。
※重要
parse関数内で、return response.TodoList
としていますが、これには下記の意味があります。
TODOリスト全体を取得した際、サーバから返ってくるJsonデータは、下記のフォーマットです。
[
{
"TodoList": {
"id": "1",
"todo": "TODO 1件目",
"status": "0"
}
},
{
"TodoList": {
"id": "2",
"todo": "TODO 2件目",
"status": "0"
}
},
{
"TodoList": {
"id": "3",
"todo": "TODO 3件目",
"status": "0"
}
}
]
データ一件分は、下記です。
"TodoList": {
"id": "1",
"todo": "TODO 1件目",
"status": "0"
}
全データがTodoList
というキー名のオブジェクトになっています。
ここでやっているのは、そのオブジェクトの中身、
{
"id": "1",
"todo": "TODO 1件目",
"status": "0"
}
だけをreturnし、後々のデータの扱いの利便性を図るためにやっています。
これがないと、idにアクセスする際、TodoList.id
、statusにアクセスする際はTodoList.status
と毎回やる手間をここで省いているわけです。
実装
では、実装しましょう!
-
app/View/Layouts/default.ctp
を上記の通り修正。 -
app/webroot/js/routers/router.js
を上記の通り修正。 -
app/webroot/js/views/todo-collection-view.js
を上記の通り作成。 -
app/webroot/js/collections/todo-collection.js
を上記の通り作成。 -
app/webroot/js/models/todo-model.js
を上記の通り作成。 - 動作確認!
- Gitにコミット
Lesson3へ!
Lesson 3 一覧画面表示
Lesson2では、MVCの基本的な流れを確認し、データが取得できていることを確認しました。
Lesson3では、その取得したデータを一覧表示してみます。
Lesson完了後のアプリの動作
http://[PublicIP]/rest-study/#todo-lists
にアクセスすると、下図の用にTODOの一覧が表示されます。
まだ表示されるだけです。
編集するファイル一覧
編集 | file | 役割 |
---|---|---|
修正 | app/View/Layouts/default.ctp | HTMLテンプレート |
修正 | app/webroot/js/views/todo-collection-view.js | ビュー(Todoリスト一覧) |
追加 | app/webroot/js/views/todo-item-view.js | ビュー(Todoリスト一覧内の1件表示用) |
各ファイルの編集内容とポイント解説
default.ctp
<title>シンプルTODOアプリ</title>
</head>
<body>
+ <!-- コンテンツ -->
+ <div id="content">
+ </div>
+ <!-- TODO一覧表示のテンプレート -->
+ <script type="text/template" id="list-template">
+ <h1>TODOリスト</h1>
+ <hr>
+ <div>
+ <table border="1" width="350px">
+ <tbody id="todo-lists"></tbody>
+ </table>
+ </div>
+ </script>
+
+ <!-- TODO一行分のテンプレート(上のtbody部分に挿入される) -->
+ <script type="text/template" id="item-template">
+ <td style="margin:0px">
+ <span class="todo-edit" style="margin:0px"><%- todo %></span>
+ </td>
+ </script>
<!-- js(library) -->
<script src="js/lib/jquery-2.1.3.min.js" type="text/javascript"></script>
@@ -17,6 +37,7 @@
<!-- collection -->
<script src="js/collections/todo-collection.js" type="text/javascript"></script>
<!-- view -->
+ <script src="js/views/todo-item-view.js" type="text/javascript"></script>
<script src="js/views/todo-collection-view.js" type="text/javascript"></script>
<!-- router -->
<script src="js/routers/router.js" type="text/javascript"></script>
- TODO一覧用のテンプレート(HTML断片)
-
<script type="text/template" id="list-template">
から始まる部分
-
- その中の一件を表示するためのテンプレート
-
<script type="text/template" id="item-template">
から始まる部分
を追加しています。
さらに、1件表示用のビューであるtodo-item-view.js
の読み込みを追加しています。
-
todo-collection-view.js
//Todo一覧表示用ビュー
(function(app) {
app.TodoCollectionView = Backbone.View.extend({
+ el : '#content',
+ tagName : 'div',
todoCollection : {},
initialize : function() {
- console.log("Todo一覧表示用ビュー初期化");
- //コレクションを生成
this.todoCollection = new app.TodoCollection()
+ this.todoCollection.on('add', this.addOne, this);
+ this.$el.html($('#list-template').html());
this.render();
},
render : function() {
- console.log("Todo一覧表示用ビュー表示処理");
- //コレクションをフェッチ
- console.log("コレクションをフェッチ");
this.todoCollection.fetch();
return this;
},
+
+ addOne : function(todo) {
+ var itemView = new app.TodoItemView({
+ model : todo
+ });
+ $('#todo-lists').append(itemView.render().el);
+ },
})
})(app);
削除したのはLesson2で入れたconsole.logなどです。
追加した行は以下。
-
el
変数Viewが管理するDOM上のエレメントを設定しておく変数です。
'#content'
を設定し、default.ctp内にある<div id="content">
要素を指しています。 -
tagName
変数Viewが
el
変数で示される要素内にDOMエレメントを追加する際のデフォルトのタグ名を設定します。 -
initialize
関数-
this.todoCollection.on('add', this.addOne, this);
collectionがデータ取得(fetch関数)の成功時に、collection内に1件データを格納するたびに
addイベント
を発火するのですが、そのイベントをキャッチして、addOne
関数が実行されるように指定しています。 -
this.$el.html($('#list-template').html());
el
変数で指定したエレメントに、default.ctpの'#list-template'
で指定されたテンプレートを描画しています。
-
-
addOne
関数
app.TodoItemView
(1件表示用のビュー)を初期化し、テンプレート内の'#todo-lists'
で指定された<tbody>
タグ内に、1件表示用のビューの描画結果全体を追加しています。これで、TODOが一件追加表示されています。
todo-item-view.js
Todoリスト一覧内の1件表示用のビューの実装です。
var app = app || {};
//Todo一覧の1件表示用ビュー
(function(app) {
app.TodoItemView = Backbone.View.extend({
//DOMに要素追加のタグ名
tagName : 'tr',
//テンプレート
template : _.template($('#item-template').html()),
initialize : function() {
},
render : function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
});
})(app);
-
tagName
変数一覧表示は
<Table>
タグで表示するので、1件分は<tr>
タグになります。 -
render
関数-
this.todoCollection.on('add', this.addOne, this);
collectionがデータ取得(fetch関数)の成功時に、collection内に1件データを格納するたびに
addイベント
を発火するのですが、そのイベントをキャッチして、addOne
関数が実行されるように指定しています。 -
this.$el.html($('#list-template').html());
el
変数で指定したエレメントに、default.ctpの'#list-template'
で指定されたテンプレートを描画しています。
-
-
addOne
関数
app.TodoItemView
(1件表示用のビュー)を初期化し、default.ctpの'#todo-lists'
で指定されたテンプレートを使用してモデルの内容を表示(DOM要素の生成)しています。
実装
では、実装しましょう!
-
app/View/Layouts/default.ctp
を上記の通り修正。 -
app/webroot/js/views/todo-collection-view.js
を上記の通り修正。 -
app/webroot/js/views/todo-item-view.js
を上記の通り作成。 - 動作確認!
- Gitにコミット
Lesson4へ!
Lesson 4 一件追加
TODOを追加できるようにしていきます。
Lesson完了後のアプリの動作
http://[PublicIP]/rest-study/#todo-lists
にアクセスすると、下図の通り、1件追加用のテキストエリアと追加ボタンが追加されています。
「TODO 4件目」と入力し、「追加」ボタンをクリックします。
編集するファイル一覧
編集 | file | 役割 |
---|---|---|
修正 | app/View/Layouts/default.ctp | HTMLテンプレート |
修正 | app/webroot/js/views/todo-collection-view.js | ビュー(Todoリスト一覧) |
各ファイルの編集内容とポイント解説
default.ctp
<!-- TODO一覧表示のテンプレート -->
<script type="text/template" id="list-template">
<h1>TODOリスト</h1>
+ <textarea style="width:300px;height:50px"id="new-todo" placeholder="Todo?" autofocus></textarea>
+ <input type="button" id="addTodo" value="追加">
<hr>
<div>
<table border="1" width="350px">
@@ -45,4 +47,4 @@
<script src="js/app.js" type="text/javascript"></script>
</body>
下記を追加しています。
- TODO入力用のテキストエリア
- 追加ボタン
todo-collection-view.js
tagName : 'div',
todoCollection : {},
initialize : function() {
this.todoCollection.on('add', this.addOne, this);
this.$el.html($('#list-template').html());
+ this.newTodo = this.$('#new-todo');
this.render();
},
+ events : {
+ 'click #addTodo' : 'onCreateTodo',
+ },
render : function() {
this.todoCollection.fetch();
return this;
},
+ onCreateTodo : function(e) {
+ this.todoCollection.create(this.newAttributes(), {
+ wait : true
+ });
+ this.newTodo.val('');
+ this.todoCollection.fetch();
+ },
addOne : function(todo) {
var itemView = new app.TodoItemView({
model : todo
});
$('#todo-lists').append(itemView.render().el);
},
+ newAttributes : function() {
+ return {
+ todo : this.newTodo.val().trim(),
+ status : 0
+ }
+ }
})
})(app);
-
initialize
関数-
this.newTodo = this.$('#new-todo');
$('#new-todo')はテキストエリアです。後で使用するためinisialize処理内で取得して変数に格納しています。
-
events
変数-
'click #addTodo' : 'onCreateTodo'
Viewのevents{}
には、DOMイベントとそのハンドラ関数の対応付けを行います。
backboneのViewがControllerの役割を兼ねているのはこの部分ですね。
ここでは、追加ボタンのClickイベントにonCreateTodo
関数を割り当てています。
-
-
onCreateTodo
関数
events{}で割り当てた追加ボタンの処理の実装です。- collectionの
create
関数を実行し、データを一件追加します。これで、サーバの一件追加APIを呼び出します(ここでは、エラー処理等は入れず、単に終了を待っています)。追加する内容は、newAttributes
関数で作成しています。 - 追加が成功したら、collectionをfetchし、最新のTODO一覧を取得しなおしています。
- collectionの
-
newAttributes
関数- todo : 入力値
- status : 0(未完了)
で新しいModelを生成してreturnしています。
-
実装
では、実装しましょう!
-
app/View/Layouts/default.ctp
を上記の通り修正。 -
app/webroot/js/views/todo-collection-view.js
を上記の通り修正。 - 動作確認!
- Gitにコミット
Lesson5へ!
Lesson 5 チェックボックス更新
チェックボックスを追加します。
TODOが完了したら、チェックを付ける機能です。
Lesson完了後のアプリの動作
http://[PublicIP]/rest-study/#todo-lists
にアクセスすると、下図の通り、TODO表示の左側にチェックボックスが追加されています。
図では、1件目がチェック付きの状態になっています。2件目にチェックを付けたらすぐサーバに反映されます。
リロードしてもチェックが残っていることが確認できます。
編集するファイル一覧
編集 | file | 役割 |
---|---|---|
修正 | app/View/Layouts/default.ctp | HTMLテンプレート |
修正 | app/webroot/js/models/todo-model.js | モデル(Todo一件) |
修正 | app/webroot/js/views/todo-item-view.js | ビュー(Todoリスト一覧内の1件表示用) |
各ファイルの編集内容とポイント解説
default.ctp
<!-- TODO一行分のテンプレート(上のtbody部分に挿入される) -->
<script type="text/template" id="item-template">
+ <td><input type="checkbox" class="toggle" <%- status === '1' ? 'checked' : '' %>></td>
<td style="margin:0px">
<span class="todo-edit" style="margin:0px"><%- todo %></span>
</td>
チェックボックスを表示するためのHTML断片を追加しています。
<%- status === '1' ? 'checked' : '' %>>
この部分、statusが1の場合はchecked属性を付加する、という処理を3項演算子で書いています。
todo-model.js
console.log("モデルをパース");
console.log(response);
return response.TodoList;
+ },
+ toggle : function() {
+ this.set('status', this.get("status") === '1' ? '0' : '1');
+ this.save();
}
});
})(app);
toggle
関数を追加しています。
model自身のstatusが、
- 0なら1に
- 1なら0に
と変換する処理を3項演算子で書いています。
その後、自身のsave()
関数でサーバ側APIを呼び出して保存しています。
viewの処理(チェックボックスをクリックされた時のハンドラ関数)内から呼び出されます。
todo-item-view.js
//テンプレート
template : _.template($('#item-template').html()),
+ //DOMイベントハンドラ設定
+ events : {
+ //チェックボックスクリック時
+ 'click .toggle' : 'onStatusToggleClick',
+ },
+
initialize : function() {
},
render : function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
+ onStatusToggleClick : function(e) {
+ this.model.toggle();
+ },
});
})(app);
events
変数
チェックボックスクリック時のハンドラ関数onStatusToggleClick
を設定しています。
onStatusToggleClick
関数
チェックボックスクリック時の実装です。
modelのtoggle
関数を実行し、ステータスを変更、保存しています。
実装
では、実装しましょう!
-
app/View/Layouts/default.ctp
を上記の通り修正。 -
app/webroot/js/models/todo-model.js
を上記の通り修正。 -
app/webroot/js/views/todo-item-view.js
を上記の通り修正。 - 動作確認!
- Gitにコミット
Lesson6へ!
Lesson 6 一件削除
不要なTODOを削除する機能を付けます。
Lesson完了後のアプリの動作
http://[PublicIP]/rest-study/#todo-lists
にアクセスすると、下図の通り、TODO表示の右側に削除用のリンクが追加されています。
編集するファイル一覧
編集 | file | 役割 |
---|---|---|
修正 | app/View/Layouts/default.ctp | HTMLテンプレート |
修正 | app/webroot/js/views/todo-item-view.js | ビュー(Todoリスト一覧内の1件表示用) |
各ファイルの編集内容とポイント解説
default.ctp
<td style="margin:0px">
<span class="todo-edit" style="margin:0px"><%- todo %></span>
</td>
+ <td>
+ <a class="remove-link" href="#">削除</a>
+ </td>
</script>
<!-- js(library) -->
削除用のリンクを表示するためのHTML断片を追加しています。
todo-item-view.js
events : {
//チェックボックスクリック時
'click .toggle' : 'onStatusToggleClick',
+ //削除ボタンクリック時
+ 'click .remove-link' : 'onRemoveClick',
},
initialize : function() {
+ this.listenTo(this.model, 'destroy', this.remove);
},
render : function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
onStatusToggleClick : function(e) {
this.model.toggle();
},
+ onRemoveClick : function(e) {
+ this.model.destroy({
+ wait : true
+ });
+ },
});
})(app);
events
変数
削除リンククリック時のハンドラ関数onRemoveClick
を設定しています。
initialize
関数
削除リンククリック時にmodelを削除(destroy)しますが、実際に削除された際にdestroy
イベントが発火されます。
そのイベントのハンドラ関数this.remove
を設定しています。
remove
関数はview自体を、そのviewのel変数で示されるDOM要素を全て破棄、および関連付けているイベントを全て破棄します。
これで画面上のTODO一件が削除されます。
onRemoveClick
関数
eventsで設定した関数の実装です。
modelを削除するdestroy関数を実行しています。
この後、実際に削除が完了すると、destoroy
イベントが発生し、initialize
関数で設定したとおりviewが削除されます。
実装
では、実装しましょう!
-
app/View/Layouts/default.ctp
を上記の通り修正。 -
app/webroot/js/views/todo-item-view.js
を上記の通り修正。 - 動作確認!
- Gitにコミット
Lesson7へ!
Lesson 7 更新画面を表示
TODOの内容を編集するための「詳細画面」に遷移する機能を実装します。
Lesson完了後のアプリの動作
http://[PublicIP]/rest-study/#todo-lists
にアクセスすると、下図の通り、削除用のリンクの右側に詳細リンクが追加されています。
TODO3件目の詳細リンクをクリックすると、下図の通り詳細画面に遷移します。
テキストエリアにTODOの内容が表示されています。編集できますが、まだキャンセルボタンしかありません。
キャンセルボタンをクリックするとTODO一覧に戻ります。
編集するファイル一覧
編集 | file | 役割 |
---|---|---|
修正 | app/View/Layouts/default.ctp | HTMLテンプレート |
修正 | app/webroot/js/routers/router.js | ルータ(Controller) |
追加 | app/webroot/js/views/todo-detail-view.js | ビュー(Todo1件の詳細画面表示用) |
各ファイルの編集内容とポイント解説
default.ctp
</head>
<body>
<!-- コンテンツ -->
- <div id="content">
- </div>
+ <div id="main"></div>
<!-- TODO一覧表示のテンプレート -->
<script type="text/template" id="list-template">
<h1>TODOリスト</h1>
@@ -29,8 +28,17 @@
</td>
<td>
<a class="remove-link" href="#">削除</a>
+ <a class="detail-link" href="#todo-lists/<%- id %>">詳細</a>
</td>
</script>
+ <!-- 詳細画面 -->
+ <script type="text/template" id="detail-template">
+ <h2>Todo #<%- id %></h2>
+ <div>
+ <textarea style="width:300px;height:50px" id="edit-todo" autofocus placeholder="Todo?"><%- todo %></textarea>
+ <input type="button" id="updateCancel" value="キャンセル"></input>
+ </div>
+ </script>
<!-- js(library) -->
<script src="js/lib/jquery-2.1.3.min.js" type="text/javascript"></script>
@@ -44,6 +52,7 @@
<script src="js/collections/todo-collection.js" type="text/javascript"></script>
<!-- view -->
<script src="js/views/todo-item-view.js" type="text/javascript"></script>
+ <script src="js/views/todo-detail-view.js" type="text/javascript"></script>
<script src="js/views/todo-collection-view.js" type="text/javascript"></script>
<!-- router -->
<script src="js/routers/router.js" type="text/javascript"></script>
-
<div id="content">
を、<div id="main">
に変更これは、後のrouter.jsの説明で述べますが、画面遷移時に
<div id="content">
要素を<div id="main">
の下に動的に追加するためです。 -
詳細画面へのリンクを追加
リンク先を
href="#todo-lists/<%- id %>"
としています。
これで、Lesson1で設定したルーティングに一致する#todo-lists/:id
へのリンクとなります。 -
詳細画面のテンプレートを追加
タイトル(id)の表示、テキストエリアとキャンセルボタンを追加しています。
-
詳細表示用ビューの読み込み追加
詳細表示用のビューである
todo-detail-view.js
の読み込みを追加しています。
router.js
+
+ currentView : false,
+
todoLists : function() {
//Todo一覧表示用ビューにルーティング
- console.log("Todo一覧表示用ビューにルーティング");
- new app.TodoCollectionView();
+ this.removeCurrentView();
+ this.nextView(app.TodoCollectionView);
},
todoDetail : function(id) {
- alert('id = ' + id + ' のTODO詳細表示');
+ this.removeCurrentView();
+ this.nextView(app.TodoDetailView, id);
},
+
+ nextView : function(View, option) {
+ if (document.getElementById('#content') === null) {
+ $('#main').append('<div id="content"/>');
+ }
+ this.currentView = new View(option);
+ },
+ removeCurrentView : function() {
+ if (this.currentView) {
+ this.currentView.remove();
+ }
+ }
+
});
})(app);
Lesson6までは画面が一つだけでしたが、詳細画面を追加しました。
その為、Viewの生成処理を変更し、さらにViewを破棄する処理を追加しています。
不要なconsole.logとalertは削除しています。
-
currentView
変数現在表示しているViewのインスタンスを格納するための変数です。
-
todoLists
関数修正前は単に、
new app.TodoCollectionView();
とTODO一覧用Viewを生成するだけでしたが、- 現在表示中のビュー(
currentView
変数が指しているビュー)を削除(removeCurrentView
関数) - TODO一覧用Viewを生成する(
nextView
関数)
の2つの処理になっています。
- 現在表示中のビュー(
-
todoDetail
関数todoLists
関数と同じく、removeCurrentView
関数とnextView
関数を実行しています。
nextView
関数には、app.TodoDetailView
id
の2つを引数として渡しています。
-
nextView
関数ここで、default.ctpの説明で少し触れた、content要素の動的追加を行っています。
content要素がなければ、main要素の下に追加するという処理をしています。
ビューの削除時にはcontent要素ごと削除されるので、content要素を追加してからビューを生成するわけです。詳細画面の場合、view引数=
app.TodoDetailView
, option引数=id
となっているので、
app.TodoDetailViewのinitialize
実行時にid
が渡されることになります。 -
removeCurrentView
関数currentView
変数が示すビューを削除しています。content要素ごと削除されます。
todo-detail-view.js
var app = app || {};
//詳細ビュー
(function(app) {
app.TodoDetailView = Backbone.View.extend({
el : '#content',
//テンプレート
template : _.template($('#detail-template').html()),
//DOMイベントハンドラ設定
events : {
//キャンセルボタンクリック時
'click #updateCancel' : 'onCancelClick',
},
//初期化
initialize : function(id) {
//Routerからidを受け取ってモデル生成
this.model = new app.TodoModel({
id : id
});
//モデルのサーバからのデータ取得完了時、描画を行う
this.listenTo(this.model, 'sync', this.render);
//モデル破棄(destroy)イベント発生時、Viewを削除
this.listenTo(this.model, 'destroy', this.remove);
//サーバからデータ取得
this.model.fetch({
wait : true
});
},
//描画
render : function() {
//テンプレートを使用し、モデルを描画する
this.$el.html(this.template(this.model.toJSON()));
return this;
},
//キャンセルボタンクリックのイベントハンドラ
onCancelClick : function() {
Backbone.history.navigate('#todo-lists', true);
},
});
})(app);
TODOの詳細画面用のビューです。
template
, events
については、これまでと同様の動きですので割愛します。
-
initialize
関数routerにより当ビュー生成時に
id
が渡されましたが、ここでそのid
のみを持つモデルを作成し、モデルのfetch
関数を実行することでサーバのAPIを実行し、最新の内容を取得しています。sync
イベント(サーバから最新情報取得後発火)にはrender
関数、
destroy
イベントにはビュー自身のremove
関数を指定しています。 -
render
関数initialize
関数で設定したとおり、実行したfetch
の完了による発火されるsync
イベントを契機として呼び出されます。
テンプレートを描画しています。 -
onCancelClick
関数キャンセルボタン押下時の実装です。
Backbone.history.navigate
関数により、'#todo-lists'
にURLを変更しています。
2番目の引数true
により、同時にそのURLへ遷移(=routerを動作させる)させています。
これでrouterが反応してTODO一覧画面に戻ります。
実装
では、実装しましょう!
-
app/View/Layouts/default.ctp
を上記の通り修正。 -
app/webroot/js/routers/router.js
を上記の通り修正。 -
app/webroot/js/views/todo-detail-view.js
を上記の通り作成。 - 動作確認!
- Gitにコミット
では、最後のLesson8へ!
Lesson 8 更新処理
詳細画面に更新ボタンを追加し、更新できるようにします。
Lesson完了後のアプリの動作
TODO一覧画面で、"TODO 3 件目"の詳細リンクをクリック後の画面です。
更新ボタンが追加されています。
内容を"3/27 19:00 第3回勉強会に参加"に変えて更新ボタンをクリックしてみます。
TODOの内容が更新され、TODO一覧画面に遷移後表示されました。
今回はこれで完成です!
編集するファイル一覧
編集 | file | 役割 |
---|---|---|
修正 | app/View/Layouts/default.ctp | HTMLテンプレート |
修正 | app/webroot/js/views/todo-detail-view.js | ビュー(Todo1件の詳細画面表示用) |
各ファイルの編集内容とポイント解説
default.ctp
<h2>Todo #<%- id %></h2>
<div>
<textarea style="width:300px;height:50px" id="edit-todo" autofocus placeholder="Todo?"><%- todo %></textarea>
+ <input type="button" id="updateTodo" value="更新"></input>
<input type="button" id="updateCancel" value="キャンセル"></input>
</div>
</script>
更新リンクの追加のみです。
todo-detail-view.js
//DOMイベントハンドラ設定
events : {
+ //更新ボタンクリック時
+ 'click #updateTodo' : 'onUpdateClick',
//キャンセルボタンクリック時
'click #updateCancel' : 'onCancelClick',
},
〜中略〜
render : function() {
//テンプレートを使用し、モデルを描画する
this.$el.html(this.template(this.model.toJSON()));
+ //入力欄への参照を取得しておく
+ this.$textBox = this.$('#edit-todo');
return this;
},
+ //更新ボタンクリックのイベントハンドラ
+ onUpdateClick : function() {
+ //テキストボックスから文字を取得
+ var todoString = this.$textBox.val();
+ this.model.save({
+ todo : todoString
+ }, {
+ wait : true,
+ silent : true,
+ success : function() {
+ Backbone.history.navigate('#todo-lists', true);
+ }
+ });
+ },
+
//キャンセルボタンクリックのイベントハンドラ
onCancelClick : function() {
Backbone.history.navigate('#todo-lists', true);
TODOの詳細画面用のビューです。
-
onUpdateClick
関数モデルを入力値で更新(
sava
関数)し、終了後キャンセルボタンと同じくBackbone.history.navigate
を使用してTODO一覧画面に戻っています。
このsave処理ですが、
this.model.save({
todo : todoString
}, {
wait : true,
silent : true,
});
Backbone.history.navigate('#todo-lists', true);
でも動きます。これまでの書き方はこうでしたね。
今回は下記のようにsuccess関数を指定し、その中で画面遷移するようにしています。
本来、サーバとのアクセス時には何らかのエラー処理が必要になります。
成功時にはsuccess関数、エラー時にはerror関数を指定しておけばそれが実行されるので、成功時の処理、エラー時の処理を整理して書くことが出来ます。
今回は例としてここだけsuccess関数を実装してみました。
this.model.save({
todo : todoString
}, {
wait : true,
silent : true,
success : function() { // ***** ココ *****
Backbone.history.navigate('#todo-lists', true);
}
});
},
実装
では、実装しましょう!
-
app/View/Layouts/default.ctp
を上記の通り修正。 -
app/webroot/js/views/todo-detail-view.js
を上記の通り修正。 - 動作確認!
- Gitにコミット
-
最後に、前回に倣って
git push
してGitHubで確認しておきましょう。
完成です!お疲れ様でした!
コメント/フィードバックお待ちしております。
参加者の方も、そうでない方もお気づきの点があればお願い致します。