Adding pagination to Revel app

Very often, we need pagination support on our data, to make it convenient for user to browse through it. Unfortunately, there is no in-built support for pagination in revel, but its not hard to build one of your own.

For pagination to work, we need 3 things

  1. Total number of data elements (totalCount)
  2. Number of elements to be shown at once (limit)
  3. The start point for each data set (offset)

Once we have this information, building pagination is really simple.

Lets take a concrete example, consider we have a list of strings which we fetch from a database, 50 in total and we want to show 10 at a time.

Here is how the code looks like

func (c App) Welcome(offset int) revel.Result {
	var posts = []models.Post{}
	var TotalCount int
	limit := 10
	currentBtn := offset
	offset = offset*limit
	c.Txn.Find(&posts).Count(&TotalCount)
	c.Txn.Limit(limit).Offset(offset).Find(&posts)
	return c.Render(posts, offset, TotalCount, limit)
}

Above we are using Gorm to query our database.
We are passing offset, TotalCount and limit to our template and that should be it.

But to render page numbers, we need to get number of pages and then loop over them, doing that in template gets complicated.

A simpler solution is to change the action and avoid complicated logic in template,

func (c App) Welcome(offset int) revel.Result {
	var posts = []models.Post{}
	var TotalCount int
	limit := 10
	currentBtn := offset
	offset = offset*limit
	c.Txn.Find(&posts).Count(&TotalCount)
	c.Txn.Limit(limit).Offset(offset).Find(&posts)
        numberOfBtns := getNumberOfButtonsForPagination(TotalCount, limit)
        sliceBtns := createSliceForBtns(numberOfBtns)
        return c.Render(posts, sliceBtns, currentBtn)
}

func getNumberOfButtonsForPagination(TotalCount int, limit int) int {
	num := (int)(TotalCount / limit)
	if (TotalCount%limit > 0) {
		num++
	}
	return num
}

func createSliceForBtns(number int) []int {
	var sliceOfBtn []int
	for i := 0; i < number; i++ {
		sliceOfBtn = append(sliceOfBtn, i+1)
	}
	return sliceOfBtn
}

Here we simplified the logic for our template and now we just need to iterate over the “sliceBtns”. Here is how the template looks like

<div>
    <ul class="pagination">
        {{ range $index, $btn := .sliceBtns }}
        {{if eq $index $.currentBtn}}
        <li class="active">
           {{else}}
        <li>
        {{end}}
            <a href="{{url "App.Welcome" $index}}">{{$btn}}</a>
        </li>
        {{end}}
    </ul>
</div>

Thats it. We are done!

Any better ways? suggestions? Let me know.

~~ Whizdumb ~~
Email : sachin.xpert@gmail.com