Issue with will_paginate page links - javascript

I currently have a comment model that posts under a micropost and both are displayed on the same page. The issue is that both are displayed on the same page and both are paginated and I am trying to go for the facebook approach to microposting. Here is the issue below:
The links for both pagination turns into this href="/users/2?page=2" rather than href="/users/2/micropost?page=2" or href="/users/2/comment?page=2". I am unsure how to go about solving this problem. Here are some of my code. All suggestions are much appreciated!
Micropost Render HTML
<table class="microposts">
<% if microposts.any? %>
<%= render microposts %>
<%= will_paginate microposts, :page_links => false %>
<% else %>
<div class="EmptyContainer"><span class='Empty'>Add a thread!</span></div>
<% end %>
</table>
Comment Section HTML
<div id='CommentContainer-<%= micropost.id%>' class='CommentContainer Condensed2'>
<div class='Comment'>
<%= render :partial => "comments/form", :locals => { :micropost => micropost } %>
</div>
<div id='comments'>
<% comments = micropost.comments.paginate(:per_page => 5, :page => params[:page]) %>
<%= render comments %>
<%= will_paginate comments, :class =>"pagination" %>
</div>
</div>
User Controller for the Show Page
def show
#user = User.find(params[:id])
#comment = Comment.find(params[:id])
#micropost = Micropost.new
#comment = Comment.new
#comment = #micropost.comments.build(params[:comment])
#comments = #micropost.comments.paginate(:page => params[:page], :per_page => 5)
#microposts = #user.microposts.order('created_at DESC').paginate(:per_page => 10, :page => params[:page])
respond_to do |format|
format.html
format.js
end
end

