はじめに
vaporフレームワークを使ったWebアプリケーションに挑戦して、静的ページを表示することができました。
静的ページとくれば今度は動的に生成するページを試してみたくなるわけですよ。
というわけで、MongoDBのドキュメントをリスト形式で表示してみます。
ドキュメントを取得する
ドキュメントの取得は既存のものを使います。
MongoDBのドキュメント全部を取得するWebAPIです。
router.get("api/books") { req -> [Book] in
let client = try! req.make(MongoClient.self)
let collection = client.db("vaporapp").collection("books", withType: Book.self)
var books = [Book]()
let booksDocs = try! collection.find()
for book in booksDocs {
books.append(book)
}
return books
}
ドキュメント一式を配列で返すものですが、Leafテンプレートに表示させて返すように作り変えていきます。
LeafテンプレートをレンダリングしたViewを返す
Leaf RendererでレンダリングしたViewを返すようにします。
router.get("api/books") { req -> Future<View> in
let client = try! req.make(MongoClient.self)
let collection = client.db("vaporapp").collection("books", withType: Book.self)
var books = [Book]()
let booksDocs = try! collection.find()
for book in booksDocs {
books.append(book)
}
return try req.view().render("BookList", ["books":books])
}
本の情報をまとめた構造体Book
の配列[Book]
を返していましたが、Viewを返すようFuture<View>
にしました。
また、静的ページのときに倣って、try req.view().render("BookList", ["books":books])
にしています。Leafテンプレートファイルの名前はBookList.leaf
にしました。安直。
Leafテンプレートに表示したい情報は、引数の2つ目にDictionary型で渡します。キーはLeafテンプレートで参照するキーワードになります。今の場合は、Leafテンプレート内でbooks
と指定すると、渡したDictionaryから値を探して表示してくれます。ざっくりとしたイメージはこんな感じです。
参考:Book構造体
struct Book: Content {
var title: String
var publisher: String
var price: Int
}
Leafテンプレートを作る
MongoDBから取得した全ドキュメントについて、タイトル、出版社、価格をテーブルに表示してみようと思います。
LeafテンプレートへDictionaryで渡した値を参照するためには#([Dictionaryのキー])
を使います。先ほど["books":books]
と渡したので、#(books)
ですね。ただし、渡されるデータの中身は文字列でも数値でもなく、構造体Bookの配列なのでそのままでは何も表示されません。配列を一つずつ処理していく必要があります。
公式サイトでもSwift-inspired syntax
と謳われています。安心してください。ループ処理が用意されています。ループ処理は馴染みのあるforを使い、#for(book in books)
という形で実現できます。
これで渡された配列から本のデータを一つずつ取り出して処理することができます。取り出したデータの中にタイトルや出版社名、価格が入っています。それらのデータどうやって取り出すかですが、#(book.[プロパティ名])
で取り出すことができます。これも馴染みのある書き方で、妙な安心感があります。
ここまでくれば各値を表示していくだけです。最終的にテーブルにまとめてみました。
データを繰り返して処理するのと同時に、HTMLの繰り返しもまとめられます。
<!DOCTYPE html>
<html>
<head>
<title>Book List</title>
</head>
<body>
<h1>Book List</h1>
<table>
#for(book in books) {
<tr>
<td>#(book.title)</td>
<td>#(book.publisher)</td>
<td>#(book.price)</td>
</tr>
}
</table>
</body>
</html>
動かしてみよう
CSS周りをいじっていないスッピン状態なので大変素っ気ないですが、リスト表示ができていることが分かります。
ちょっと意地悪してみた
return try req.view().render("BookList", userInfo: ["books":books,"book":Book(title: "どう表示されるかな?", publisher: "意地悪出版", price: 7000)])
データを参照するキーの値book
を衝突させてみました。どう表示されるか?
名前衝突で処理できず、途中で止まるだろうなと予想。
想定通りの動きでした。
名前衝突しないように気をつけるかルールづくりが必要ですね。
まとめ
リスト表示が無事できるようになりました。Leafテンプレートには#for
のループ以外にももちろん条件分岐の#if
もありますし、演算もできます。渡されるデータが何かということさえはっきりさせておけば、フロントエンドとバックエンドの役割分担がやりやすそうですね。
ところで、Leafテンプレートのレンダリングデバッグはどこで見られるんだろうか?Chromeのデベロッパツール?IDEに頼ってみる必要があるかもしれないです。