Reputation: 45
Would like to keep my views DRY!
Problem description:
I have successfully implemented AJAX pagination, however I have to use static paths in the example below - using root_path for the "load more products" link. This is fine when user is visiting root URL, but obviously breaks when visiting a /s/[storename] or /c/[collectionname]
I can't quite conceptually wrap my head around what I need to do to refactor this? I'd like to preserve the /t /s /c routing if possible.
Any thoughts?
routes
root_path GET / products#tile
tag_view_path GET /t/:tag(.:format) products#tile
store_view_path GET /s/:store_slug(.:format) products#tile
collection_view_path GET /c/:collection_slug(.:format) products#tile
products_controller
def tile
# /t/:tag Show all by tag
# /s/:store_slug Show all by store
# /c/:collection_slug Show all by collection
# Nothing passed Show all by rank
if params[:tag]
# find by tag
# to be implemented
elsif params[:store_slug]
store = Store.friendly.find(params[:store_slug])
@products = store.products.paginate(:page => params[:page]).order('rank DESC')
respond_to do |format|
format.html
format.js
end
elsif params[:collection_slug]
collection = Collection.friendly.find(params[:collection_slug])
@products = collection.products.paginate(:page => params[:page]).order('rank DESC')
respond_to do |format|
format.html
format.js
end
else
@products = Product.paginate(:page => params[:page]).order('rank DESC')
respond_to do |format|
format.html
format.js
end
end
end
tile.html.erb (next page link)
<div><%= link_to 'Load More Products', root_path(:page => @products.next_page), :class => 'load-more-products', :remote => true if @products.next_page %></div>
tile.js.erb (JS to append ajax response)
$('.column-holder').append('<%= escape_javascript(render partial: "product", collection: @products) %>');
$('a.load-more-products').attr('href', '<%= root_path page: @products.next_page %>');
routes as requested
# product short URL /p/slug-name-for-title
match 'p/:product_slug', to: 'products#show', via: 'get', as: 'product_view'
# show products by tag /t/tagname
match 't/:tag', to: 'products#tile', via: 'get', as: 'tag_view'
# show products by store /s/store-slug-name
match 's/:store_slug', to: 'products#tile', via: 'get', as: 'store_view'
# show products by collection /c/collection-slug-name
match 'c/:collection_slug', to: 'products#tile', via: 'get', as: 'collection_view'
Upvotes: 1
Views: 55
Reputation: 7466
You need to pass "where" you are to your view so you paginate the right thing.
I would look for a way to make this 3 or 4 different methods as well. Short term you can DRY it up a little like this.
# NOTE you may need to do something like
# helper ProductHelper # to get the method from the "other file" below
def tile
# /t/:tag Show all by tag
# /s/:store_slug Show all by store
# /c/:collection_slug Show all by collection
# Nothing passed Show all by rank
if params[:tag]
# find by tag
# to be implemented
elsif params[:store_slug]
store = Store.friendly.find(params[:store_slug])
@products = store.products.paginate(:page => params[:page]).order('rank DESC')
elsif params[:collection_slug]
collection = Collection.friendly.find(params[:collection_slug])
@products = collection.products.paginate(:page => params[:page]).order('rank DESC')
else
@products = Product.paginate(:page => params[:page]).order('rank DESC')
end
respond_to do |format|
format.html
format.js
end
end
Really the above is, #products_by_tag, #products_by_store, #products_by_collection, and #index for all. That is another matter though.
class ProductHelper
def url_for_tile_type(options = {})
if params[:tag]
tag_view_path(options)
elsif params[:store_slug]
store_view_path(options)
elsif params[:collection_slug]
collection_view_path(options)
else
product_path(options)
end
end
end
Then your js view becomes,
$('.column-holder').append('<%= escape_javascript(render partial: "product", collection: @products) %>');
$('a.load-more-products').attr('href', '<%= url_for_tile_type page: @products.next_page %>');
Here we have implemented something that functions somewhat like polymorphic_url_for
and sends us to the "right" next page.
In your routes.rb
match
is deprecated in rails 4 I think. If you are using rails 3+ you can do.
# product short URL /p/slug-name-for-title
get 'p/:product_slug', to: 'products#show', as: 'product_view'
# show products by tag /t/tagname
get 't/:tag', to: 'products#tile', as: 'tag_view'
# show products by store /s/store-slug-name
get 's/:store_slug', to: 'products#tile', as: 'store_view'
# show products by collection /c/collection-slug-name
get 'c/:collection_slug', to: 'products#tile', as: 'collection_view'
Upvotes: 1