読者です 読者をやめる 読者になる 読者になる

the industrial

都内で働くITエンジニアの日記

Playframework2.3.6/Scala2.11.4でページングのサンプル

初めに

この記事は、 Play framework Advent Calendar 2014の 2日目分の記事です。

1日目はkazuhiro haraさんのPlay Java開発、今ならこうやる話です。

3日目はgarbagetownさんのドキュメント翻訳の手順です。

初めまして、@omiendです。

普段は家で、Playframework/Scalaを利用した趣味プロをお酒を飲みながらやってます。

現在は特に、自分だけのオリジナルフェスティバルを表現できる、「マイタイテ!(マイタイムテーブル)」を育てています。
良かったら遊んで行ってください。

マイタイテ!

さて、僕からは、Playframeworkで行うページングのサンプルを作成してみたので、ご紹介させていただきます。

ソースはこちらに置いてます。

使っているもの

データ格納用のクラス/オブジェクトを定義

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)">&laquo;</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)">&raquo;</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勉強中ですので、もしご指摘の点等ございましたら、どしどしメッセージください。

ありがとうございました。