Using the will_paginate gem to generate a React renderable HTML string
Generally when I post about programming it’s because I’ve finally found a solution to something that has been driving me crazy, some problem with a lot of documentation to read through and not a lot of Stack Overflow answers (and if they do exists, the use case is always a little off). This is no exception!
First, why would you want to combine React and Ruby gems? In my case, I thought it was the simple, lazy path. Way back when, the site I work for was built using will_paginate for all pagination. I recently ran into a case where I needed to include that pagination in a React app. It wasn’t for anything fancy, the links and logic needed to match the rest of site. So, trying to re-create the gem magic in React seemed like overkill compared to creating a partial with the code, generating an HTML string using the Rails render_to_string
method, and sending it to React to dangerouslySetInnerHTML
.
Well, it turns out will_paginate
uses the url_for
method and it was not happy to be called without the request data (undefined method `host’ for nil:NilClass
?!) it uses to build urls. After a lot of hunting around, I came across this pull request to get will_paginate working with mountable engines that seemed adaptable. My helper accepts the request from the controller and pulls out the necessary fields to send with it’s own instance of url_for
.
module WillPaginateHelper
class StringLinkRenderer < WillPaginate::ActionView::LinkRenderer
protected
def url(page)
@base_url_params ||= begin
url_params = merge_get_params(default_url_params)
url_params[:only_path] = true
merge_optional_params(url_params)
end
url_params = @base_url_params.dup
add_current_page_param(url_params, page)
if @options[:url_builder]
@options[:url_builder].call(url_params)
else
@template.url_for(url_params)
end
end
end
def will_paginate_string(collection = nil, options = {}, request = nil)
params = {
:host => request[:host],
:controller => request[:controller],
:action => request[:action]
}
unless request.query_parameters.empty?
request.query_parameters.each do |k, v|
params[k] = v
end
end
options.merge!(
:renderer => WillPaginateHelper::StringLinkRenderer,
:url_builder => ->(params) { Rails.application.routes.url_for(params) },
:params => params
)
will_paginate(collection, options)
end
end
Then, in the partial that is being rendered to a string, it’s possible to call will_paginate_string(collection, options, request)
and the results can be sent as a JSON response to render in React.
Admittedly, Rails isn’t my strong suit so I’d be interested to hear if I missed some easier way to do this.