will_paginate with endless Scrolling | Rails4 - javascript

THIS IS THE SOLUTION
So i'm using will_paginate / Bootstrap Will Paginate with Endless Scrolling.
To get the Pagination working:
1.) In my Controller i updated my index action with
#clips = Clip.order("created_at desc").page(params[:page]).per_page(20)
2.) Edit my index view:
<%= will_paginate #clips%>
DONE
Pagination works just fine.
To Add Endless scrolling i did the same steps as in my previous Rails 3 App.
1.) Edit my clips.js.coffee
jQuery ->
$('#clips-masonry').imagesLoaded ->
$('#clips-masonry').masonry itemSelector: ".clips-masonry" # Thats my Masonry
if $('.pagination').length # Thats for the Endless Scrolling
$(window).scroll ->
url = $('.pagination .next_page a').attr('href')
if url && $(window).scrollTop() > $(document).height() - $(window).height() - 50
# What to do at the bottom of the page
$('.pagination').text("Fetching more Clips...")
$.getScript(url)
$(window).scroll()
2.) Create an index.js.erb with:
$boxes = $('<%= j render(#clips) %>')
$('#clips-masonry').append( $boxes ).imagesLoaded( function(){
$('#clips-masonry').masonry( 'reload');
});
<% if #clips.next_page %>
$('.pagination').replaceWith('<%= j will_paginate(#clips) %>');
<% else %>
$('.pagination').remove();
<% end %>
3.) Added format.js to my Controller index action
def index
#clips = Clip.order("created_at desc").page(params[:page]).per_page(12)
respond_to do |format|
format.html
format.js
end
end
4.) My _clip.html.erb is wrapped with the div
<div class="clip-box clips-masonry" data-no-turbolink>

Ok, i got it working with my Updated Question, everyone who stumbles upon this problem, This is the solution.

Incase someone prefers to use JS/JQUERY:
$(window).scroll(function() {
var url;
// Checks if products are currently being loaded
if (window.pagination_loading) {
return;
}
// Grabs the URL from the "next page" button
url = $('.pagination .next_page').attr('href')
// Chckes if you're n height from the bottom
if (url && $(window).scrollTop() > $(document).height() - $(window).height() - 50) {
// Variable to know that you are currently loading products
window.pagination_loading = true;
// Text while loading
$('.pagination').text('');
// Run the script
return $.getScript(url).always(function() {
return window.pagination_loading = false;
});
}
});
Index.js.erb:
$products = $('<%= j render(#products) %>')
$('#productsContainer').append( $products );
<% if #products.next_page %>
$('.pagination').replaceWith('<%= j will_paginate(#products) %>');
<% else %>
$('.pagination').remove();
<% end %>
index.html.erb
<div class="container">
<%= will_paginate #products %>
</div>
Controller:
respond_to do |format|
format.html
format.js
end

Related

Will_paginate, render next page

