Rails 5 using will_paginate to create a load more button - javascript

So I'm trying to create a load more button (not infinite scroll although some simple js tweaks could make it so). I'm using the will_paginate gem in Rails 5.
Its currently all working as expected except when I click load more it loads the next page posts twice e.g:
Load more >
Post 4,
Post 5,
Post 6,
Post 4,
Post 5,
Post 6
FYI my 'posts' are #links:
links_controller.erb
def index
#links = Link.order('created_at DESC').paginate(:page => params[:page], :per_page => 3)
respond_to do |format|
format.html
format.js
end
end
views/links/index.html.erb
<div class="link-wrap">
<%= render #links %>
</div>
<% if #links.next_page %>
<div class="link more">
<%= link_to 'Load More', links_path(:page => #links.next_page), :class => 'next_page', :remote => true if #links.next_page %>
</div>
<% end %>
pagination.js.coffee
$ ->
$('.next_page').on 'click', (e) ->
e.preventDefault()
url = $(this).attr('href')
$.getScript(url)
index.js.erb
$('.link-wrap').append('<%= j render(#links) %>');
<% if #links.next_page %>
$('.next_page').attr('href','<%= links_path(:page => #links.next_page) %>');
<% else %>
$('.more').remove();
<% end %>
For the life of me I can't figure out why it would be rendering the links twice when 'load more' is clicked. This seems like a really simple thing but I'm struggling.

So it was a stupid error of course.
Remove the line on index.html.erb:
:remote => true if #links.next_page
So now it looks like this:
<div class="link-wrap">
<%= render #links %>
</div>
<% if #links.next_page %>
<div class="link more">
<%= link_to 'Load More', links_path(:page => #links.next_page), :class => 'next_page' %>
</div>
<% end %>
All works fine now!

If you check the server console, you can actually see two requests coming in, one triggered by your :remote => true enabled link and other from your custom ajax. Actually you don't need any js to make this work. Just remove that click function from your pagination.js.coffee or remove :remote => true from the link. After all both are doing the same thing. Hope this will help.

Related

Rails 5, simple javascript with form submit

I am hoping someone can help me here. Been at this for a few hours, but can't seem to figure out why my Javascript is not working on my simple form. I'm simply trying to display comments using the ajax call. I have gem 'jquery-rails', and required jquery, and jquery_ujs in my application.js file. Here's the code I have so far:
I have a Joy model that has_many comments.
<% #joys.each do |joy| %>
<ul id="comments-<%= joy.id %>">
<% joy.comments.where(void:'f').each do |comment| %>
<li><%= comment.body %></li>
<% end %>
</ul>
<%= form_with(model: [ joy.user, joy, joy.comments.build ], data:{ "js-comments-#{joy.id}" => true}) do |f| %>
<%= f.text_area :body, :class=>'form-control', :required=>'true' %>
<%= f.submit "Post Comment" %>
<% end %>
<script>
$(document).ready(function() {
$('[data-js-comments-<%= joy.id %>]').on("ajax:success", function(event, data, status, xhr){
$('#comments-<%=joy.id%>').append(xhr.responseText);
});
});
</script>
<% end %>
In my Comment controller, in my 'create' action, I have:
if #comment.save
render partial: "comment", locals: {comment: #comment}
end
In my Comment views, I created a partial, called _comment.html.erb:
<li>
<%= comment.body %>
</li>
So, now when I enter a comment, and click 'Post Comment', it correctly saves my Comment, but it does not ajax load the comment into list. Furthermore, the form does not refresh either. The text remains in the text box. It is not only until after I refresh the page when my comment shows up in the list. Anyone with any thoughts on what I'm missing or doing wrong???
Your help is greatly appreciated in advance.
form_with it's a remote form, you dont need that block <script>..</script>.
Just use create.js.erb then use jquery to append new comment.
And if you want to check if your message was created successfully, you can add a #status value in your controller.
//comments_controller.rb
def create
...
if #comment.save
#status = "success"
else
#status = "failed"
end
...
end
//create.js.erb - for example
$("#yourCommentList").append('<%= j( render partial: 'comments/comment', locals: #comment ) %>');
<% if #status == "success" %>
$("body").append(...your notification...)
...
<% else %>
...
<% end %>

Rails will_paginate two objects on the same page, passing one param from search field

I am using will_paginate on the results from two instance variables, each using the same param[:query] on two different PgSearch.multisearch methods. In the views, when clicking on one of the pagination links, both the tables were being updated. Is there any way to get around this problem? By passing only one param from the form? (As my search form has only one text field). I have searched and gone through the similar questions, most of them suggesting to use two params., but none of them gave me any thoughts to solve this prob using one param. :/
Code in the form:
<%= form_tag("/search", :method => "get", :remote => true) do %>
<%= label_tag(:query, "Search for: ") %>
<%= text_field_tag(:query, params[:query]) %>
<%= submit_tag("Search", class: "btn btn-primary") %>
<% end %>
Controller code:
def index
#employee = Employee.find(session[:user_id])
#search_employees = PgSearch.multisearch(params[:query]).where(:searchable_type => "Employee").paginate(page: params[:page], :per_page => 5)
#search_customers = PgSearch.multisearch(params[:query]).where(:searchable_type => "Customer").paginate(page: params[:page], :per_page => 5)
respond_to do |f|
f.html
f.js
end
end
Code in the View:
<% if !#search_employees.empty? %>
<h2>Employees</h2>
<table class="table table-hover">
.
.
<% #search_employees.each_with_index do |doc, index| %>
<% user = doc.searchable %>
<tr id="employee-<%= user.id %>">
.
.
<% end %>
</tbody>
</table>
<% end %>
<%= will_paginate #search_employees %>
<% if !#search_customers.empty? %>
<h2>Customers</h2>
<table>
.
.
</table>
<% end %>
<%= will_paginate #search_customers %>
Can I send two different params with the same text field value from the form? if so., please let me know how. Any ideas or suggestions would be much appreciated. :)
Thanks in Advance. :)
Pagination depends on the page parameter, but not on the query parameter from the search form, therefore you don't need to change anything in your form.
To update only corresponding table you need to customize page parameter in at least one of the tables. For example change page parameter for customers:
<%= will_paginate #search_customers, :param_name => 'customers_page' %>
and in controller:
#search_customers = PgSearch.multisearch(params[:query])
.where(:searchable_type => "Customer")
.paginate(:page => params[:customers_page], :per_page => 5)
This should resolve the issue

Why does my js.erb file only run in one of these situations?

I have the following action on my controller for a Comment:
def create
#comment = comment.new(params[:comment])
#comment.replyable_type = params[:replyable_type]
#comment.replyable_id = params[:replyable_id]
#comment.user = current_user
respond_to do |format|
format.html do
# Removed
end
format.js do
#comment.save
end
end
end
and the following create.js.erb file for when it gets called by a remote form:
console.log('It did run');
<% if #comment.errors.any? %>
<% #comment.errors.full_messages.each do |msg| %>
console.log('<%= javascript_escape(msg) %>');
<% end %>
<% else %>
comment = "<%= escape_javascript render #comment %>";
<% if #comment.replyable_type == 'Comment' %>
$('#comment-<%= #comment.id %> > .replies').prepend(comment));
<% else %>
$('.comments').prepend(comment);
<% end %>
// removed
<% end %>
Along with the following form partial:
<%= form_for(Comment.new, url: comments_path, remote: true) do |f| %>
<div class="comment-form" id="comment-<%= replyable_type %>-<%= replyable_id %>">
<%= f.text_area :content, cols: 60, rows: 5 %>
<%= hidden_field_tag :replyable_id, replyable_id %>
<%= hidden_field_tag :replyable_type, replyable_type %>
<%= f.submit 'Send Comment', class: 'btn btn-primary' %>
</div>
<% end %>
replyable is a polymorphic association, which can be either a Post or a Comment object. When I submit a copy of the form on a page for a post, where replyable_type is Post and replyable_id is a post's ID, it works fine. When I submit a copy of the form on the same page, with the replyable_type set to Comment, and the replyable_id set to a comment's ID, the view the javascript does not run, at all. The action on the controller runs, using Firebug I can see the rendered javascript being sent back in response to the post request, and the comment is added to the database.
There are no errors in either the Rails console for generating the javascript or in the Firebug console to do with running the generated javascript. There are also no errors in the #comment.errors array. Even the first console.log in the Javascript is not run when the replyable object is a comment, despite being present in the rendered javascript.
What could cause this? I am using Rails 3.2.13 on Ruby 1.9.3

