I realize there are dozens of similar questions here on this topic, I have looked through many of them and tried to duplicate the solutions and nothing has worked for me.
I have a list of tasks displayed in the index view as a partial, when the user adds a task I would like list to show the newest task (as well as all the others already there).
Here is the create controller:
def create
#task = current_user.tasks.build(task_params)
if #task.save
flash[:success] = "Task added"
respond_to do |format|
format.js
end
else
flash[:error] = "Task not added"
render 'new'
end
The _new partial:
<%= form_for(#task, method: :post, remote: true) do |f| %>
<%= f.label :comments %>
<%= f.text_field :comments, id: "task-comments" %>
<%= f.submit "Insert" %>
<% end %>
Index.html.erb
<div id="new-task-form">
<%= render 'new' %>
</div>
<div id="current-tasks">
<%= render partial: 'show_list', locals: {tasks: #tasks} %>
</div>
create.js.erb
console.log("create.js.erb called");
document.getElementById("task-comments").value = "";
document.getElementById("current-tasks").html('<%= j(render partial: "show_list") %>');
I have been able to get the console to log the message and even been able to clear the input box but the partial does not render, I have tried this in a few different ways and the script always seems to stop executing when it gets to the escape javascript line.
There are no JS errors, as stated above, the console logs the message when the "submit" button is pressed. There is a warning [Violation] Forced reflow while executing JavaScript took 114ms but I don't think that is relevant to this issue.
Here is the partial that I am trying to render _show_list.html.erb
<% unless #tasks.nil? %>
<ul>
<% tasks.each do |tsk| %>
<li><%= tsk.comments %></li>
<% end %>
</ul>
<% end %>
def create
#task = current_user.tasks.build(task_params)
if #task.save
flash[:success] = "Task added"
#respond_to do |format|
#format.js
#end
else
flash[:error] = "Task not added"
render 'new'
end
end
It's always good practice to use local variable in partial
1) => Index.html.erb
<div id="new-task-form">
<%= render 'new' %>
</div>
<div id="current-tasks">
<%= render partial: 'show_list', locals: {tasks: #tasks} %>
</div>
2) => If you are using jquery (create.js.erb)
console.log("create.js.erb called");
$("#task-comments").val('');
$("#current-tasks").html('<%= j render "show_list", tasks: #tasks) %>');
3) => _show_list.html.erb(Use tasks instead of #tasks)
<% unless tasks.blank? %>
<ul>
<% tasks.each do |tsk| %>
<li><%= tsk.comments %></li>
<% end %>
</ul>
<% end %>
ok you have some problem here with your validation on the show_list partial.
you have this line
<% unless #tasks.nil? %>
try to use something more readable like
<% if #tasks.present? %>
and on your controller create action, you don't fill the #tasks variable, so that's the problem in reality, add on you controller the load for all the tasks after saving the new one.
if #task.save
#tasks = Task.all
flash[:success] = "Task added"
and that will make it work, but I recommend to load just the created one and add just one column to the table and not update the complete list again if you have all the others on the client already.
Related
In my Rails 5 app, Doctor/Admin have to create a Caregiver with linked Patient (new object CaregiverPatient represents that). During this process, inside the registrants_controller#new, Doctor/Admin search for a Patient (which is done by Ransack gem) and from the result he/she push Add Patient button to add Patient to initialized CaregiverPatient object from caregiver_patient#new and display #patient.full_name inside the form.
In order not to lose already filled form fields, everything must be done asynchronously. I've tried to do so with AJAX, below my code:
registrant_controller.rb
def new
#registrant = Registrant.new
#patient = Registrant.find(session[:patient_id_to_add_caregiver]) if session[:patient_id_to_add_caregiver]
end
registrants/_new_form.html.erb
<%= form_for #registrant do |f| %>
<%= f.fields_for :registration do |registration| %>
<% if #patient %>
<div id="add-patient"></div>
<% else %>
<%= required_text_field_group registration, #registrant, :registrant_id, "Patient Added", {}, { disabled: true } %>
<% end %>
<% end %>
# some other logic (...)
# patient search result
<% #registrants do |registrant| %>
<%= link_to 'Add Patient', new_registrant_link_path(registrant), remote: true %>
<% end %>
Add Patient button triggers caregiver_patients#new action:
#caregiver_patient_controller.rb
class CaregiverPatientsController < ApplicationController
# new_registrant_link_path
def new
session[:patient_id_to_add_caregiver] = params[:registrant_id].to_i
respond_to do |format|
format.html { redirect_to request.referer }
format.js { render action: 'patient_to_caregiver', notice: 'Patient Added' }
end
end
end
AJAX request is made:
#views/caregiver_patients/patient_to_caregiver.js.erb
$("#add-patient").html("<%= escape_javascript(render :partial => 'registrants/add_patient') %>");
views/registrants/_add_patient.html.erb
Patient Added: <%= #patient.full_name %>
But surprisingly gets an error:
ActionView::Template::Error (undefined method `full_name' for nil:NilClass):
1: Patient Added: <%= #patient.full_name %>
Which probably means that registrants_controller#new was not refreshed under the hood somehow. What have I missed?
Trying to get a flash message and render a partial on an ajax call with link_to and remote:true
I have in my controller examples.rb:
def create
* Some stuff happens *
respond_to do |format|
format.js
format.html do
flash[:notice] = "Works!"
redirect_back(fallback_location: root_path)
end
end
end
I have the "link_to" form in _one_partial.html.erb
<%= link_to examples_path(whatever: locals[:whatever]), class: "whatever", method: :post, remote: true do %>
<div class="whatever_vix">
<p> Click Here! </p>
</div>
<% end %>
which is in a view as:
<div id="addhere">
<%= render 'favorites/one_partial', locals: {whatever: #whatever} %>
</div>
and in create.js.erb
$('#addhere').append("<%= j render 'whatever/another_partial' %>")
The ** some stuff happens ** part in my controller obviously works, but the new partial in create.js.erb doesn't show, neither the flash message appears once I add remote:true to the "link_to" form
My application has a Micropost model and a Comment model.
I introduced in the Micropost partial links for showing/hiding and refreshing comments:
<li id="micropost-<%= micropost.id %>">
<span class="user"><%= link_to micropost.user.name, micropost.user %></span>
<span class="content">
<%= micropost.content %>
<%= image_tag micropost.picture.url if micropost.picture? %>
</span>
<% if micropost.comments.any? %>
~ <%= link_to "Show/hide comments", "#", class: "comments-link", remote: true %>
~ <%= link_to "Refresh comments", fetch_comments_path(micropost), remote: true %>
<% end %>
<% if logged_in? && (current_user == micropost.user || current_user.friend?(micropost.user)) %>
<div class="comment-section">
<%= form_for(current_user.comments.build, remote: true) do |f| %>
...
<% end %>
</div>
<% end %>
<div class="comments-section">
<% if micropost.comments.any? %>
<ol id="comments_micropost-<%= micropost.id %>">
<% micropost.comments.each do |comment| %>
<%= render comment %>
<% end %>
</ol>
<% end %>
</div>
</li>
This links are added to the page after creating the first comment and are loaded if the micropost has comments.
However, in case there is just one comment and it is deleted, they are useless so I would like to remove them with Ajax via the destroy action of the comments controller.
I am having my troubles to realize this.
The main problem is that I cannot refer to the micropost using the to-be-destroyed comment.
Once the comment is destroyed any #micropost = #comment.micropost association would return a nil object:
def destroy
#comment = current_user.comments.find_by(id: params[:id])
#micropost = #comment.micropost
#comment.destroy
respond_to do |format|
format.html do
flash[:success] = "Comment deleted"
redirect_to request.referrer || root_url
end
format.js
end
end
Thus, inside create.js.erb I cannot use a if #micropost.comments.nil? remove links conditional
Moving #comment.destroy after the respond_to block would not work for format.html, without refreshing the page.
If it worked, I could use a if #micropost.comments.count == 1 remove links conditional in create.js.erb
I cannot move #comment.destroy inside the format.html block, because it would not be used by format.js.
What solution can I use?
The delete link in the comment partial is:
<%= link_to "delete", comment, method: :delete, remote: true %>
Is it possible to pass in link_to a params value equal to micropost.id, such as:
<%= link_to "delete", comment, method: :delete, micropost_id: micropost.id, remote: true %>
so that I can write in the destroy action:
#micropost = Micropost.find(params[:micropost_id]) ?
When you destroy your object it is destroyed form database and all reference keys are destroyed immediately. So you can first grab post Id and the remove comment.
def destroy
#micropost_id = #comment.micropost.id
#comment.destroy
end
Now you can use #micropost_id to take another action or to redirect to the parent post like this:
redirect_to '/microposts/'+ #micropost_id
Or
redirect_to MicroPost.find(micropost_id)
I'm trying to create a form, where I can upload an CSV file to import or to preview before import it.
In my form I have:
<%= form_for(#contact_import, :remote => true) do |f| %>
<% if #contact_import.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#contact_import.errors.count, "error") %> prohibited this import from completing:</h2>
<ul>
<% #contact_import.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.file_field :file %>
</div>
<%= f.submit "Import" %>
<%= f.submit "Preview", :name => 'preview' %>
and in my controller:
def create
#contact_import = ContactImport.new(params[:contact_import])
if params[:preview]
logger.debug "Let's preview the contacts:" + params.inspect
#contacts = #contact_import.update_preview
#contact_attributes = ContactImport.mapping_attributes
#I should now update the preview div
else
logger.debug("Got the commit" + params.inspect)
if #contact_import.save
redirect_to root_url, notice: "Imported contacts successfully."
else
render :new
end
end
end
How can I update the view to show the preview contacts, by uploading the CSV file?
Note: The CVS file processing at the moment is at the model and had been omitted.
I'd take them to another version of the new page, parse the file and fill the contact_import object - prepare the page with hidden variables to be submitted to the create page.
You can simply look for this button push and render the preview page, using the generated #contact_import generated from the file
def create
#contact_import = ContactImport.new(params[:contact_import])
if params[:preview]
render :preview
elsif #contact_import.save
redirect_to root_url, notice: "Imported contacts successfully."
else
render :new
end
end
preview.html.erb is similar to new.html.erb, but with hidden inputs and back button. Posting from preview will also go to create, but should not cause any error conditions.
I don't believe you'll need a new route - just render :preview instead of :new in this case.
Thanks for reading!
So I'm relatively new to RoR (Rails 5) and I try to render posts of a user in a partial (_posts_panel.html.erb) which contains another partial: the navigation (_posts_navigation.html.erb). I'm using ajax to show the posts when clicked on in the navigation which is working fine.
Now I want to use it for deleting a post. The post is destroyed when I click on the button and I managed to remove it in navigation.
What is not working:
the next post should be shown in the panel after the deletion
when there is no post left I want to reload the panel because another partial (_posts_empty.html.erb) should be rendered
My Code:
_posts_panel.html.erb: (it is rendered in show.html.erb of user)
<% if #user.simple_posts.empty? %>
<%= render 'simple_posts/shared/posts_empty' %>
<% else %>
<div id="post-navigation">
<%= render 'simple_posts/shared/posts_navigation' %>
</div>
<%= simple_posts_edit_button(current_user, #simple_post)%>
<%= simple_posts_delete_button(current_user, #simple_post)%> #button helper
<div id="show_post">
<%= render #simple_post %>
<%= yield %>
</div>
<%end %>
_posts_navigation.html.erb
<div class="bootstrap-vertical-nav">
<ul class="nav flex-column">
<% #user.simple_posts.reverse_each do |post|%>
<li class="nav-item" id="<%= #simple_post.id %>">
<%= link_to post.title, user_simple_post_path(#user, post), remote: true, class:"nav-link text-dark" %>
</li>
<% end %>
</ul>
</div>
<% if #user.id == current_user.id %>
<%= link_to "+ New Post", new_user_simple_post_path(current_user), class: "btn btn-info w-100", role: "button" %>
<% end %>
destroy.js.erb
$('li#<%= #simple_post.id %>').remove();
simple_posts_controller.rb
def destroy
#simple_post.destroy
#simple_post =current_user.simple_posts.first
respond_to do |format|
# format.html { redirect_to simple_posts_url, notice: 'Simple post was successfully destroyed.' }
format.js
end
end
I tried to append in destroy.js.erb the same code from showing the post $('#show_post').html("<%= escape_javascript render #simple_post %>"); but it's not working correctly. When deleting the first post (but only that one) the next one is shown but the post is not removed from the navigation. I also tried $('#post-navigation').html("<%= escape_javascript render partial: 'simple_posts/shared/posts_navigation' %>"); and it nothing happens.
Also I tried to reload the whole panel with $('#post-panel').html("<%= escape_javascript render partial: 'simple_posts/shared/posts_panel' %>");in show.html.erb of user but again: nothing. I'm not sure if I have to specify in which view or partial exactly it has to render the partial.
I don't really know how to approach this further.
Edit 1
ButtonHelper.rb
def simple_posts_delete_button(current_user, simple_post)
if(current_user.id == #user.id)
link_to svg('x-square'), [current_user, simple_post], remote: true, method: :delete, class:"btn"
end
end
Edit 2
show-action from simple_posts_controller.rb
def show
#user = User.find{params[:id]}
#simple_post = SimplePost.find(params[:id])
respond_to do |format|
format.js
format.html # show.html.erb
end
end
show.js.erb
$('#post-actions').html("<%= j render partial: 'simple_posts/shared/posts_actions', locals: {simple_post: #simple_post, user: #user} %>");
$('#show-post').html("<%= j render #simple_post %>");
There are several problems with your partials:
First, to remove the post from your navigation you just need to update the partial, the post is already destroyed/removed at this point.
After destroy, #simple_post in your button helper is not updated with ajax and still comes from the users_controller (where you set it the first time i presume)
You can put your action buttons inside a new partial and specify the locals.
You can also wrap the html inside a main div for the last update (when all the posts are deleted)
_posts_panel.html.erb:
<div id="panel">
<% if #user.simple_posts.empty? %>
<%= render 'simple_posts/shared/posts_empty' %>
<% else %>
<div id="post-navigation">
<%= render partial: 'simple_posts/shared/posts_navigation', locals: { simple_post: #simple_post } %>
</div>
<div id="post-actions"> #<-- new partial
<%= render partial: 'simple_posts/shared/posts_actions', locals: { simple_post: #simple_post } %>
</div>
<div id="show_post">
<%= render #simple_post %>
<%= yield %>
</div>
<% end %>
</div>
_posts_actions.html.erb:
<%= simple_posts_edit_button(current_user, simple_post)%>
<%= simple_posts_delete_button(current_user, simple_post)%>
Note that you are not using the instance variable #simple_post anymore but the local one.
Now you can write your js like this:
destroy.js.erb:
<% if #simple_post %> //<-- check if "#simple_post = current_user.posts.first" still exists after destroy
$('#post-navigation').html("<%= j render partial: 'simple_posts/shared/posts_navigation', locals: { simple_post: #simple_post } %>");
$('#post-actions').html("<%= j render partial: 'simple_posts/shared/posts_actions', locals: { simple_post: #simple_post } %>");
$('#show_post').html("<%= j render #simple_post %>");
<% else %>
$('#panel').html("<%= j render partial: 'simple_posts/shared/posts_empty' %>");
<% end %>