Problem lies within will_paginate way of creating urls for each page (it doesn't have anything to do with jQuery).
By design, will_paginate try its best to guess what's the base url for the page user is on (internally it's using controller/action to do that). That base url is then combined with any extra params passed to will_paginate helper using :params and succesive page numbers.
For now (will_paginate 3.0.3), in order to overwrite this default behavior, you need to write your custom LinkRenderer class. Below there's example of such class - it makes use of new, extra option :base_link_url that can be passed to will_paginate view helper. Passed string is then used as a base when creating pagination links. If :base_link_url option is not passed, it will fallback to default behavior.
Put following class somewhere rails can find it on load (/lib for example, provided you've added /lib to your autoload paths in application.rb):
# custom_link_renderer.rb
class CustomLinkRenderer < WillPaginate::ActionView::LinkRenderer
def prepare(collection, options, template)
#base_link_url = options.delete :base_link_url
#base_link_url_has_qs = #base_link_url.index('?') != nil if #base_link_url
super
end
protected
def url(page)
if #base_link_url.blank?
super
else
#base_url_params ||= begin
merge_optional_params(default_url_params)
end
url_params = #base_url_params.dup
add_current_page_param(url_params, page)
query_s = []
url_params.each_pair {|key,val| query_s.push("#{key}=#{val}")}
if query_s.size > 0
#base_link_url+(#base_link_url_has_qs ? '&' : '?')+query_s.join('&')
else
#base_link_url
end
end
end
end
Usage:
# in your view
will_paginate collection, :renderer => CustomLinkRenderer, :base_link_url => '/anything/you/want'
And now back to your case. By this time you probably see the solution - you can have two will_paginate widgets on one page with different base urls by passing different :base_link_url options for those two.

Related

Update ajax content with will_paginate

How can I update my view keeping all existing ajax and will_paginate functionality in place?
I have a page rehome.html.erb
<div id="options">Option Select Here</>
<div class="all_animals">
<%= render #animals %>
</div>
<% unless #animals.current_page == #animals.total_pages %>
<div id="infinite-scrolling">
<%= will_paginate #animals %>
</div>
<% end %>
// WILL_PAGINATE
<script type="text/javascript">
$(function(){
if($('#infinite-scrolling').size() > 0) {
$(window).on('scroll', function(){
//Bail out right away if we're busy loading the next chunk
if(window.pagination_loading){
return;
}
more_url = $('.pagination a.next_page').attr('href');
if(more_url && $(window).scrollTop() > $(document).height() - $(window).height() - 50){
//Make a note that we're busy loading the next chunk.
window.pagination_loading = true;
$('.pagination').text('Loading.....');
$.getScript(more_url).always(function(){
window.pagination_loading = false;
});
}
});
}
});
</script>
This will load all the #animals collection, paginating it to 6 per page, and when I scroll down the page another 6 are loaded etc etc.
Corresponding controller
class PublicController < ApplicationController
before_filter :default_animal, only: [:rehome]
def rehome
respond_to do |format|
format.html
format.js
end
end
private
def default_animal
#animals = Animal.animals_rehome.paginate(:page => params[:page], :per_page => 6)
end
end
rehome.js.erb
$('.all_animals').append('<%= j render #animals %>');
<% if #animals.next_page %>
$('.pagination').replaceWith('<%= j will_paginate #animals %>');
<% else %>
$(window).off('scroll');
$('.pagination').remove();
<% end %>
So when an option is selected from the dropdown an ajax post is made to create a new query which will return a new collection of #animals
$.ajax({
type: 'POST',
url: '/public/rehomed',
data: data_send,
success: function(data) {
//console.log(data);
}
});
Controller
def rehomed
# conditions logic
#animals = Animal.joins(:user).where(conditions).paginate(:page => params[:page], :per_page => 6)
respond_to do |format|
format.js {render json: #animals }
end
end
What I want to do is have the new collection loaded (paginated to 6 per page again) and when I scroll down only show the objects belonging to the new collection of #animals (if there are any).
At the moment the pagination links are not updated as when I scroll down the page the original collection is loaded.
Edit
So I have created a rehomed.js.erb file which is pretty much the same as my rehome.js.erb:
$('.all_animals').empty();
$('.all_animals').append('<%= j render #animals %>');
<% if #animals.next_page %>
$('.pagination').replaceWith('<%= j will_paginate #animals %>');
<% else %>
$(window).off('scroll');
$('.pagination').remove();
<% end %>
and within my rehomed action
respond_to do |format|
format.js
end
So the new collection of animals is loaded, the pagination links are recreated but using the rehomed url, example being:
Before
<a class="next_page" href="/public/rehome?page=2" rel="next">Next →</a>
After
<a class="next_page" href="/public/rehomed?page=2" rel="next">Next →</a>
So when I scroll down I just get the following as the links don't exist and getScript fails
$('.pagination').text('Loading.....');
Edit 2
I have implemented #japed's answer but now after the new collection is rendered the pagination will continue to render the whole collection of the db including repeating the ones that where selected for the new collection, it's repeating itself.
How can I ensure that the correct url is generated for my links?
Will paginate allows you to set the params hash so you should be able to change it to use your other controller action like this:
will_paginate(#animals, :params => { :controller => "public", :action => "rehomed" })

Dynamically Change Div Content in Rails 4.1.4

I've spent almost my entire day trying to figure out how to solve this dilemma but unfortunately a majority of the solutions I've found are related to an outdated Rails version that still allowed "render" to be used in assets.
Here's what I'm trying to do:
My view finds each "Trip" entry and displays each as a thumbnail on the page. When the user clicks on each thumbnail, I would like the additional details (and also associations, each trip has a has_many: weeks) to be rendered in the Div below those thumbnails (replacing the previous content).
I can't seem to get Ajax to work and after several hours of attempting finally learned that "render" can't be used in assets. I would sincerely appreciate any help and along with potential solutions if someone could provide a possible reference guide for Ajax with Rails 4 that would be fantastic because I can't seem to find one.
Here's my code:
View - index.html.erb
<div>
<ul class="thumbnails col-md-offset-2">
<% Trip.find_each do |trip| %>
<li class="col-md-3" style="list-style-type:none;">
<%= link_to image_tag("http://placehold.it/350x350", :border => 0), :class => 'thumbnail', :url=>'/trips/selected_trip/params', :remote => true %>
<h3><%= trip.name %></h3>
</li>
<% end %>
</ul>
<div id="selected_trip">
</div>
</div>
Controller - trips.controller.rb
class TripsController < ApplicationController
before_filter :authenticate_user!
after_action :verify_authorized
def index
#trips = Trip.all
authorize Trip
end
def new
#trip = Trip.new
authorize Trip
end
def create
#trip = Trip.new(trip_params)
authorize Trip
if #trip.save
flash[:notice] = "New trip has been created."
redirect_to #trip
else
#Fill me in
end
end
def edit
#trip = Trip.find(params[:id])
authorize Trip
end
def update
#trip = Trip.find(params[:id])
authorize Trip
#trip.update(trip_params)
flash[:notice] = "Trip has been updated."
redirect_to #trip
end
def show
#trip = Trip.find(params[:id])
authorize Trip
end
def destroy
#trip = Trip.find(params[:id])
authorize Trip
#trip.destroy
flash[:notice] = "Trip has been deleted."
redirect_to trips_path
end
def selected_trip
#trip = Trip.find(params[:id])
#trip.name
respond_to do |format|
format.js
end
end
end
private
def trip_params
params.require(:trip).permit(:name, :description, :status)
end
end
Javascript - trips.js.erb (I know this method doesn't work anymore with render not being available in assets)
$('#selected_trip').html("<%= escape_javascript(render :partial => 'selected_trip', :content_type => 'text/html'%>")
Partial - _selected_trip.html.erb
<p>Success!</p> <!-- Just for testing, will replace with actual content -->
Thanks,
Nate
Edit 11:10PM (it works)-
I've changed my controller to:
def selected_trip
#trip = Trip.find(params[:id])
authorize Trip
render :partial => 'selected_trip', :content_type => 'text/html'
end
and my view to:
<div>
<ul class="thumbnails col-md-offset-2">
<% Trip.find_each do |trip| %>
<li class="col-md-3" style="list-style-type:none;" data-tripID="<%= trip.id %>">
<%= link_to image_tag("http://placehold.it/350x350", :border => 0), selected_trip_trip_path(trip), :class => 'thumbnail', :remote => true %>
<h3><%= trip.name %></h3>
</li>
<% end %>
</ul>
<div id="selected_trip">
</div>
</div>
</div>
<script>
$('a.thumbnail').on('ajax:success', function(evt, data) {
var target = $('#selected_trip');
$(target).html(data);
});
</script>
If you want to avoid rendering from the assets, you can try doing it this way.
I am assuming you know where you need to put the listener in order to catch the AJAX call, and also you can figure out, where you want to place the results when the AJAX comes back with a success status. Then, you want to do something like this:
$('whatever_container_they_can_click').on('ajax:success', function(evt, data) {
var target = $('#selected_trip'); // Find where is the destination
$(target).html(data);
});
Change your controller action to:
def selected_trip
#trip = Trip.find(params[:id])
render :partial => 'selected_trip', :content_type => 'text/html'
end

The AJAX request cannot see the effect without refresh the browser in Rails

I faced the same problem with this guy
I change rjs to js.erb just like him. And we all use <%= button_to 'Add to Cart',line_items_path(:product_id => product) ,:remote=>true %> to send an AJAX request to the controller. format.js to fine and execute create.js.erb. But the cart did not add anything.
log result :
Rendered line_items/_line_item.html.erb (4.3ms)
Rendered carts/_cart.html.erb (8.0ms)
Rendered line_items/create.js.erb (8.8ms)
That's the index.html.erb we send the AJAX request
<% if notice %>
<p id="notice"><%= notice %></p>
<% end %>
<h1>Your Pragmatic Catalog</h1>
<% #products.each do |product| %>
<div class="entry">
<%= link_to image_tag(product.image_url), line_items_path(:product_id => product), html_options = {:method => :post} %>
<h3><%= product.title %></h3>
<%=sanitize product.description %>
<div class="price_line">
<span class="price"><%= number_to_currency(product.price,:precision=>3) %></span>
<%= button_to 'Add to Cart',line_items_path(:product_id => product) ,:remote=>true %>
</div>
</div>
<% end %>
That's the line_items controller function to handle the request
# POST /line_items
# POST /line_items.json
def create
# for exercise only
session[:counter] = nil
#cart = current_cart
product = Product.find(params[:product_id])
#line_item = #cart.add_product(product.id)
respond_to do |format|
if #line_item.save
format.html { redirect_to store_index_path }
format.js
format.json { render json: #line_item, status: :created, location: #line_item }
else
format.html { render action: "new" }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
create.js.erb
$('#cart').html("<%= escape_javascript(render(#cart)) %>");
I fixed the problem.
Thanks to the great article which tell me the ability of firebug to see the source of response from AJAX request.
And JSLint helps me checkout the javascript syntax.
And finally I would like to thanks the Firebug which is such a great tool.
The problem is that the javascript is not being executed if there is any syntax error.
In my problem:
I should use single-quoted instead of double-qouted to wrap the render results. The render results comes out with many HTML with "", and "" which wrap them will cause syntax error in javascript. (double-qouted in double-qouated is not allowed)
So I simply change $('#cart').html("<%= escape_javascript(render(#cart)) %>"); to
$('#cart').html('<%= escape_javascript(render(#cart))%>');
I hope this answer will help the others who also suffer from this nightmare staff.
Help me increase the question rate if this is possible :)
Let's use j() helper method: $('#cart').html("<%=j render(#cart) %>");

Issue with Rendering Ajax/Rails

Situation: Currently have a comment model that paginates under a micropost. I am trying to make the next button render comments that belong to the micropost.
Issue: I am unsure how to go about making a route/action inorder to bring these comments through.
Codes: I have some code that I will provide below, if anything isn't right please assist.
All help is much Appreciated.
References: Issue with Ajax Appending
User Controller
def show
#user = User.find(params[:id])
#micropost = Micropost.new
#comment = Comment.new
#comment = #micropost.comments.build(params[:comment])
#microposts = #user.microposts.order('created_at DESC').paginate(:per_page => 10, :page => params[:page])
end
Pagination JS
$("#CommentPagin").on('click', 'a', function(e){
// Get data from server - make sure url has params for per_page and page.
$.get($(this).attr('href'), function(data){
// refresh client with data
$("#cc").append(data);
});
});
Comment Section
<div id='comments'>
<% comments = micropost.comments.paginate(:per_page => 5, :page => params[:page]) %>
<div id="CommentPagin">
<span class="CommentArrowIcon"></span>
<%= will_paginate comments, :page_links => false , :class =>"pagination" %>
</div>
<%= render 'users/comments' %>
</div>
Comment Rendering Section
<div id="cc">
<% comments = micropost.comments.paginate(:per_page => 5, :page => params[:page]) %>
<%= render comments %>
</div>
UPDATE
User Model
class User < ActiveRecord::Base
has_many :microposts
has_many :comments
end
Micropost Model
class Micropost < ActiveRecord::Base
belongs_to :user
has_many :comments
accepts_nested_attributes_for :comments
end
Comment Model
class Comment < ActiveRecord::Base
attr_accessible :content, :user_id, :micropost_id
belongs_to :micropost, :counter_cache => true
belongs_to :user
belongs_to :school
end
Routes.rb
kit::Application.routes.draw do
resources :pages
resources :application
resources :schools
resources :microposts
resources :comments
resources :users
resources :sessions
resources :password_resets
resources :relationships, only: [:create, :destroy]
resources :users do
member do
get :following, :followers
end
end
resources :microposts do
member do
post :vote_up, :unvote
end
end
resources :microposts do
member do
post :upview
end
end
resources :microposts do
resources :comments
end
resources :schools do
collection do
get :mostrecent
get :mostdiscussed
get :highestrated
get :viewcount
end
end
match "/users/:id/personalstream" => "users#personalstream"
match "/schools/:id/mostrecent" => "schools#mostrecent"
match "/schools/:id/mostdiscussed" => "schools#mostdiscussed"
match "/schools/:id/viewcount" => "schools#viewcount"
match "/schools/:id/highestrated" => "schools#highestrated"
match "/schools/:id", :to => 'schools#show', :as => "school"
match "/microposts/:id/comments" => "microposts#comments"
match "/microposts/:id/upview" => "microposts#upview"
match "/microposts/:id/vote_up" => "microposts#vote_up"
match "/microposts/:id/unvote" => "microposts#unvote"
get "log_out" => "sessions#destroy", :as => "log_out"
get "sign_in" => "sessions#new", :as => "sign_in"
get "sign_up" => "users#new", :as => "sign_up"
get "home" => "users#home", :as => "home"
root to: "schools#index"
end
add a new action to the microposts controller:
app/controllers/microposts_controller.rb
def comments
#micropost = Micropost.find(params[:id])
#comments = #micropost.comments
# we dont need all the html head stuff
render :layout => false
end
write a view (app/views/microposts/comments.html.erb) where you display all the #comments as you want
and add a new member to you microposts resource get :comments
now you can try in the browser /microposts/(add a micropost id here)/comments
this should deliver you all the comments for the user and format them as you wish.
the last part is the simplest: when the user clicks on the div, make a request to this site via ajax and attach the answer to the div where you want to display the comments. the code could look like this:
$("#CommentPagin").click( function(){
$("#CommentPagin").load( "<%= micropost_comments_path( #user ) %>" );
});
hope you got an idea. report back if its not working

Rails: best way to test an action called remotely?

I was trying out Rails again, this time the 3 version, but I got stuck while writing tests for an action that I only call remotely.
A concrete example:
Controller
class PeopleController < ApplicationController
def index
#person = Person.new
end
def create
#person = Person.new(params[:person])
#person.save
end
end
View (index.html.erb)
<div id="subscription">
<%= form_for(#person, :url => { :action => "create" }, :remote => true) do |f| %>
<%= f.text_field :email %>
<%= f.submit "Subscribe" %>
<% end %>
</div>
View (create.js.erb)
<% if #person.errors.full_messages.empty? %>
$("#subscription").prepend('<p class="notice confirmation">Thanks for your subscription =)</p>');
<% else %>
$("#subscription").prepend('<p class="notice error"><%= #person.errors.full_messages.last %></p>');
<% end %>
How can I test that remote form submission? I would just like to find out if the notice messages are being presented correctly. But if I try to do just
test "create adds a new person" do
assert_difference 'Person.count' do
post :create, :people => {:email => 'test#test.com'}
end
assert_response :success
end
It will say that the "create" action is missing a template.
How do you guys usually test remote calls?
Could you just use the 'xhr' function instead of the 'post' function? An example can be found at http://weblogs.java.net/blog/2008/01/04/testing-rails-applications, if you search for 'xhr'. But even then, I'm curious, even with a remote call, don't you need to return SOMETHING? Even just an OK header?

Categories