I have a model "Thing" which has_many "Comments". I want the list of #thing.comments to refresh with ajax when the "post a comment" button is pressed.
This code works to list the text of the first comment:
view
<script type="text/javascript">
$("#post").click(function () {
$.get( "<%= postcomment_thing_path(:id => #thing.id) %>", function( data ) {
$('#comments_h2').html(data);
});
});
</script>
controller:
def postcomment
#thing = Thing.find(params[:id])
render text: #thing.comments.first.text.to_s
end
But when I try to print the whole comment block, it only prints one single "#".
controller:
def postcomment
#thing = Thing.find(params[:id])
render text:
#thing.comments.each do |comment|
comment.text.to_s
end
end
How can I print the text for all the comments?
You don't need to load all comments when a new one is posted, you load the old ones with rails and display them, then with ajax you append/prepend a new one to the list of old ones.
http://api.jquery.com/prepend/
http://api.jquery.com/append/
I assume your comments are in a div:
<div class="comments">
<% #thing.comments.each do |comment| %>
<div class="comment_<%= comment.id %>">
<%= comment.content %>
</div>
<% end %>
</div>
you form where you add a comment should have remote: true:
<%= form_for #comment, remote: true do |f| %>
inputs here
<% end %>
when you add a comment, I assume it's action create that's being called, so you'll need a create.js.erb file in app/views/comments/ folder and a _comment.html.erb in the same location.
create.js.erb will contain your js code that will append/prepend the comment to the div.comments element:
<% if #comment.valid? %> // #comment should be loaded in controller create action.
$('div.comments').append("<%= j(render(#comment)) %>");
$("#new_comment")[0].reset();
<% else %>
alert("Can't add comment");
<% end %>
You should add a "remote: true" to your "post comment" button. From there, you can have your controller respond_to |format| and have the data updated in realtime.
More information about working with ajax in Rails found here
Well, while its not a good idea to be displaying all the comments again as suggested by #rmagnum2002. You should be appending or prepending the current comment based on your requirements. However, to get the current code working you must make some changes
def postcomment
#thing = Thing.find(params[:id])
comment_text = ""
#thing.comments.each do |comment|
comment_text += comment.text.to_s
end
# you could use map and join them by a delimiter also
render text: comment_text
end
and that should render what you expect.
Hope that helps
Related
I am building webstore. I added a bit of AJAX functionality to add, update and remove items from my cart without refreshing.
It works perfect as long as there are no items in a cart and render cart_empty partial properly.
The problem is when I add few items, and I can update/remove them without refreshing and
finally I remove the last one, and it do not switch to the cart_empty partial and there are only empty table headers left from cart-items partial.
Can I add some logic to destroy.js.erb?
destroy.js.erb
$("#cart-item").html("<%= j render 'carts/cart_items' %>");
the show.html.erb contains this unless function:
<% unless current_order.order_items.empty? %>
<div id="cart-item">
<%= render 'cart_items' %>
</div>
<% else %>
<div id="cart-empty">
<%= render 'cart_empty' %>
</div>
<% end %>
Maybe should I modify somehow my controller to check if there are order_items in a current_order?
Thank you!
A JS.erb template is evaluated in exactly the same way as a HTML template. So you can just add a condition:
<% if current_order.order_items.any? %>
$("#cart-item").html("<%= j render 'carts/cart_items' %>");
<% else %>
$("#cart-item").html("<%= j render 'carts/cart_empty' %>");
<% end %>
None that this implies that current_order is either a helper or a local passes to the view.
But you might want to refactor this and push the condition down to the partial:
# carts/cart_items.html.erb
<% if cart_items.none? %>
<% render :cart_empty %>
<% else %>
# ...
<% end %>
Which will let you just write:
<%= render 'carts/cart_items', cart_items: current_order.order_items %>
See:
Layouts and Rendering in Rails - passing local variables
When calling render The view is not being updated, however, I do notice in the network tab, after the POST request is made to me POST route, it's returning HTML as the response, and the response has my rendered error message. It's just not updating the page. I don't know what to make of that.
In my POST action, I'm forcing this to be called
flash.now[:notice] = responseMessage
render :deactivate_show
Which renders the action:
def deactivate_show
#user = User.find(params[:id])
authorize! :deactivate_user, #user
if current_user.role == "admin"
if #user.broker?
#companyUsers = User.where(parent_id: #user.id)
#properties = Property.where(user_id: #user.id)
.or(Property.where(user_id: #companyUsers.ids))
elsif #user.broker_manager?
# get all agents in company
# exclude current user
# get broker
#companyUsers = User.where(parent_id: #user.parent_id)
.where.not(id: #user.id)
.or(User.where(id: #user.parent_id))
#properties = Property.where(user_id: #companyUsers.ids)
.or(Property.where(user_id: #user.parent_id))
else #user.agent?
#properties = #user.properties
if #user.parent_id?
#companyUsers = User.where(parent_id: #user.parent_id)
.where.not(id: #user.id)
.or(User.where(id: #user.parent_id) )
else
# dealing with owner.
#companyUsers = User.where('role IN (2,4,1)')
.where.not(id: #user.id)
end
end
else
end
end
which renders a view I have that displays the form.
I added to my show view:
<div class="messages">
<% flash.each do |key, value| %>
<div class="hello"><%= value %></div>
<% end %>
</div>
Note the class of "hello".
When I make a POST request, the response in the network tab has the notice:
Rendered from POST request response:
<div class="messages">
<div class="hello">testing errors</div>
</div>
Edit: here's the form:
<div class="form-wrap">
<%= form_with do %>
<%= button_tag( id: 'button--submit', class: 'button button--secondary') do %>
<i class="icon-arrow-right"></i>
<span>Deactivate</span>
<% end %>
<% end %>
</div>
Well the actual answer was something no one could have provided since I didn't share my form. I was using form_with, which apparently does AJAX by default, thus not rendering the view.
Thanks for the help from this question: Rails render not showing in browser, despite positive server reply
I've changed
<%= form_with do %>
to
<%= form_with local: true do %>
and now it works.
The Flash is only rendered upon a new request, which you don't make with render. Instead, you should use flash.now[:notice] to have it display on render.
New to AJAX and search. I feel like I'm an inch away on this one, but I'm not sure what to fix. Also, my controller looks really hacky to me right now.
At any rate, I'm trying to create a search that allows users to search through blog posts on my page using AJAX. Here are the (relevant parts of the) parts:
posts_controller.rb
def show
#posts = Post.all.reverse
#post = Post.find(params[:id])
#link_num = 10
respond_to do |format|
format.html # show.html.erb
format.json { redirect_to #post }
end
end
def search
#link_num = 10
#posts = Post.all.reverse
#post = Post.find(params[:id])
#The including function returns the search results
#search = Post.first.including(params[:term])
render 'show'
end
What strikes me as "hacky" here is that I repeat all the variable assignments (there are others I didn't show cause they're not relevant). Shouldn't an AJAX call ideally not have to redefine/reload all these variables? Also, I have to pass :id to my search action through a hidden field. This feels weird/wrong to me.
show.html.erb
<h1 class="squeeze">Recent Posts</h1>
<%= form_tag("/search", method: "get", class: "search") do %>
<%= text_field_tag(:term, '', placeholder: "Search posts:") %>
<%= hidden_field_tag(:id, #post.id) %>
<%= submit_tag("Search", class: "btn search_button", remote: true) %>
<% end %>
<% if !#search%>
<ul>
<% #posts.first(#link_num).each do |p| %>
<li>(<%= p.created_at.strftime("%b %d, %Y") %>)</span></li>
<% end %>
<% if #posts.length > #link_num %>
<div class="link_disclaimer">
<h4>---</h4>
<h5><%= "Only showing #{#link_num} most recent posts." %></h5>
<h5>Search to narrow results.</h5>
</div>
<% end %>
</ul>
<% elsif #search.empty? %>
<h3>Term not found!</h3>
<% else %>
<ul>
<% #search.first(#link_num).each do |p| %>
<li>(<%= p.created_at.strftime("%b %d, %Y") %>)</span></li>
<% end %>
<% if #search.length > #link_num %>
<div class="link_disclaimer">
<h4>---</h4>
<h5><%= "Only showing first #{#link_num} relevant hits." %></h5>
<h5>Narrow search for more specific results.</h5>
</div>
<% end %>
</ul>
<% end %>
routes.rb
match '/search', to: 'posts#search'
Currently, the search itself works fine, with three major problems:
The aforementioned messiness of my Controller.
The fact that the whole page reloads. Isn't that the opposite of what AJAX is supposed to do? How can I get it to reload just the list of links?
The URL. It's super messy (i.e "/search?utf8=✓&term=limits&id=11&commit=Search"). I'd ideally have it remain untouched by the search, but if that's not possible, maybe just something like "/search?=". How's that done?
Based on the comment here is basic logic to make the function work(Sorry for no real code as that is too time consuming)
In controller you make a method say "search". The method need an argument which is the phrase to search. It gets the result from db and respond to JSON only with the result.
In template you have a search form.
In JS, you listen the event of user clicking the submit button, catch the characters they write, and handle it to ajax function.
Write Ajax code, preferred using jQuery's ajax(). Basically use ajax() to send the typed characters to controller method in #1 as argument. Then ajax() will get the response(the search result) from server.
Update the result in DOM. You can either add a new div to show search result, or replace current main content div to show result.
The case is when user click an add link, if the url already added, there will be a alert otherwise will display a form to add new bookmark. The code below works quite well for checking the duplicated url, but if the url is not duplicated I just don't know how to render a add bookmark (in this case the page will be loaded like a normal non ajax request)
This is the link in view
<%= link_to "add", user_bookmark_add_path(current_user, bookmark), remote: true %>
The link will invoke the controller action add
# controllers/bookmarks_controller.rb
def add
#bookmark = Bookmark.find(params[:bookmark_id])
respond_to do |format|
format.js
end
end
The javascript file
# views/bookmarks/add.js.erb
<% if duplicated_url? #bookmark.url %>
alert("Duplicated")
<% else %>
# how to render the new bookmark form here
<% end %>
Any suggestion ? Thanks
Create a partial for new bookmark form.
_form.html.erb
<%= form_for(bookmark) do |f| %>
<%= f.text_field :name %>
<%= f.submit "submit" %>
<% end %>
Add id to your link
.html.erb
<%= link_to "add", user_bookmark_add_path(current_user, bookmark), remote: true, id: "bookmark" %>
Replace your link with partial.
.js.erb
<% if duplicated_url? #bookmark.url %>
alert("Duplicated")
<% else %>
$("#bookmark").replaceWith("<%= j render "form", bookmark: Bookmark.new %>");
<% end %>
On your add.js.erb file, in the else part of the code you can append a partial to your list like this:
$('#your_list').append(
"<%= escape_javascript(render('your_item_of_the_table_partial')) %>"
);
This partial can be a list item, a table row, a div with your content, anything. The thing is, you will need a chunk of html to be re-rendered on your screen with the new content.
Example of a list item partial:
# _bookmark_item.html.erb
<li><%= #bookmark.url %> </li>
Try something like this:
$('#your_div_id').append('<%= escape_javascript(raw render :partial => 'your_form_partial') %>')
This will add the contents of your ruby partial to the DOM.
I'm attempting to make a two column layout. The column on the left is navigation, and the column on the right is content.
Is there a way to display show.html.erb, edit.html.erb, and new.html.erb at different times in the right div, when the corresponding navigation is selected, without re-loading the whole page?
I know I can use a partial for the left div, and render new pages, but I want to avoid having a separate page load for each view.
Item Controller:
def index
#item = item.find(:all, :order => "id DESC")
end
def new
#item = item.new
end
def create
#item = item.new(params[:item])
if #item.save
redirect_to root_path
else
render "new"
end
end
def edit
#item = item.find(params[:id])
end
def update
#item = item.find(params[:id])
if #item.update_attributes(params[:item])
redirect_to root_path
else
render "index"
end
end
HTML:
<div id="left" ">
<p id=link_to "Current_Item", item_path %></p>
<p id=Link_to "Add_Item", new_item_path %></p>
<p id=Link_to "Edit_Item", edit_item_path %></p>
</div>
<div id="right">
</div>
Routes File:
resources :items
Is "but I'm trying to avoid having a separate page load" means that you want to load your views by ajax instead of a complete new page? If so then your links should be a remote_link and your views should update the div id='right' with the content of corresponding view.
Your links should use :remote=>true option. See the details.
And your view should response to update the content within the <div id="right">.
Another link here with some more insights.
I would recommend modifying app/views/layouts/application.html.erb then put
<div id="left">
<p><%= link_to "Current_Item", item_path %></p>
<p><%= Link_to "Add_Item", new_item_path %></p>
<p><%= Link_to "Edit_Item", edit_item_path %></p>
</div>
<div id="right">
<%= yield %>
</div>
into it. Then the show.html.erb, edit.html.erb, and new.html.erb views should be automatically rendered for each appropriate action that gets called into the right section of your page.
Also I recommend Rails tutorial which is a great tutorial on rails. Then go to Codeschool to learn even more.