I'am using will_paginate gem to paginate my posts. And I want to do infinite scroll. I watch few video and read couple of posts, but thats not really what I need (because of my app structure). You see, home.html.erb (to be short), looks like:
<div class='news-lent'>
<%= render 'posts/posts_for_all', posts_variable: #posts %>
</div>
And my _posts_for_all.html.erb (to be short), look like (to be short):
<% posts_variable.each do |post| %>
<%= link_to post.theme, post %>
<% end %>
<% will_paginate posts_variable, :next_label => "More" %> # => i need it every
time the page is loaded
I've already wrote JS for all, except one line:
$('.news-lent').append('<%= j render *some_thing* %>')
Well, I don't know what pass to render. I cannot pass my #posts(it will return error(missing partial posts/_post)), and #posts.next_page(it will return 2), and #{posts_path(#post)}?page=#{#post.current_page+1}(but this don't work too) and other problems.
Can you help me, I need something to put inside j render so it rendered next page?
UPDATE
I reworked everything with RailCast videos, and same way like #Szilard Magyar tell me to do, but now it keep rendering the same (first page) all time and it not rendering second page at all, what can create such problem?
UPDATE
Follow railcast video (reworked my app structure), and a new problem appears:
My home.js.coffee:
jQuery ->
url = $('.pagination .next_page a').attr('href')
$(window).scroll ->
if url && $(window).scrollTop() > $(document).height() - $(window).height() - 50
$('.pagination').text("Fetching...")
$.getScript(url)
My index.js.erb:
$('.one').append('<%= j render #posts, posts_variable: #posts %>');
<% if #posts.next_page %>
$('.pagination').replaceWith('<%= j will_paginate(#posts) %>');
<% else %>
$('.pagination').remove();
<% end %>
<% sleep 3 %>
My index.html.erb:
<div class="news-lent">
<div class="one">
<%= render #posts, posts_variable: #posts %>
</div>
<div id="infinite-product-scrolling">
<%= will_paginate #posts, :next_label => " Еще" %>
</div>
</div>
But now, when I'am on first page and scroll down it rendering second page, but 2x times, and when I again scrolling to bottom and it rendering second page (it not rendering third page). I don't know how to fix it, can you land me again?
index.html.erb
<div class="product-index-list new-product-insert">
<%= render #products %>
</div>
<div id="infinite-product-scrolling">
<%= will_paginate #products %>
</div>
<div class="no-more">
<p>No more products.</p>
</div>
_product.html.erb
<div class="name"><%= product.name %></div>
<div class="oneliner"><%= product.oneliner %></div>
products.js
if ($('#infinite-product-scrolling').size() > 0) {
$(window).on('scroll', function() {
$('#infinite-product-scrolling').hide();
var more_products_url;
more_products_url = $('#infinite-product-scrolling .pagination .next_page a').attr('href');
if (more_products_url && $(window).scrollTop() > $(document).height() - $(window).height() - 60) {
$('.pagination').html("<i class='fa fa-circle-o-notch fa-2x fa-spin'></i>");
$('#infinite-product-scrolling').show();
$.getScript(more_products_url);
}
});
};
index.js.erb
$('.new-product-insert').append('<%= j render #products %>');
<% if #products.next_page %>
$('.pagination').replaceWith('<%= j will_paginate #products %>');
<% else %>
$(window).off('scroll');
$('.pagination').remove();
$('.no-more').delay(1000).fadeIn(1500);
<% end %>
Solve it, the problem appears because of my mistake. In here:
jQuery ->
url = $('.pagination .next_page a').attr('href')
$(window).scroll ->
url variable was assigned only one time, before scrolling and that was my mistake. So if url was save, so it always rendered second page and never third. We have to move our url lower, under .scroll event and it will always check, what page have been rendered. Like this:
jQuery ->
$(window).scroll ->
url = $('.pagination .next_page a').attr('href')
By the way, I will recommend to all (if they are using jQuery from RailsCast tutorial) to add some script to prevent multiple page load when user scroll
jQuery ->
$(window).scroll ->
url = $('.pagination .next_page a').attr('href')
return if(window.pagination_loading) # -> add this
if url && $(window).scrollTop() > $(document).height() - $(window).height() - 50
window.pagination_loading = true # -> and this
$('.pagination').text('Fetching products...')
$.getScript(url).always ->
window.pagination_loading = false # -> and this
And for prevent bugs etc. to url variable you need to add a, so it look like url = $('.pagination .next_page a').attr('href') and not like in RailsCast tutorial: url = $('.pagination .next_page').attr('href')

Rails AJAX Destroy - Re-render index issues

