Riot.js 3.3 と Lumen 5.4 でつくる 初めてのSPA つなぎこみ編
はじめに
最近 Riot が気になっていたので、
Laravel製軽量フレームワーク Lumen 5.4 と Riot 3.3 で簡単なブログを作ってみることにした。
今回はいよいよフロントエンドとバックエンドをつなぎこむ。 フロントエンドの ajax まわりを実装していく。
他のエントリーはこちら。
スポンサーリンク
作るもの
いろいろ頑張って最終的にこんな感じのブログを作る。
- 記事一覧の表示(カテゴリーの絞り込みあり)
- 記事詳細の表示
- 記事の新規作成・更新
記事一覧
記事詳細
記事編集
記事保存
前提
- Lumen 5.4.5
- Riot.js 3.3.1
手順
- ナビゲーション画面(ajax でカテゴリー一覧を取得)
- 一覧画面(ajax で記事一覧を取得)
- 詳細画面(ajax で記事詳細を取得)
- 編集画面(ajax で新規作成・更新処理)
1. ナビゲーション
ナビゲーションに表示カテゴリーをマスターデータから取ってくる。
カスタムタグの <script>
部分を変更する。
navbar.tag
<script> // this.categories = [ // {id: 1, name: 'Tech'}, // {id: 2, name: 'Book'}, // {id: 3, name: 'Hobby'}, // {id: 4, name: 'Others'}, // ] // console.log(this.categories) // ajax 呼び出しの中では、`this` が Riot のタグインスタンスではなく、ajax のレスポンスオブジェクトを参照してしまうため、`this` を `self` に代入。 var self = this $.ajax({ type : 'GET', url : window.location.origin + '/api/v1/categories', data : {} }) .done(function(res){ self.categories = res // テンプレート変数と子要素を更新 self.update() }) .fail(function(){ // bootstrap modal を表示 $("#connection-error-dialog").modal(); // bootstrap modal が閉じた時のイベント $('#connection-error-dialog').on('hidden.bs.modal', function (e) { window.location.href = window.location.origin }) }) .always(function(){ console.log('GET categories') }) </script>
ポイント
var self = this
とすることで Riot のタグインスタンスを ajax 内でも使えるようにする。self.update()
で現在のタグインスタンス上のすべてのテンプレート変数を更新する。(参考)$("#connection-error-dialog").modal();
は Bootstrap の modal 関数$('#connection-error-dialog').on('hidden.bs.modal', function (e) {});
も同じく Bootstrap の modalイベント。
2. 一覧画面
list.tag
<script> // this.list = [ // { // id: 1, // title: 'Hello, world!', // text: 'Some quick example text to build on the card title and make up the bulk of the cards content.', // categories: ['Tech', 'Book'], // }, // { // id: 2, // title: 'Hello, world!', // text: 'Some quick example text to build on the card title and make up the bulk of the cards content.', // categories: ['Book', 'Hobby', 'Others'] // }, // { // id: 3, // title: 'Hello, world!', // text: 'Some quick example text to build on the card title and make up the bulk of the cards content.', // categories: ['Hobby', 'Others'] // }, // { // id: 4, // title: 'Hello, world!', // text: 'Some quick example text to build on the card title and make up the bulk of the cards content.', // categories: ['Others'] // }, // ]; // console.log(this.list) var self = this // opts 変数の中にルータから渡されたオブジェクトが格納されている console.log(opts) var params = {} if (opts.categoryName) params.category = opts.categoryName $.ajax({ type : 'GET', url : window.location.origin + '/api/v1/posts', data : params }) .done(function(res){ self.list = res for (var i=0; i<self.list.length; i++) { // タイトルが60文字以上だった場合省略 if (self.list[i].title.length >= 60) { self.list[i].title = self.list[i].title.slice(0, 60) + '...' } // テキストが100文字以上だった場合省略 if (self.list[i].text.length >= 100) { self.list[i].text = self.list[i].text.slice(0, 100) + '...' } self.list[i].title = self.list[i].title.replace(/(\r\n)|\n|\r/g, "<br />") self.list[i].text = self.list[i].text.replace(/(\r\n)|\n|\r/g, "<br />") } self.update() // ajax 終了前にタグがマウントされてしまうため、 // タグを再マウントして描画しなおす。 riot.mount('raw') }) .fail(function(){ $("#connection-error-dialog").modal(); $('#connection-error-dialog').on('hidden.bs.modal', function (e) { window.location.href = window.location.origin }) }) .always(function(){ console.log('GET posts.') }); </script>
ポイント
- ルータから渡された変数は
opts
でアクセスできる。ここでは、カテゴリーでソートされたときのカテゴリー名を取得する。 - テンプレート内にある カスタムタグ
<raw>
は、ajax 通信が終わる前にマウントされてしまうため、ajax 通信が終わったあとにriot.mount('raw')
で再マウントすることで描画し直している。
3. 詳細画面
ここは一覧画面とほぼ同じ。
post.tag
<script> // this.id = 1 // this.title = 'Hello, world!' // this.text = 'Some quick example text to build<br/> on the card title and make up the bulk of the cards content.' // this.categories = ['Tech', 'Book'] var self = this console.log(opts) var id = opts.id $.ajax({ type : 'GET', url : window.location.origin + '/api/v1/posts/' + id, data : {} }) .done(function(res){ self.id = res.id; self.title = res.title.replace(/(\r\n)|\n|\r/g, "<br />") self.text = res.text.replace(/(\r\n)|\n|\r/g, "<br />") self.categories = res.categories self.update() riot.mount('raw') }) .fail(function(){ $("#connection-error-dialog").modal(); $('#connection-error-dialog').on('hidden.bs.modal', function (e) { window.location.href = window.location.origin }) }) .always(function(){ console.log('GET post.') }); </script>
4. 編集画面
edit.tag
<script> // this.categories = [ // {id: 1, name: 'Tech'}, // {id: 2, name: 'Book'}, // {id: 3, name: 'Hobby'}, // {id: 4, name: 'Others'}, // ] // console.log(this.categories) var self = this var id = opts.id // 'new' か 記事ID self.post = {} // category の取得 $.ajax({ type : 'GET', url : window.location.origin + '/api/v1/categories', data : {} }) .done(function(res){ self.categories = res self.update() }) .fail(function(){ $("#connection-error-dialog").modal(); $('#connection-error-dialog').on('hidden.bs.modal', function (e) { window.location.href = window.location.origin }) }) .always(function(){ console.log('GET categories.') }) // 更新の場合、デフォルト値を設定 if (id !== 'new') { $.ajax({ type : 'GET', url : window.location.origin + '/api/v1/posts/' + id, data : {} }) .done(function(res){ self.post = res self.update() }) .fail(function(){ alert('Connection error.'); }) .always(function(){ console.log('GET post.') }) } cancel() { history.back() } submit(e) { e.preventDefault() var form = $('#edit-form') var type = 'POST'; var url = window.location.origin + '/api/v1/posts' var data = form.serialize() // 更新の場合はPUT if (id !== 'new') { type = 'PUT'; url = window.location.origin + '/api/v1/posts/' + id data = form.serialize() + '&id=' + id } $.ajax({ type : type, url : url, data : data }) .done(function(res){ console.log('Submitted.') $("#saved-dialog").modal(); $('#saved-dialog').on('hidden.bs.modal', function (e) { window.location.href = window.location.origin }) }) .fail(function(){ $("#connection-error-dialog").modal(); $('#connection-error-dialog').on('hidden.bs.modal', function (e) { window.location.href = window.location.origin }) }) .always(function(){ console.log('GET categories.') }) } </script>
ポイント
- 編集画面は新規作成と更新があるため、更新の場合は既存データを取得して表示させる
まとめ
Riot と Lumen でブログを作った。
Riot は本当に簡単で、やりたいことは大体ドキュメントを見ればすぐわかる。
書き方もシンプルだし、今後 SPA を作るときは Riot が最も有力な候補となりそう。
関連記事
スポンサーリンク