AJAX micropost's comments on the user page

On the user's page there are microposts and each of them have it's own comment form and comments. Using "Endless Page" railscast i'm trying to create "show more comments" button, which will load comments by AJAX. But it's not working.
The problem is in show.js.erb file because:
1) common pagination of comments (without AJAX) is working well
2) "show more button" is working well too. I tested it on the users list page
I think "show more comments" not working because it don'understand <%= j render(comments) %> , <%= j will_paginate(comments) %> and i should have here variables like <%= j render(#comments) %> , <%= j will_paginate(#comments) %>.
But when i try to write in my users_controller.rb
def show
#micropost = Micropost.find(params[:micropost_id])
#comments = #micropost.comments
end
it's not working because on my user's page there are many microposts and i have an error "Couldn't find micropost without an id". So in my microposts/_micropost.html.erb i had to use this
<% comments = micropost.comments.paginate(:per_page => 5, :page => params[:page]) %>
<%= render comments %>
Can anyone please help? How should i change my show.js.erb?
users/show.html.erb
<%= render #microposts %>
microposts/_micropost.html.erb
...
micropost's content
...
<%= render 'comments/form', micropost: micropost %>
<% comments = micropost.comments.paginate(:per_page => 5, :page => params[:page]) %>
<div class="commentaries">
<%= render comments %>
</div>
<div id="append_and_paginate">
<%= will_paginate comments, :class =>"pagination", :page_links => false %>
</div>
javascripts/users.js.coffee
jQuery ->
if $('.pagination').length
$('#append_and_paginate').prepend('<a id="append_more_results" href="javascript:void(0);">Show more</a>');
$('#append_more_results').click ->
url = $('.pagination .next_page').attr('href')
if url
$('.pagination').text('Fetching more...')
$.getScript(url)
users/show.js.erb
$('.commentaries').append('<%= j render(comments) %>');
<% if comments.next_page %>
$('.pagination').replaceWith('<%= j will_paginate(comments) %>');
<% else %>
$('.pagination').remove();
<% end %>
<% sleep 0.3 %>
Bennington, in users/show.html.erb you have
But #microposts is not defined in your controller. What I think you want is to define #microposts as all the microposts associated with what, a User?
If so, you'd want something like `#microposts = Micropost.where(:user_id => current_user.id) or something.

