Rails: Ajax for Like button when iterating through status updates - javascript

In my Users index action I am iterating through all the Users who have posted a status update and ordering them by created_at DESC.
In my index.html.view
<% #users.each.do |user| %>
<% user.status = #status %>
<%= #status.content %>
<div id ="like_form">
<% if #status.has_like_already?(#status, current_user)%>
<%= render 'already_liked'%>
<%else%>
<%= render 'like'%>
<%end%>
</div>
_like.html.erb
<%= link_to "Like", likes_path(:like_for => #status.id), remote: true, :method=>:post %>
_already_liked.html.erb
<span>Liked</span>
create.js.erb
$("#like_form").html("<%= escape_javascript(render('users/already_liked')) %>")
The issue is that I am able to like a status update and for the first post the Like button changes to Unlike, but on subsequent clicks the like button doesn't change to Unlike. This is because #like_form for the first post has already been changed to Unlike. Since I am iterating, how do I get the like functionality to work with Ajax for each status posted?

You can add user id to div, so you can unique your div's id.
index.html.erb
<div id ="like_form_<%= user.id %>">
</div>
_like.html.erb
<%= link_to "Like", likes_path(:like_for => #status.id, :user_id=>user.id), remote: true, :method=>:post %>
create.js.erb
$("#like_form_<%= params[:user_id]%>").html("<%= escape_javascript(render('users/unlike')) %>")

Related

How to check policies for objects created through AJAX

In my program one Board can have many Sections. On Boards#show I list all Sections for this Board. A user can create, edit and delete Sections for Boards this user has created.
I use Pundit for my authorizations.
My problem is that after having added AJAX to the create Section action, my policy checks don't work anymore. The AJAX call is working fine in that Sections are added but the "Edit" and "Delete" links are only displayed after I reload the page.
Boards#show
<% if policy(#board).edit? %>
<p><strong>Create a new Section</strong></p>
<%= render 'sections/shared/form', board: #board, section: #section %>
<% end %>
<br>
<p><strong>Sections</strong></p>
<div id="sections">
<% #board.sections.order(id: :asc).each_with_index do |section, index| %>
<span><%= "#{index + 1}. #{section.title}" %></span>
<% if policy(section).edit? %>
<%= link_to "Edit", edit_board_section_path(#board, #board.sections[index]) %>
<% end %>
<% if policy(section).destroy? %>
<%= link_to("Delete", board_section_path(#board, #board.sections[index]), method: :delete) %>
<% end %>
<br>
<% end %>
</div>
Form partial
<%= simple_form_for([#board, #section], remote: true, authenticity_token: true) do |f| %>
<%= f.input :title, id: "section_title" %>
<%= f.submit "Save" %>
<% end %>
AJAX call
var newSection = "<span><%= "#{#board.sections.length}. #{#section.title}" %></span><br>";
var sections = document.getElementById('sections');
sections.insertAdjacentHTML('beforeend', newSection);
var sectionTitle = document.getElementById('section_title');
sectionTitle.value = '';
I think the problem is that the newly created Section isn't inserted into the loop and thus the policy cannot be checked. How could I refactor my code to solve this?
You should be able to render you HTML templates into your js.erb file. See the post from the DHH https://signalvnoise.com/posts/3697-server-generated-javascript-responses how to use this properly. It is an older post so maybe some things changed but you should know what to search for. But if it still works this way, you should be able to do something like this:
Extract section to a partial _section.html.erb
<span><%= "#{index + 1}. #{section.title}" %></span>
<% if policy(section).edit? %>
<%= link_to "Edit", edit_board_section_path(#board, #board.sections[index]) %>
<% end %>
<% if policy(section).destroy? %>
<%= link_to("Delete", board_section_path(#board, #board.sections[index]), method: :delete) %>
<% end %>
<br>
Use this partial also in your Boards#show file.
Render your partial in the js.erb file
var newSection = "<span><%=j render #section %></span><br>";
Something like this should work, but I don't use server generated javascripts so they could have changed some things. But hopefully it will help you at least with the direction.

Rails/JS button onclick to re-render partial without entering into DB

I wish to reload a partial-form with a button(Add). I'm new and don't know how to simply display fields in partial like listings one under the other as many times Add button is clicked. I can't find a relevant example. all AJAX examples mention js.erb when object is saved in DB.
<div class="panel-body">
<%= render partial: "degrees/form", :locals => { :f => f } %>
<%= f.link_to (fa_icon 'plus').to_s + " Add Another Qualification ", render(:partial => 'degrees/form', :locals => { :f => f }), class: "btn btn-primary" %>
</div>
Here, #application is the main form trying to display degree fields. Partial is simply two text-fields- one for selecting educational degree and second its detail.Here's partial
<%= f.fields_for [Degree.new], :url => { :action => "index" } do |ff| %>
<div class = "form-group" }>
<%= ff.select :level, options_for_select(Job::EDUCATION, params[:level]), include_blank: "Select Degree", class: 'span-2' %>
<%= ff.text_field :description, :class => 'span5' %>
</div>
<% ff.submit "Add Another Degree", class: 'btn btn-primary' %>
You don't necessary need to save things to pass away to .js.erb... you can just instantiate...
But if your example is precise, you are missing the remote: true flag for the link... And the partial is not defined on the link... you need to make a view responding to the ajax...
Form example
<div class="panel-body">
<%= link_to new_thing_path, remote: true do %>
<i class="fa fa-plus">
Add another qualification
<% end %>
<div class="new-things-container">
</div>
</div>
Controller answering to the ajax request
class ThingsController < ApplicationController
def new
#thing = Thing.new
respond_with #thing
end
end
View for the ajax request rendering a partial inside a specified div
//views/things/new.js.erb
$('.panel-body .new-things-controller').append("<%= render partial: 'degrees/form', locals: { thing: #thing } %>")

Passing specific data on partials using Ajax in Rails

I'm new to rails and what I'm trying to do is the following:
I'm creating a store and unstore button to save and 'un-save' the event respectively. I used my event attributes and current user to find that stored event. By using ajax and remote functionality of rails I will able to change the behavior of the button from store to unstore or vise versa.
In my _feed.html.erb.
<% if #feed_items.any? %>
<ul>
<%= render partial: 'shared/feed_item', collection: #feed_items %>
</ul>
<% end %>
Each #feed_items contains set of data (title, category, etc).
In my _feed_item.html.erb.
<li id="<%= feed_item.id %>" class="item">
...
<div class="small-3 text-center columns">
<%= render 'shared/store_form', event: feed_item %>
</div>
...
</li>
Event symbol sends that feed_item to the store_form.
In my _store_form.html.erb.
<div id="store_form_<%= event.id %>">
<% if event.stored?(current_user) %>
<%= render 'shared/unstore', event: event %>
<% else %>
<%= render 'shared/store', event: event %>
<% end %>
</div>
I pass again a feed_item into partial.
_store.html.erb
<%= form_for(event.storages.build(saver_id: current_user.id,
organizer_id: event.user_id),
remote: true) do |f| %>
<div>
<%= f.hidden_field :organizer_id %>
<%= f.hidden_field :saved_id, value: event.id %>
</div>
<%= f.submit "store" %>
<% end %>
_unstore.html.erb
<%= form_for(event.storages.find_by(saver_id: current_user.id,
organizer_id: event.user_id) ,
html: { method: :delete },
remote: true) do |f| %>
<%= f.submit 'unstore' %>
<% end %>
create.js.erb
$("#store_form").html("<%= escape_javascript(render 'shared/store', event: event )%>")
destroy.js.erb
$("#store_form").html("<%= escape_javascript(render 'shared/unstore', event: event) %>")
And I'm getting these errors when clicking store/unstore event.
ActionView::Template::Error (undefined local variable or method `event' for #<#<Class:0x00000004d69be8>:0x00000004d68d10>):
1: $("#store_form").html("<%= escape_javascript(render 'shared/unstore', event: event) %>")
ActionView::Template::Error (undefined local variable or method `event' for #<#<Class:0x00000004d69be8>:0x00000004fdc6a0>):
1: $("#store_form").html("<%= escape_javascript(render 'shared/store', event: event )%>")
My question is, how can I pass that specific "event" data into the partial. I can't used #feed_item onto the create and destroy js because feed_item have the set of events. Is there any better approach to handle this?
In your action create and delete in the controller you should instantiate your variable event: change event by #event and change your js to:
$("#store_form").html("<%= escape_javascript(render 'shared/unstore') %>
because you donĀ“t need add instantiate vars to render calls, rails is doing for you

Rails 3.2 - form erroneously submitted multiple times

I have the model Box, and each Box has many box_videos (another model). I want the user to be able to add box_videos to the box, so I created the following edit form for that (after creation of #box):
<%= form_tag "/box_videos", { method: :post, id: "new_box_videos", remote: true } do %>
<%= text_field_tag "box_videos[][link]", '' %>
<%= text_area_tag "box_videos[][description]", '' %>
<%= hidden_field_tag("box_videos[][box_id]", #box.id) %>
<%= hidden_field_tag("box_videos[][user_id]", current_user.id) %>
<div class="another_video">Add Another Video</div>
<%= submit_tag "Save Videos" %>
<% end %>
<%= form_for(#box) do |f| %>
<%= f.text_field :name %>
<%= f.text_field :size %>
<%= f.text_field :all_other_attributes %>
<%= f.submit "Create Box" %>
<% end %>
And some Javascript to facilitate adding more box_videos in one click.
<script>
$('.another_video').click(function() {
$('#new_box_videos').prepend('<input id="box_videos_link" name="box_videos[][link]" placeholder="Link to a youtube video." style="width: 18em;" type="text" value=""><textarea id="box_videos_description" name="box_videos[][description]" placeholder="Describe this video." style="width: 18em;"></textarea><br/><br/><input id="box_videos_box_id" name="box_videos[][box_id]" type="hidden" value="' + gon.box_id.toString() + '"><input id="box_videos_user_id" name="box_videos[][user_id]" type="hidden" value="' + gon.user_id.toString() + '">');
});
</script>
The above code works, in that params[:box_videos] when submitting three box_videos is as follows:
[{"link"=>"https://www.youtube.com/watch?feature=player_detailpage&v=dpAP8bq3ddU
", "description"=>"foo", "box_id"=>"63", "user_id"=>"16"}, {"link"=>"https
://www.youtube.com/watch?feature=player_detailpage&v=dpAP8bq3ddU", "description"
=>"bar", "box_id"=>"63", "user_id"=>"16"}, {"link"=>"https://www.you
tube.com/watch?feature=player_detailpage&v=dpAP8bq3ddU", "description"=>"hello world",
"box_id"=>"63", "user_id"=>"16"}]
In my controller I would just create a box_video object for every hash in the array and it works out just fine. BUT the problem comes when each time I submit the nested form_tag form, I send multiple requests to the controller action! Which means there are duplicates being created.
I can think of adding logic to the box_videos controller create action to check for duplicate content, but it seems rather hacky. Can anyone please let me know why this is happening?
You can't have nested form elements according to the html spec (see this answer).
You might want to use nested forms for this, it provides the creation of associated models via jquery with useful form_helper wrappers.

Clear a text_field after create w/o refresh

Right now I'm using AJAX for creating comments. However when I hit enter the textfield remains populated. I want the field to refresh (and still be able to write in another comment). no errors but it still doesn't refresh the textfield it seems. The create part works fine.
create.js/erb: (need to fix the second line here so that it fully replaces)
$('#<%= dom_id(#micropost) %>').html("<%= escape_javascript(render(:partial => #micropost.comments))%>")
$("#comment_field_<%=#micropost.id%>").replaceWith("<%= escape_javascript(render 'shared/comment_form', micropost: #micropost) %>")
Microposts/_micropost:
<span id="<%= dom_id(micropost) %>"><%= render micropost.comments %></span>
<span id="comment_field_<%=micropost.id%>"><%= render 'shared/comment_form', micropost: micropost if signed_in? %></span>
Shared/Comment_form:
<%= form_for #comment, id:"comment_form", remote: true do |f| %>
<%= hidden_field_tag :micropost_id, micropost.id %>
<div id="comment_field">
<%= link_to gravatar_for((current_user), size: 29) %>
<%= f.text_field :content, placeholder: "Say Something...", id: "comment_text_field", :style => "width: 508px; text-indent: 5px" %>
</div>
<% end %>
Needed to only use jquery to clear the text_field by setting empty value
create.js.erb
$("#comment_text_field_<%=#micropost.id%>").val("")
best practice to use a unique dynamic ID but probably would have worked either way.
Shared/Comment_form:
<%= f.text_field :content, id: "comment_text_field_#{micropost.id}" %>

Categories

Resources