初めに
この記事は、 Play framework Advent Calendar 2014の 2日目分の記事です。
1日目はkazuhiro haraさんのPlay Java開発、今ならこうやる話です。
3日目はgarbagetownさんのドキュメント翻訳の手順です。
初めまして、@omiendです。
普段は家で、Playframework/Scalaを利用した趣味プロをお酒を飲みながらやってます。
現在は特に、自分だけのオリジナルフェスティバルを表現できる、「マイタイテ!(マイタイムテーブル)」を育てています。
良かったら遊んで行ってください。
さて、僕からは、Playframeworkで行うページングのサンプルを作成してみたので、ご紹介させていただきます。
ソースはこちらに置いてます。
使っているもの
- Playframework2.3.4
- Scala 2.11.4
- sbt 0.13.7
- Slick 2.1.0
- play-slick 0.8.0
- H2DB(Playに標準でくっついているもの)
データ格納用のクラス/オブジェクトを定義
models/PageNation.scala
package models /** PageNation Class */ case class PageNation[T](currentPageNum: Int // 現在のページ番号 ,totalDataCount: Int // 全体件数 ,dataList : List[T] // 一覧するデータリスト ) { /** 最大ページ番号を返却 */ def totalPageNum: Int = { if (totalDataCount % PageNation.maxPageNum > 0) { // 端数が存在する場合1ページ追加 totalDataCount / PageNation.maxPageNum + 1 } else { totalDataCount / PageNation.maxPageNum } } /** ページ番号リストの作成起点となる番号を返却 */ def startIndex: Int = { var ret: Int = 1 // 現在のページ番号 if (currentPageNum + PageNation.bfNum + 1 > totalPageNum) { ret = totalPageNum - (PageNation.bfNum * 2) } else { ret = currentPageNum - PageNation.bfNum } // 0以下だったら1を返却 if (ret <= 0) { ret = 1 } ret } /** ページ番号のリストを返却 */ def pageNumList: List[Int] = { // 返却用 var pageNumList: List[Int] = List.empty // ページ番号リストを作成 for (i: Int <- startIndex to totalPageNum if pageNumList.size < (PageNation.bfNum * 2 + 1) && pageNumList.size < totalPageNum) { if (i >= 1 && totalPageNum >= i) { pageNumList = i :: pageNumList } i + 1 } pageNumList.reverse } } /** PageNation Object */ object PageNation { val maxPageNum: Int = 5 // 1ページに表時するデータの件数 val bfNum : Int = 5 // 現在ページの両脇ページ番号を表示する件数 }
PageNationクラス/オブジェクトでは、ページング処理に使用するデータをひとまとめにしてます。
引数はそれぞれ
- currentPageNum:Int → 現在のページ番号を格納
- totalDataCount: Int → データの総件数(models/Data.countで取得)を格納
- dataList:List[T] → 一覧するデータを格納
となっています。
ページ定義
views/pageNationscala.html
@(pageNation: PageNation[_], pageName: String) <nav> <ul class="pagination"> <li class="@if(pageNation.currentPageNum==1){active}"><a href="@routes.Application.pageNation(1, pageName)">«</a></li> @for(page <- pageNation.pageNumList){<li class="@if(pageNation.currentPageNum==page){active}"><a href="@routes.Application.pageNation(page, pageName)">@page</a></li>} <li class="@if(pageNation.currentPageNum==pageNation.totalPageNum){active}"><a href="@routes.Application.pageNation(pageNation.totalPageNum, pageName)">»</a></li> </ul> </nav>
pageNationscala.htmlでは、ページングリンクのhtmlレンダリングされる元を定義しております。
(htmlレンダリングって表現合っているのかな)
使い方
controllers/Application.scala
def index(currentPageNum: Int) = DBAction { implicit request => Ok(html.index(PageNation[Data]( currentPageNum ,Data.count ,Data.findOffset(currentPageNum * PageNation.maxPageNum - PageNation.maxPageNum, PageNation.maxPageNum) ) ,form) ) } ~割愛~ /** Page遷移先を指定 */ def pageNation(currentPageNum: Int, pageName: String) = Action { implicit request => pageName match { case "index" => Redirect(routes.Application.index(currentPageNum)) // 新たに別のページでPageNationクラスを利用する場合、ここに遷移先を追記する // 例) case "secondPage" => Redirect(routes.Application.secondPage(currentPageNum)) case _ => Redirect(routes.Application.index(currentPageNum)) } }
Controllerにて、models/Dataでデータを取得(Slickを利用)し、PageNationクラスのインスタンスに格納した後、Viewに渡しています。
Viewからは遷移するページを指定して、Controller.pageNationメソッドを呼ぶようにし、ここからページ遷移先を変更しています。
views/index.scala.html
@(page: PageNation[Data], form: Form[Int])
~割愛~
@pageNation(page, "index")
~割愛~
indexscala.htmlからはPageNationのオブジェクトを引数として、先ほどのpageNation.scala.htmlを呼び出します。
ルーティング例
Routes
GET / controllers.Application.index(p: Int ?= 1) GET /pageNation controllers.Application.pageNation(p: Int ?= 1, page: String)
routesの定義はこんな感じです。
最後に
おそらく、お世辞にもモダンな作りとは言えないものだとは思いますが、基本的なページング処理の実装ということでご紹介させていただきました。
それから、僕自身まだまだScala勉強中ですので、もしご指摘の点等ございましたら、どしどしメッセージください。
ありがとうございました。