I know there's a ton of threads about this, but I can't seem to figure out what's broken here. I have a remote link that calls a like method.
When I click the like link...
What's happens:
The like is created (as expected).
The page does not refresh (as expected).
What should happen:
Everything above, plus the content change in like.js.erb. I've added a console.log to this file and it does not show up when clicking the like link.
_like.html.erb
<% recipe = recipe || #recipe %>
<%= link_to unlike_recipe_path(recipe), remote: true, class: "recipe-card__like like" do %>
<i class="heart icon"></i>
<% end %>
note: the partial is rendered both on a show page and within an index, which is why I use the || statement.
like.js.erb
$('.like').bind('ajax:success', function(){
console.log('like clicked');
$(this).closest('.like').hide();
$(this).closest('.like-container').html('<%= escape_javascript (render partial: 'unlike', recipe: #recipe ) %>');
});
routes.rb
resources :recipes do
resources :likes
member do
get "/like", to: "recipes#like"
end
end
recipes_controller.rb
def like
#recipe = Recipe.find(params[:id])
#like = #recipe.likes.create(recipe_id: #recipe.id, user_id: current_user["uid"])
respond_to do |format|
format.html
format.js
end
end
application.js
//= require jquery
//= require jquery_ujs
//= require rails-ujs
//= require turbolinks
//= require semantic-ui/sidebar
//= require_tree .
You can do it in many ways using JS/Html/Ajax.
#like.js.erb
#if wanna validate the request success or fail use #like
# just make sure #like is not string type otherwise it will always success in JS file.
if("<%= #like %>"){
console.log('like clicked');
$(this).closest('.like').hide();
$(this).closest('.like-container').html('<%= escape_javascript (render partial: 'unlike', recipe: #recipe ) %>');
}
Also, in controller no need recipe_id: #recipe.id. It's supposed to assign automatically.
#like = #recipe.likes.create(user_id: current_user["uid"])
Like I said in comment, the moment you .bind('ajax:succeed') is already to late because this file is indeed the result. Your erb file will be filled before rendering the output so you could do
<% if #result == "success" %>
console.log('like clicked')
$(this).closest('.like').hide();
$(this).closest('.like-container').html('<%= escape_javascript (render partial: 'unlike', recipe: #recipe ) %>');
<% else %>
console.log('something went wrong, this person has to many likes')
<% end %>
where #result is your controller variable
My link was calling the unlike method instead of the like method. Simple typo.
Original
<% recipe = recipe || #recipe %>
<%= link_to unlike_recipe_path(recipe), remote: true, class: "recipe-card__like like" do %>
<i class="heart icon"></i>
<% end %>
What it should be
<% recipe = recipe || #recipe %>
<%= link_to like_recipe_path(recipe), remote: true, class: "recipe-card__like like" do %>
<i class="heart icon"></i>
<% end %>
Related
I'm facing a weird bug with my ajax, where the like button is activating for all post in the view. I never had this issue with the first iteration of this feature. Its a simple two state like button that hides the filled heart with the empty heart if the current user isn't liking the post. The code that I have is written below. For the buttons themselves, I'm using a simple css class/png background url.
stories_controller.rb
def like
#story = Story.friendly.find(params[:id])
if !current_user.liked? #story
#story.liked_by current_user
elsif current_user.liked? #story
#story.unliked_by current_user
end
respond_to do |format|
format.js {render :nothing => true }
end
end
stories\like.js.erb
<% if current_user.liked? #story %>
$('#like-story-id').removeClass('story-heart-btn').addClass('story-heart-btn-active');
<% else %>
$('#like-story-id').addClass('story-heart-btn-inactive');
<% end %>
$('.likes-story-count').html("<%= #story.get_upvotes.size %>");
_story.html.erb
<ul id="left-story-footer-list">
<li class="story-votes" id="#story_<%= story.id %>">
<%= link_to like_story_path(story), style: 'text-decoration: none', class: 'like-story-btn', method: :put, remote: true do %>
<div class="story-heart-btn" id="like-story-id"></div>
<% end %>
</li>
<li><p class="story-card-text-format likes-story-count"><%= number_with_delimiter(story.get_likes.size) %></p></li>
</ul>
routes.rb
resources :stories do
member do
put 'like', to: 'stories#like'
get 'like', to: 'stories#like'
end
end
Doesn't this like-story-id should also be a unique inside ul?
<div class="story-heart-btn" id="like-story-id"></div>
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
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 have a rails app, which is constructed by three parts such as navigation above, main content, and side menu.
I already implemented follow un-follow button in my main content.
It's working with Ajax perfectly so far.
basically if I press follow button, follow action in users_controller.rb will be called and changes follow flag, and then it calls follow.js to re-render follow button as partial.
Beside that, in side menu, it has the number of people whom current_user is following.
This number also should be refreshed right after follow action was executed.
To refresh more than 2 partials at once. How can I archive this?
controllers/users_controller.rb
....
def follow
#user = User.find(params[:id])
current_user.follow(#user)
respond_to do |format|
format.js {render :action=>"follow.js"}
end
end
def unfollow
#user = User.find(params[:id])
current_user.stop_following(#user)
respond_to do |format|
format.js {render :action=>"unfollow.js"}
end
end
...
views/users/follow.js.erb
$('.follow_user[data-user-id="<%=#user.id%>"]').html('<%= escape_javascript(render :partial => "users/follow_user", :locals => {:user => #user}) %>');
views/users/unfollow.js.erb
$('.follow_user[data-user-id="<%=#user.id%>"]').html('<%= escape_javascript(render :partial => "users/follow_user", :locals => {:user => #user}) %>');
views/layouts/_menu.html.erb
<% if user_signed_in? %>
<li><%= link_to(following_user_path(current_user.username)) do %>
<i class="icon-heart"></i>Following
<%= '(' + current_user.all_following.count.to_s + ')' if current_user.all_following.count > 0 %>
<% end %>
</li>
<li><%= link_to(followed_user_path(current_user.username)) do %>
<i class="icon-heart"></i>Followed by
<%= '(' + current_user.followers.count.to_s + ')' if current_user.followers.count > 0 %>
<% end %>
</li>
<% else %>
<li><%= link_to sanitize('<i class="icon-heart"></i> ') + "Following", following_user_path %></li>
<li><%= link_to sanitize('<i class="icon-heart"></i> ') + "Followed by", followed_user_path %></li>
<% end %>
# dedicate partial _following_number.html.erb
<span id="following_number">
<% if user.followers.count > 0 %>
<%= '(' + user.followers.count.to_s + ')' %>
<% end %>
</span>
# update layout with partial:
<li><%= link_to(followed_user_path(current_user.username)) do %>
<i class="icon-heart"></i>Followed by
<%= render :partial => "following_number", :user => current_user %>
<% end %>
# follow.js.erb && unfollow.js.erb:
...
$(document).find("#following_number").replaceWith('<%= escape_javascript(render("following_number", :user => #user, :formats => [:html])) %>')
More elegant with helper:
def following_number(user)
user.followers.count > 0 ? "(#{user.followers.count})" : nil
end
# then put spans to layout:
<span id="following_number"><%= following_number(current_user) %></span>
# and js:
$(document).find("#following_number").html('<%= escape_javascript(following_number(#user)) %>')
You could create a new action that render_to_string both of those partials and format them into a json object:
{
div_id_1: "<ul....." ,
div_id_2: "<div...."
}
One simple way of doing this is to return a .js from rails and have your view execute this script, which will have the actions to update/replace your view.
For example this simply renders three partials and later using jQuery replaces the content in your view. Lets say the action is my_action so you will place the following code in your my_action.js.erb:
$("#content_1").html("<%= escape_javascript(render('partial_1'))%>");
$("#content_2").html("<%= escape_javascript(render('partial_2'))%>");
$("#content_3").html("<%= escape_javascript(render('partial_3'))%>");
This example can be extended the way you want or need. You can also render json for your models and call a function to update them in the view.
So I have the following form:
<%= form_tag url_for(:controller => 'profile', :action => 'follow_topic'), :remote => true do %>
<%= hidden_field_tag :topic_id, topic_id %>
<%= content_tag :button, :class => 'link', :onclick => "javascript:document.getElementById('followtopic#{Topic.find(topic_id).identifier}').innerHTML='Following...'" do %> Follow <% end %>
<% end %>
and I'm trying to get the controller to process it as JS in place of HTML. The funny thing is I have a form exactly like this in another spot in the app that seems to work fine, and the controller definitions are the same. Can't quite figure out the problem. Any ideas on what I should be checking first?
def follow_topic
#topic = Topic.find(params[:topic_id])
current_user.follows << #topic
respond_to do |format|
format.js
end
end
You got it right except your format.js isn't doing anything. What are you expecting in your form submit? and what are you expecting in return? a json or http response 200?
specify that in your format.js like so:
...
respond_to do |format|
format.js { render :nothing => true, :response => :ok if current_user.follows << #topic }
end
...
or something to that effect.
This question is OLD but it's unanswered and I just struggled with the same problem for hours, so hopefully this will help someone in future.
Make sure app/assets/javascripts/application.js contains:
//= require jquery
//= require jquery_ujs
And that erb contains <%= javascript_include_tag "jquery", "jquery_ujs" %> and not just a <script> tag.
This is what fixed it for me.