# paginate

## Purpose

Creates next/previous buttons and a breadcrumb trail to allow pagination of results

## Examples

Example domain class:

    class Book {
         String title
         String author
    }

Example controller:

    class BookController {
      def list = {
         [books: Book.list(params)]
      }
    }

Paginate code:

    <g:paginate controller="book" action="list" total="${Book.count()}" />

or

    <g:paginate next="Forward" prev="Back"
                maxsteps="0" controller="book"
                action="list" total="${Book.count()}" />

## Description

Attributes

- `total` (required) - The total number of results to paginate
- `action` (optional) - the name of the action to use in the link, if not specified the default action will be linked
- `controller` (optional) - the name of the controller to use in the link, if not specified the current controller will be linked
- `id` (optional) - The id to use in the link
- `params` (optional) - A map containing request parameters
- `prev` (optional) - The text to display for the previous link (defaults to "Previous" as defined by default.paginate.prev property in I18n messages.properties)
- `next` (optional) - The text to display for the next link (defaults to "Next" as defined by default.paginate.next property in I18n messages.properties)
- `max` (optional) - The number of records displayed per page (defaults to 10). Used ONLY if params.max is empty
- `maxsteps` (optional) - The number of steps displayed for pagination (defaults to 10). Used ONLY if params.maxsteps is empty
- `offset` (optional) - Used ONLY if params.offset is empty

## Source

Show Source

    def paginate = { attrs ->
    		def writer = out
            if(attrs.total == null)
                throwTagError("Tag [paginate] is missing required attribute [total]")		def messageSource = grailsAttributes.messageSource
    		def locale = RCU.getLocale(request)		def total = attrs.int('total') ?: 0
    		def action = (attrs.action ? attrs.action : (params.action ? params.action : "list"))
    		def offset = params.int('offset') ?: 0
    		def max = params.int('max')
    		def maxsteps = (attrs.int('maxsteps') ?: 10)		if(!offset) offset = (attrs.int('offset') ?: 0)
    		if(!max) max = (attrs.int('max') ?: 10)		def linkParams = [:]
    		if(attrs.params) linkParams.putAll(attrs.params)
            linkParams.offset = offset - max
            linkParams.max = max
    		if(params.sort) linkParams.sort = params.sort
    		if(params.order) linkParams.order = params.order		def linkTagAttrs = [action:action]
    		if(attrs.controller) {
    			linkTagAttrs.controller = attrs.controller
    		}
    		if(attrs.id!=null) {
    			linkTagAttrs.id = attrs.id
    		}
    		linkTagAttrs.params = linkParams		// determine paging variables
    		def steps = maxsteps > 0
    		int currentstep = (offset / max) + 1
    		int firststep = 1
    		int laststep = Math.round(Math.ceil(total / max))		// display previous link when not on firststep
    		if(currentstep > firststep) {
    			linkTagAttrs.class = 'prevLink'
                linkParams.offset = offset - max
                writer << link(linkTagAttrs.clone()) {
    				(attrs.prev ? attrs.prev : messageSource.getMessage('paginate.prev', null, messageSource.getMessage('default.paginate.prev', null, 'Previous', locale), locale))
    			 }
    		}		// display steps when steps are enabled and laststep is not firststep
    		if(steps && laststep > firststep) {
    			linkTagAttrs.class = 'step'			// determine begin and endstep paging variables
    			int beginstep = currentstep - Math.round(maxsteps / 2) + (maxsteps % 2)
    			int endstep = currentstep + Math.round(maxsteps / 2) - 1			if(beginstep < firststep) {
    				beginstep = firststep
    				endstep = maxsteps
    			}
    			if(endstep > laststep) {
    				beginstep = laststep - maxsteps + 1
    				if(beginstep < firststep) {
    					beginstep = firststep
    				}
    				endstep = laststep
    			}			// display firststep link when beginstep is not firststep
    			if(beginstep > firststep) {
    				linkParams.offset = 0
    				writer << link(linkTagAttrs.clone()) {firststep.toString()}
    				writer << '<span class="step">..</span>'
    			}			// display paginate steps
    			(beginstep..endstep).each { i ->
    				if(currentstep == i) {
    					writer << "<span class=\"currentStep\">${i}</span>"
    				}
    				else {
    					linkParams.offset = (i - 1) * max
    					writer << link(linkTagAttrs.clone()) {i.toString()}
    				}
    			}			// display laststep link when endstep is not laststep
    			if(endstep < laststep) {
    				writer << '<span class="step">..</span>'
    				linkParams.offset = (laststep -1) * max
    				writer << link(linkTagAttrs.clone()) { laststep.toString() }
    			}
    		}		// display next link when not on laststep
    		if(currentstep < laststep) {
    			linkTagAttrs.class = 'nextLink'
    			linkParams.offset = offset + max
    			writer << link(linkTagAttrs.clone()) {
    				(attrs.next ? attrs.next : messageSource.getMessage('paginate.next', null, messageSource.getMessage('default.paginate.next', null, 'Next', locale), locale))
    			}
    		}	}