目次

Version 8, last updated by lkuczera at Nov 02 04:28 UTC

Lift has nice pagination ready to use. Its built basically on Paginator trait, you can paginate on any type with it. Here we’ll examine PaginatorSnippet wich works on mapper classes and provides navigation and paging of results.

Imagine application that measures network speed to some ip addreses and stores results in database.
Mapper class for measurements has three fields

  • ip – ip address of measurement
  • download – measured download speed
  • upload – measured upload speed
class Measurement extends LongKeyedMapper[Measurement] with IdPK {
	def getSingleton = Measurement
	object ip extends MappedString(this,16)
	object upload extends MappedDouble(this)
	object download extends MappedDouble(this)
}

object Measurement extends Measurement with LongKeyedMetaMapper[Measurement] with CRUDify[Long,Measurement]

To ease working with this example CRUDify has been mixed into Measurement meta mapper.

Here is example PaginatorSnippet that works on Measurement mappers.

import net.liftweb._
import model.Measurement
import net.liftweb.http.PaginatorSnippet
import mapper._
import scala.xml.NodeSeq
import util.Helpers._

class ExamplePaginatorSnippet extends PaginatorSnippet[Measurement] {
	override def count = Measurement.count
	override def page = Measurement.findAll(StartAt(curPage*itemsPerPage), MaxRows(itemsPerPage))
		
	def renderPage(in: NodeSeq): NodeSeq = page.flatMap(item =>
		bind("item", in,"ip" -> item.ip,
				"download" -> item.download,
				"upload" -> item.upload))
		
}

Using Paginator you need to provide to properties:

  • count – number of all items you have (here all Measurements)
  • page – represents current slice

This line gets all Measurements that fits current page.

override def page = Measurement.findAll(StartAt(curPage*itemsPerPage), MaxRows(itemsPerPage))

Render page is used to bind results into view.

def renderPage(in: NodeSeq): NodeSeq = page.flatMap(item =>
		bind("item", in,"ip" -> item.ip,
				"download" -> item.download,
				"upload" -> item.upload))

PaginatorSnippet defines default paginate method that is used to generate navigation. Actual usage in template is below.


<lift:surround with="default" at="content">
	<table title="Measurements">
      		<tr><th>IP</th><th>Upload</th><th>Download</th></tr>
	<lift:ExamplePaginatorSnippet.renderPage>
		<tr><td><item:ip/></td><td><item:upload/></td><td><item:download/></td></tr>
	</lift:ExamplePaginatorSnippet.renderPage>
	</table>
	<lift:ExamplePaginatorSnippet.paginate>
		<nav:first/> | <nav:prev /> | <nav:allpages/>|  <nav:next/>  | <nav:last/> | <nav:records />
	</lift:ExamplePaginatorSnippet.paginate>
</lift:surround>

You can customize prefix and of course you can provide your own xml by overriding defaults.
Offset param is used for navigation purposes and is passed as HTTP parameter.
In case of name clash you can override offset parameter name: override def offsetParam = "offset"
To override default xml for navigation:

def prevXml: NodeSeq = Text(?("<"))
def nextXml: NodeSeq = Text(?(">"))
def firstXml: NodeSeq = Text(?("<<"))
def lastXml: NodeSeq = Text(?(">>"))
def currentXml: NodeSeq = Text("Displaying records "+(first+1)+"-"+(first+itemsPerPage min count)+" of "+count)

Also if you pass your own parameters they will be eaten to avoid that override page url.

override def pageUrl(offset: Long): String = appendParams(super.pageUrl(offset), List("your param" -> "value"))