Will_paginate, render next page - javascript

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')

Related

Individual JavaScript for multiple records in Rails

I've been trying to wrap my head around this for days now, but I can't seem to find a solution for this problem.
Basically what I want to do is a feed similar to Facebook. A feed with multiple posts, of which each has comments/replies.
Now what I can't get to work is an AJAX "Load more" button for each post. I've only got it working for all posts at once, but not for individual ones.
What I have so far:
The feed:
<% #posts.each do |post| %>
<%= render "posts/thread", post: post %>
<% end %>
The threads:
<%= render post %>
<% replies = post.replies.paginate(:page => params["replies#{post.id.to_s}"], :per_page => 2) %>
<%= render replies %>
<%= will_paginate replies, :param_name => 'replies'+post.id.to_s %>
<%= link_to "Load More", "#", class: "load-more", id: post.id, :remote => true %>
The posts:
<div class="post" data-id="<%= post.id %>">
## content ##
</div>
pagination.coffee:
jQuery ->
$('.load-more').on 'click', ->
postId = $(this).attr('id')
more_posts_url = $('#post-'+postId+' .pagination .next_page').attr('href')
if more_posts_url
$('.load-more').html('<img src="/assets/ajax-loader.gif" alt="Loading..." title="Loading..." />')
$.getScript more_posts_url
return
return
index.js.erb:
<%= #posts.each do |post| %>
$('#post-<%= post.id %>').append('<%= j render post.replies %>');
<% end %>
But this does nothing.
I really don't understand how JS works for multiple records on one page.
I suppose this could help: https://jsfiddle.net/se708oou/2/
Main points of difference:
You should loop through all of the .load-more elements
$('.load-more').each(function() {
Change reference to use this
$(this).on('click', function() {
(Miscellaneous) This is how you take data-* in jQuery:
postId = $(this).data('id');
Cheers! :)
Your posts div
<div class="post" data-id="<%= post.id %>">
...
</div>
Has a class of post and a data attribute id, but in index.js.erb you are trying to select #post-<%= post.id %>
That selector isn't in the dom, You need to change your selector to
$('.post[data-id=<%= post.id %>').append('<%= j render post.replies %>');

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

will_paginate with endless Scrolling | Rails4

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

How can I return javascript from a Ruby on Rails helper method?

I have a Note model, which can contain have either an image link attachment (linktype = "image" or some text (linktype = "text). When I display the notes, the method of display changes depending on the linktype. An example is:
<% #notes.each do |q| %>
<h2 class="title"><%= q.name %></h2>
<% if q.linktype == "other"%>
<script type="text/javascript">some javascript</script>
<% elsif q.linktype == "text"%>
<%= q.text %>
<% elsif q.linktype == "image"%>
<img src="<%= q.link %>" />
<% end %>
<% end %>
I have to display the notes in a few different views in my site, so rather than have to repeat the viewing code multiple times, I want to have it in one place and refer to it from different views.
My initial thought was to put the display code in a helper, something like this:
<% #notes.each do |q| %>
note_display(q.linktype, q.link)
<% end %>
But some of the displaying involves javascript (line 4 in first code block). Can I still use a helper method, even though I would need it to return javascript? If so, how do I do it? Thanks for reading.
There's nothing special about javascript, you can return it from helpers as you would return other html content. The only thing I would suggest is to use helpers for tags like
image_tag(q.link)
javascript_tag("some javascript")
content_tag("h2", q.name, :class => "title")
As far as ERB is concerned, JavaScript is just string content like anything else rendered by a template. So your helper method can just construct and return a string:
def note_display(note)
content = ''
content << content_tag('h2', h(note.name), :class => 'title')
if note.linktype == 'other'
content << javascript_tag("some javascript")
elsif note.linktype == 'text'
content << h(note.text)
elsif note.linktype == 'image'
content << image_tag(note.link)
end
content
end
You can use this helper method using:
<% #notes.each do |n| %>
<%= note_display(n) %>
<% end %>

Categories