Problem using form builder & DOM manipulation in Rails with multiple levels of nested partials

I'm having a problem using nested partials with dynamic form builder code (from the "complex form example" code on github) in Rails. I have my top level view "new" (where I attempt to generate the template):
<% form_for (#transaction_group) do |txngroup_form| %>
<%= txngroup_form.error_messages %>
<% content_for :jstemplates do -%>
<%= "var transaction='#{generate_template(txngroup_form, :transactions)}'" %>
<% end -%>
<%= render :partial => 'transaction_group', :locals => { :f => txngroup_form, :txn_group => #transaction_group }%>
<% end -%>
This renders the transaction_group partial:
<div class="content">
<% logger.debug "in partial, class name = " + txn_group.class.name %>
<% f.fields_for txn_group.transactions do |txn_form| %>
<table id="transactions" class="form">
<tr class="header"><td>Price</td><td>Quantity</td></tr>
<%= render :partial => 'transaction', :locals => { :tf => txn_form } %>
</table>
<% end %>
<div> </div><div id="container">
<%= link_to 'Add a transaction', '#transaction', :class => "add_nested_item", :rel => "transactions" %>
</div>
<div> </div>
... which in turn renders the transaction partial:
<tr><td><%= tf.text_field :price, :size => 5 %></td>
<td><%= tf.text_field :quantity, :size => 2 %></td></tr>
The generate_template code looks like this:
def generate_html(form_builder, method, options = {})
options[:object] ||= form_builder.object.class.reflect_on_association(method).klass.new
options[:partial] ||= method.to_s.singularize
options[:form_builder_local] ||= :f
form_builder.fields_for(method, options[:object], :child_index => 'NEW_RECORD') do |f|
render(:partial => options[:partial], :locals => { options[:form_builder_local] => f })
end
end
def generate_template(form_builder, method, options = {})
escape_javascript generate_html(form_builder, method, options)
end
(Obviously my code is not the most elegant - I was trying to get this nested partial thing worked out first.)
My problem is that I get an undefined variable exception from the transaction partial when loading the view:
/Users/chris/dev/ss/app/views/transaction_groups/_transaction.html.erb:2:in
_run_erb_app47views47transaction_groups47_transaction46html46erb_locals_f_object_transaction'
/Users/chris/dev/ss/app/helpers/customers_helper.rb:29:in
generate_html'
/Users/chris/dev/ss/app/helpers/customers_helper.rb:28:in
generate_html'
/Users/chris/dev/ss/app/helpers/customers_helper.rb:34:in
generate_template'
/Users/chris/dev/ss/app/views/transaction_groups/new.html.erb:4:in
_run_erb_app47views47transaction_groups47new46html46erb'
/Users/chris/dev/ss/app/views/transaction_groups/new.html.erb:3:in
_run_erb_app47views47transaction_groups47new46html46erb'
/Users/chris/dev/ss/app/views/transaction_groups/new.html.erb:1:in
_run_erb_app47views47transaction_groups47new46html46erb'
/Users/chris/dev/ss/app/controllers/transaction_groups_controller.rb:17:in
new'
I'm pretty sure this is because the do loop for form_for hasn't executed yet (?)... I'm not sure that my approach to this problem is the best, but I haven't been able to find a better solution for dynamically adding form partials to the DOM. Basically I need a way to add records to a has_many model dynamically on a nested form.
Any recommendations on a way to fix this particular problem or (even better!) a cleaner solution are appreciated. Thanks in advance.
Chris
I suggest to use another example instead of this. I went also through this process recently and the best implementation is from ryan bates this: http://github.com/ryanb/complex-form-examples/tree/unobtrusive-jquery-deep
If you are using prototype you that's not a problem, you can easily change the implementation to prototype from jquery.

Categories

Resources