I have an issue with a Rails AJAX app which is confusing me, even though it seems very simple! I am dealing with class Order in a simple point of sale rails app. The request is being made as the order will be deleted on page refresh (but I am getting no refresh of #orders) as I thought I am specifying in destroy.js.erb.
orders/index.html
<div id="orders">
<%= render 'orders/index' %>
</div>
<%= link_to 'New Order', new_order_path, remote: true %>
<div id="order-form" style="display:none;"></div>
orders/_index.html
<% if #orders.any? %>
<% #orders.each do |order| %>
<%= link_to "Show #{order.id}", order_path(order), class: "something" %>
<%= link_to "Delete #{order.id}", order_path(order), method: :delete, class: "something" %>
<%= link_to "Delete order with ajax", order_path(order), remote: true, method: :delete %>
<% end %>
<% else %>
<p>No orders yet</p>
<% end %>
destroy.js.erb
//When deleting order on order index - render orders again.
$('#orders').html("<%= j (render 'orders/index') %>");
and the relevant actions from orders_controller.rb
class OrdersController < ApplicationController
respond_to :html, :js
def index
#orders = Order.paginate(page: params[:page])
if params[:search]
#orders = Order.search(params[:search]).order("created_at DESC")
else
#orders = Order.all.order('created_at DESC')
end
end
def destroy
#order = Order.find(params[:id])
if #order.destroy
flash[:notices] = ["Order was successfully deleted"]
redirect_to orders_path
else
flash[:notices] = ["Order could not be deleted"]
render order_path(#order)
end
end
I suspect the issue is in my orders_controller destroy or index action, but I am a little unclear on a number of the ways of working with AJAX in Rails.
Link to repo - https://github.com/benhawker/point-of-sale-rails
Might be because after your destroy you're redirecting to the index path
I am getting no refresh of #orders
Your JS is likely not firing, you'll need the following:
def destroy
#order = Order.find params[:id]
respond_to do |format|
if #order.destroy
format.js
format.html { redirect_to orders_path, notice: "Order was successfully deleted" }
else
format.js
format.html { render order_path(#order), notice: "Order could not be deleted" }
end
end
end
This will fire app/views/orders/destroy.js.erb, which seems okay in your OP.
Try Updating your destroy action to
#order = Order.find(params[:id])
#order.destroy
#orders=Order.all
remove all the redirects
will work.

rails AJAX js.erb same route more actions

I have rails4 app with bootstrap tabs. I have the root /common_medias for all the tabs, and loading the data w/ AJAX + format.js + js.erb for each tab when user clicks on the given tab. My problem is that I also wanna do infinite scrolling w/ the help of will_pagination gem, but the js.erb file is already used for loading the initial data. At the moment both the bootstarp tab js action and will_pagination js action point to the same url, so both try to use the same js.erb which causes some problems.
What is the good workaround? Creating a new controller action so there will be separated js.erb files (This one seems to be weird since pagination and original data will be separated). Or should I use conditionals in the single js.erb file to check what was the trigger action (loading data on tab click/ scrolling). I don't know how to implement any of these, so pls provide some code based on mine with the preferred way.
common_medias.html.erb
<div>
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active">Files</li>
<li role="presentation">Links</li>
<li role="presentation">Calendar</li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="filestab">
<%= render partial: "common_medias/message_file" %>
</div>
<div role="tabpanel" class="tab-pane" id="linkstab">
#LOADED BY JS ON TABCLICK
</div>
<div role="tabpanel" class="tab-pane" id="calendarstab">
...
</div>
</div>
</div>
common_medias controller
def common_medias
#param_id = request.parameters['user_id']
#user = User.find(#param_id)
#conversation ||= Conversation.between(current_user.id, #user.id).first
#common_files = #conversation.messages.with_file.order(created_at: :desc).paginate(page: params[:files], per_page: 3)
if params[:tab] == "file"
#common_files = #conversation.messages.with_file.order(created_at: :desc).paginate(page: params[:files], per_page: 3)
end
#messages can have multiple links, but with the following each link can be displayed separately with files
if params[:tab] == "link"
#message_links_pre = #conversation.messages.with_link.order(created_at: :desc).paginate(page: params[:links], per_page: 3)
#message_links = #message_links_pre.map { |f| { link: f.link, created_at: f.created_at, user_id: f.user_id} }.flatten
#common_links = formatting_links(#message_links)
end
if params[:tab] == "calendar"
#implementing later
end
respond_to do |format|
format.html
format.js
end
end
common_medias.js
$(document).on("page:change", function() {
//infinite scrolling for tasks based on pagination gem
if ($('#infinite-common-file-scrolling').size() > 0) {
$(window).on('scroll', function() {
$('#infinite-common-file-scrolling').hide();
var more_common_files_url;
more_common_files_url = $('#infinite-common-file-scrolling .pagination .next_page a').attr('href');
if (more_common_files_url && $(window).scrollTop() > $(document).height() - $(window).height() - 60) {
$('.pagination').html('<img src="/assets/ajax-loader.gif" alt="Loading..." title="Loading..." />');
$('#infinite-common-file-scrolling').show();
$.getScript(more_common_files_url);
}
});
};
if ($('#infinite-common-link-scrolling').size() > 0) {
$(window).on('scroll', function() {
$('#infinite-common-link-scrolling').hide();
var more_common_links_url;
more_common_links_url = $('#infinite-common-link-scrolling .pagination .next_page a').attr('href');
if (more_common_links_url && $(window).scrollTop() > $(document).height() - $(window).height() - 60) {
$('.pagination').html('<img src="/assets/ajax-loader.gif" alt="Loading..." title="Loading..." />');
$('#infinite-common-link-scrolling').show();
$.getScript(more_common_links_url);
}
});
};
});
$(document).on('shown.bs.tab', 'a[data-toggle="tab"]', function (e) {
var target = $(this).data("activetab");
var href = $(this).data("link");
$.ajax({
type: "GET",
url: href,
data: {tab: target},
dataType: "script"
});
});
common_medias.js.erb
//tabclick code
<% if params[:tab] == "file" %>
var filebody = $('.tab-pane.active');
filebody.html('<%= j render partial: "common_medias/message_file" %>');
<% elsif params[:tab] == "link" %>
var linkbody= $('.tab-pane.active');
linkbody.html('<%= j render partial: "common_medias/message_link" %>');
<% end %>
//CODE SHOULD RECOGNIZE WHICH ONE TO INVOKE
//pagination code for files
$('.tab-pane.active').append;
<% if #common_files.next_page %>
$('#infinite-common-file-scrolling .pagination').replaceWith('<%= j will_paginate #common_files %>');
<% else %>
$(window).off('scroll');
$('#infinite-common-file-scrolling .pagination').remove();
$('.no-more').delay(1000).fadeIn(1500);
<% end %>
//pagination code for links
$('.tab-pane.active').append;
<% if #message_links_pre.next_page %>
$('#infinite-common-link-scrolling .pagination').replaceWith('<%= j will_paginate #message_links_pre %>');
<% else %>
$(window).off('scroll');
$('#infinite-common-link-scrolling .pagination').remove();
$('.no-more').delay(1000).fadeIn(1500);
<% end %>
UPDATE
Changing tabs work well (goes to /get_links, fetching the data and renders get_links.js.erb). Will_paginate gem shows the proper URL in the DOM(/get_links), yet on scrolling common_media.js.erb will be rendered instead of get_links.js.erb.
common_medias controller
def common_medias
#messages = #conversation.messages.includes(:user).order(created_at: :desc).limit(50).reverse
#message = Message.new
#common_files = #conversation.messages.with_file.order(created_at: :desc).paginate(page: params[:files], per_page: 3)
# respond_to do |format| COMMENTED OUT STILL PAGINATION GOES FOR common_medias.js.erb
# format.html
# format.js
# end
end
def get_links
#message_links_pre = #conversation.messages.with_link.order(created_at: :desc).paginate(page: params[:links], per_page: 3)
#message_links = #message_links_pre.map { |f| { link: f.link, created_at: f.created_at, user_id: f.user_id} }.flatten
#common_links = formatting_links(#message_links)
respond_to :js
end
common_medias.html.erb
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active">Files</li>
<li role="presentation">Links</li>
<li role="presentation">Calendar</li>
</ul>
common_media.js
if ($('#infinite-common-link-scrolling').size() > 0) {
$(window).on('scroll', function() {
$('#infinite-common-link-scrolling').hide();
var more_common_links_url;
more_common_links_url = $('#infinite-common-link-scrolling .pagination .next_page a').attr('href'); //SHOWS /get_links?will_paginate_params url here
if (more_common_links_url && $(window).scrollTop() > $(document).height() - $(window).height() - 60) {
$('.pagination').html('<img src="/assets/ajax-loader.gif" alt="Loading..." title="Loading..." />');
$('#infinite-common-link-scrolling').show();
$.ajax({
type: "GET",
url: more_common_links_url,
data: {scrolling: "linkscroll"},
dataType: "script"
});
}
});
};
common_medias.js.erb
alert('whyyou');
get_links.js.erb
alert('hahalinks');
<% if params[:scrolling] == "linkscroll" %>
$('.linkbody').append('<%= j render partial: "common_medias/message_link" %>');
<% if #message_links_pre.next_page %>
$('#infinite-common-link-scrolling .pagination').replaceWith('<%= j will_paginate #message_links_pre %>');
<% else %>
$(window).off('scroll');
$('#infinite-common-link-scrolling .pagination').remove();
$('.no-more').delay(1000).fadeIn(1500);
<% end %>
<% else %>
var linkbody= $('.tab-pane.active');
linkbody.html('<%= j render partial: "common_medias/message_link" %>');
<% end %>

ruby if statment for js.erb file

Could anyone assist me in setting up my js.erb file to cover two scenarios.
At the moment i have infinite scroll working when the page initially loads, but when i change the params the new collection loads fine but when i scroll down the page to load the rest of the results i just get the next set of 6 objects replacing the last, rather than appending them onto the 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').empty();
<% end %>
Javascript
function infiniteScroll() {
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;
});
}
});
}
}
So on page load i call the function
$(function(){
infiniteScroll();
});
and as a ajax success callback
success: function(data) {
infiniteScroll();
}
When the ajax call is made the params :rehomed are always present so i could wrap the separate logic in
<% if params[:rehomed].present? %>
<% end %>
does anyone have any ideas for dealing with the newly loaded content in my rehome.js.erb file
Thanks
Update
So on page load i need this to be the logic
$('.all_animals').append("<%= j render #animals %>");
<% if #animals.next_page %>
$('.pagination').replaceWith('<%= j will_paginate #animals %>');
<% else %>
$(window).off('scroll');
$('.pagination').empty();
<% end %>
and when an update is made to the collection i need
$('.all_animals').empty();
$('.all_animals').append("<%= j render #animals %>");
<% if #animals.next_page %>
$('.pagination').replaceWith('<%= j will_paginate #animals %>');
<% else %>
$(window).off('scroll');
$('.pagination').empty();
<% end %>
How to combine them into one statement is what im trying to work out

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" })

Categories