ActiveModel Serializer::Pagination not working with ActionController::Parameters

1k Views Asked by At

Setup:

Rails 5, using ActiveModel::Serializer and Kaminari

Sample code:

def index
  @catalogs = Catalog.page(params[:page])

  render json: @catalogs, adapter: :json_api
end

Problem:

When params[:page] is nil, my result is as expected:

{
    "data": [
        {
            "id": "a020ab21-9028-4bfd-8f9c-1b735ed4734b",
            "type": "catalogs",
            "attributes": {
                "name": "First",
                "locale": "en"
            }
        }
    ],
    "links": {
        "self": "http://localhost:3000/v1/catalogs?page%5Bnumber%5D=1&page%5Bsize%5D=1",
        "next": "http://localhost:3000/v1/catalogs?page%5Bnumber%5D=2&page%5Bsize%5D=1",
        "last": "http://localhost:3000/v1/catalogs?page%5Bnumber%5D=3&page%5Bsize%5D=1"
    }
}

However, when I make a Postman call to the "next" URL (http://localhost:3000/v1/catalogs?page%5Bnumber%5D=2&page%5Bsize%5D=1),

I get:

Started GET "/v1/catalogs" for 172.18.0.1 at 2017-09-08 15:27:04 +0000
undefined method `to_i' for #<ActionController::Parameters:0x0000c68977f718>
Did you mean?  to_s
               to_h

Is there something different that has to be done with Rails 5 params to get pagination for ActiveModel::Serializers to work?

2

There are 2 best solutions below

0
On

It appears params[:page] doesn't hold the page number, but a "hash": { number: 1, size: 1 }. That said, you want to use the page number as the argument to page:

 def page_params
   params.fetch :page, {}
 end

 @catalogs = Catalog.page(page_params[:number])

Maybe even call .per(page_params[:size]) too, to let the API change that as well.

0
On

Solution:

I encountered nested pagination params problematic in some cases. You could use params page and per_page instead of page[number] and page[size]. Solution for will_paginate is in this comment on GitHub issue. Solution is probably also for kaminari, because its the issue of serialization gem, not pagination gem.

Explanation:

As Leonel explained, link that you are clicking:

localhost:3000/v1/catalogs?page%5Bnumber%5D=2

is the same as:

localhost:3000/v1/catalogs?page[number]=2

so your code should access these params like:

params[:page][:number]

not:

params[:page]

Solution from above, using ActiveModel::Serializers will create links like this:

localhost:3000/v1/catalogs?page=2&per_page=50

So you can access pagination params like you want in your controller:

Catalog.page(params[:page]) # page number
Catalog.per(params[:per_page]) # page size