I have a long block of comments on a view of model Page. Instead of showing all the comments on page load, I'm trying to create a "view more" button that shows the next ten comments. The button sends an ajax request to the controller, which then renders this block using jquery:
_view_more.html.erb
<% comments.each_with_index do |comment, index|%>
<% if (( index > #start_number) && (index < #end_number) ) %>
<%= comment.text %>
<% end %>
Let's say I always want to show the next 10 comments. I would just set #start_number = #start_number + 10 and #end_number = #end_number + 10
in the controller, but instance variables get reset, so #start_number would be nil. How can I set a variable that increases by 10 upon every ajax request?
"view more" button
<%= link_to "view more", view_more_page_path, remote: true %>
pages_controller.rb
def view_more
#page = Page.find(params[:id])
respond_to do |format|
format.html { redirect_to root_path }
format.js
end
end
view_more
$("#comments-body").append("<%= escape_javascript(render 'view_more') %>");
I will use haml and coffee-script
When rendering comments you put an html5 data attribute with the id of the comment:
#view where to render comments
%div#comments-wrapper{:"data-pageid" => #page.id}
=render #comments
=link_to "View more", "#", id: "view-more-link"
The comment partial
#comments/_comment.html.haml
%p.single-comment{:"data-commentid" => comment.id}
=comment.body
application.coffee
$ ->
$("#view-more-link").on 'click', ->
last_comment_id = $("#comments-wrapper .single-comment").last().data('commentid')
page_id = $("#comments-wrapper").data("pageid")
$.ajax
url: "/comments/view_more"
dataType: "script"
data:
last_comment_id: last_comment_id
page_id: page_id
comments_controller
def view_more
#page = Page.find(params[:pageid])
if params[:last_comment_id]
#comments = #page.comments.where("comments.id > ?", params[:last_comment_id]).limit(10)
end
respond_to do |format|
format.js
end
end
comments/view_more.js.erb
$("#comments-wrapper").append("<%= escape_javascript(render #comments) %>");
Note: I don't how your routes were set up so I put the page.id as a data-attribute as well
I would use already implemented pagination gems kaminari or will_paginate. I'll create this example using will_paginate.
First of all, it's important to say that your approach is incorrect, because it loads all comments every view_more request. If you want to show 10 comments, makes sense select only they from database, right? The pagination gem will do it for you!
Let's to the code:
"view more" button
<%= link_to "view more", view_more_page_path, remote: true, id: 'view-more-btn' %>
pages_controller.rb
def view_more
#comments = Page.find(params[:id]).comments.paginate(page: params[:page], per_page: 10)
respond_to do |format|
format.html { redirect_to root_path }
format.js
end
end
_view_more.html.erb
<% #comments.each do |comment| %>
<%= comment.text %>
<% end %>
view_more.js.erb
$("#comments-wrapper").append("<%= escape_javascript(render 'view_more') %>");
# We need to update the 'view more' link with the next page number
$('#view-more-btn').attr('href', '<%= view_more_page_path((params[:page] || 0) + 1) %>')
is it not good to update an hidden variable before making ajax call with the count..?
var currentVal = parseInt($("[type=hidden]").val());
$("[type=hidden]").val( currentVal + 1 );
add the hidden field right at the begining of the comments section with default value as "0"
<input type="hidden" name="hiddenId" id="hiddenId" value="0">
Hope it will help
If you want a quick and dirty approach, you could save start_number and end_number inside a cookie.
But keeping track of what needs to be rendered next on client side would be a right thing to do.
Related
In my Rails 7 (with bootstrap) app I need to create a table with all users transaction that has on each row an arrow that when clicked on, expands that row inside which is a new table (that's what I think it is - a new table). I think the attached design will better explain what I mean:
To achieved that I could try javascript with something like this https://jsfiddle.net/Wfxpu/180/ but I'm not sure if it's a modern approach. I'm wondering is it possible to a write something like this without any JS code using turbo maybe? or even pure HTML ?
Here is what I was trying to do:
# transaction controller
class TransactionsController < ApplicationController
def index
response = client.transactions.list(platform_id: current_user.platform_id, page: 1, per_page: 100)
#transactions = response.body['data']
end
private
def client
#client ||= TestAPI::Client.new
end
end
so then sample table would be:
# views/transactions/index
<table>
<% #transactions.each do |transaction| %>
<tr><%= transaction.amount %></tr>
<% end %>
</table>
You could try using a remote link_to as your "arrow" to invoke show_more action.
<%= link_to your_path, remote: true do %>
your image
<% end %>
In your controller you would need to respond to that with
def show_more
...
#data_to_show = some_database_call
respond_to do |format|
format.js
...
end
end
then in show_more.js.erb you can run javascript using the data that you just received.
var content = document.querySelector("#hidden-section-1");
content.innerHTML = ""
content.insertAdjacentHTML("beforeend",
"<%= j render('layouts/your_template', data: #data_to_show) %>");
where layouts/your_template would be a template with all the stuff you want to show(the "other" table you mentioned)
<h1> merchant: <%= data.merchant %> <h1>
...
<p> transaction_id: <%= data.transaction_id %> </p>
...
I just incorporated the DESTROY method for items in my school project. It worked fine, but now I must use AJAX to complete the action. After implementing this code, it only displays on the browser when I refresh the page, and not instantly when I delete an item. If I did not include enough information please let me know.
_item.html.erb
<% item.each do |i| %>
<p><%= i.name %> | <%= link_to "Complete", i, method: :delete, remote: true, class: 'glyphicon glyphicon-ok' %></p>
<% end %>
destroy.js.erb
<% if #item.destroyed? %>
$('#item-' +<%= #item.id %>).hide();
<% else %>
$('#item-' +<%= #item.id %>).prepend("<%= flash[:error] %>");
<% end %>
items_controller.rb
class ItemsController < ApplicationController
def index
#items = Item.all
end
def show
#item = Item.find(params[:id])
end
def new
#item = Item.new
end
def edit
#item = Item.find(params[:id])
end
def create
#item = Item.new(params.require(:item).permit(:name))
if #item.save
flash[:notice] = "The item was added to your list."
redirect_to current_user
else
flash[:error] = "There was a problem creating your item."
redirect_to current_user
end
end
def destroy
#item = Item.find(params[:id])
if #item.destroy
flash[:notice] = "\"#{#item.name}\" was completed and destroyed."
else
flash[:error] = "There was an error completing the item."
end
respond_to do |format|
format.html
format.js
end
end
end
Flash sets the message for the next request, this is why it works correctly in your create action (because you are redirecting). In your destroy action, you are rendering (which is not good, since it means delete request gets resent on page refresh, you should be redirecting here too) and setting flash, so it shows up on next request (refresh). If you want to send message for the response to the current request, you have to use flash.now :
flash.now[:notice] = "\"#{#item.name}\" was ..."
Again, you should use flash and redirect on success and use flash.now and render on request failure.
EDIT:
The above paragraphs only apply to html requests. I initially missed the point of the question! Thanks BroiSatse
Looking at your display code, you dont seem to be setting the "#item-#item.id", so your return js does nothing. Add it like this:
<p id="item-<%= i.id %>"><%= i.name %> | ...
I have following problem
I have link_to
//_sort_by.html.erb
<%= link_to "sort", :sort_by => "things", remote: true %>
And in my controller I have function which choose posts according to sort_by
def show_posts
#category=Category.find_by(name: params[:sort_by])
if(#category)
#posts=Post.where(category_id: #category)
respond_to do |format|
format.js
format.html
end
else
#posts=Post.all
end
end
and in views
//show_posts.html.erb
<div class="posts">
<%= render #posts %>
</div>
//
and I have _post.html.erb
It works fine (obviously without remote: true), but it refreshes site everytime when I change category of posts so
in show_posts.js.erb I'd like to refresh only class="posts", like
$('.posts').load(location.href + " .posts");
In routes.rb I have get 'show_posts'. But unfortunatelly it doesn't work. Could someone help me?
Edit:
changing show_posts.js.erb to
$('.posts').html("<%= j render #posts %>");
works. Thank you all for tips!
$('.posts').append("<%= escape_javascript render(:partial => 'posts') %>");
Change your show_posts.js.erb with these contents.
Try this out
$('.posts').html("<%= j render 'post' %>");
Hope this helps!
I am using Rails 4.1.1 Why do I have to click twice for jQuery effect to show?
I have been Googling but I cannot find any solutions, not sure if there's something wrong with my jQuery code or....
When I click "Submit" for the first time, the effect didn't appear but the POST action is already called, when I click for the second time, the effect appeared and the POST action is called.
Please check below my create.js.erb, users_controller.rb, new.html.erb
create.js.erb
function isValidEmailAddress(emailAddress) {
var pattern = new RegExp(/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))#((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i);
return pattern.test(emailAddress);
};
$("form").submit(function( event ) {
var email = $("input#user_email").val();
if (email == "") {
$("h1").text( "Email cannot be empty" ).show().fadeOut( 3000 );
return;
} else if (!isValidEmailAddress(email)) {
$("h1").text( "Email address is not valid" ).show().fadeOut( 3000 );
}else {
$("h1").text( "Correct email address" ).show();
}
event.preventDefault();
});
new.html.erb
<%= form_for #user, remote: true do |f| %>
<p>
<%= f.text_field :email %>
</p>
<p>
<%= f.submit :Submit %>
</p>
<% end %>
<h1></h1>
<% #users.each do |user| %>
<tr>
<li><%= user.email %></li>
</tr>
<% end %>
users_controller.rb
class UsersController < ApplicationController
def new
#users = User.all
#user = User.new
end
def create
#user = User.new(user_params)
respond_to do |format|
format.html { redirect_to '/' }
format.js
end
#user.save
end
private
def user_params
params.require(:user).permit(:email)
end
end
Turbo links might be causing this issue -try disabling turbo links in your app , that has worked for me In the past when I've had a similar problem
You're getting the process wrong. When you submit a remote form in Rails, the app sends an AJAX request to the create action and then evals the received js code (from your create.js.erb). So first time you click the button, nothing happens.
The js view is not the place to handle validations, you should put that somewhere in your assets/javascripts. The js view is a place for updating the html view of the page with the form (e.g. adding records to the list, showing received errors, reseting the form).
I'm trying to append new comments to a list of existing comments using javascript and ajax. I set up my Comments#create to create a new comment and then render its text. But how can I access this text with ajax?
controllers/comments_controller.rb
def new
#comment = Comment.new
#comments = Comment.all
end
def create
#thing = Thing.find(params[:thing_id])
#comment = #thing.comments.create(comment_params)
render text: #comment.text.to_s + "".html_safe
end
My form for a new comment and ajax/javascript attempt:
<%= form_for([#thing, #comment], remote: true) do |f| %>
<%= f.text_area :text, :placeholder => "Explain your rating..." %>
<div id="btn"><%= f.submit "Post", class: "btn", id: "postacomment" %></div>
<script type="text/javascript">
$("#postacomment").click(function() {
$.get( "<%= new_thing_comment_path(:id => #comment.id) %>", function( data ) {
$('#comments_h2').prepend( data );
});
});
</script>
<% end %>
First of all, don't try to bend HTTP methods to fill your needs, follow them instead.
If you want to respond to javascript with rails, that is fairly easy. On your comments controller:
def new
#comment = Comment.new
#comments = Comment.all
end
def create
#thing = Thing.find(params[:thing_id])
#comment = #thing.comments.create(comment_params)
respond_to do |format|
format.html { redirect_to new_comments_path } #this is just a redirection in case JS is disabled
format.js
end
end
As you can see we are now responding to two types of formats, in this case html and js, this forces you to have those corresponding views, or at least for the js version which may look like this:
app/views/comments/create.js.erb:
$('#comments_h2').prepend("<%= j #comment %>");
In the example above I'm assuming you have a partial for rendering a comment, it should look something like:
app/views/comments/_comment.html.erb:
<h2><%= comment.content %></h2>
Obviously you have to update that file to meet your needs.
Hope it helps!