I'm tying to do an Ajax request on a delete method for active storage files so my page won't reload.
I have two controllers: 'project_steps' (i'm using wicked gem) and 'projects'.
My view: project_steps/fourth_step.html.erb
<% if #project.supporting_docs.attached? %>
<div id="remove_file">
<%= render partial: "existing_files", :locals => {project: #project} %>
</div>
<% end %>
My partial: project_steps/_existing_files.html.erb
<% #project.supporting_docs.each do |file| %>
blah blah
<%= link_to 'Remove', delete_file_attachment_project_url(file.signed_id),
method: :delete, remote: true, class: "btn btn-sm btn-danger" %>
<% end %>
My projects_controller:
def delete_file_attachment
file = ActiveStorage::Blob.find_signed(params[:id])
file.attachments.first.purge
respond_to do |format|
format.js
end
end
projects/delete_file_attachment.js.erb:
$('#remove_file').html("<%= j render(partial: 'project_steps/existing_files', :locals =>
{project: #project}) %>")
My Routes:
resources :projects do
member do
delete :delete_file_attachment
end
end
scope 'projects/:project_id' do
resources :project_steps
end
My Error
ActionView::Template::Error (undefined method `supporting_docs' for nil:NilClass):
3: <strong>You have attached the following files:</strong>
4: </div>
5: <br>
6: <% #project.supporting_docs.each do |file| %>
7: <div class="row">
8: <div class="col">
My delete works fine and I see why the error is there but i'm wondering how can I make Ajax work and what i'm doing wrong? Happy to provide as much code as needed! Ty.
P.S if anyone would like to suggest a solution other than going through partial you feel might be better by all means!
When using partials with locals, you access the variable without the #, i.e. to access the variable in project_steps/_existing_files.html.erb you need to use project instead of #project:
project_steps/_existing_files.html.erb
<% project.supporting_docs.each do |file| %>
blah blah
<%= link_to 'Remove', delete_file_attachment_project_url(file.signed_id),
method: :delete, remote: true, class: "btn btn-sm btn-danger" %>
<% end %>
Please also note that using #project in your projects/delete_file_attachment.js.erb doesn't work if you don't set the #project variable in the controller action delete_file_attachment, i.e. you'd probably need to add a line along #project = Project.find ... to your delete_file_attachment method.
See the Layouts and Rendering in Rails guide for more informations about using locals in partials.
Related
am having an issue related to ActiveRecord and am totally stuck in this.
basically am trying to call a controller method from a partial html.erb file. it's passing the username in id when I call it from partial and correct id when I call it from it's original HTML file.
wallets_controller:
before_action :find_wallet, only: %i[update]
def update
//some code ...
end
private
def find_wallet
// issue is here...
#wallet = Wallet.find(params[:id])
end
update.html.erb:
<%= f.fields_for :wallets do |wallet| %>
<%= render 'wallet_fields', f: wallet %>
<% end %>
_wallet_fields.html.erb:
<div class="flex-shrink-1">
<%= link_to update_nfts_wallet_path(wallet: f.object), remote: true, method: :post, class: 'btn btn-icon nfts-refresh-btn' do %>
<span class="material-icons-outlined">refresh</span>
<% end %>
</div>
it's throwing this error: ActiveRecord::RecordNotFound (Couldn't find Wallet with 'id'=hamid)
when add my Link_to update_nfts_wallet_path(wallet: f.object) from _wallet_fields.html.erb
and in controller I get actionController like this
and if I add this code in update.html.erb
<%= f.fields_for :wallets do |wallet| %>
<div class="flex-shrink-1">
<%= link_to update_nfts_wallet_path(wallet), remote: true, method: :post, class: 'btn btn-icon nfts-refresh-btn' do %>
<span class="material-icons-outlined">refresh</span>
<% end %>
</div>
<% end %>
then in controller, I get ActionController like this:
I need it the same as this when I call it from partial.
passing the same wallet from partial then why there is different parameters in ActionController. it's not finding the wallet because id have username not an id.
Change
update_nfts_wallet_path(wallet: f.object)
to
update_nfts_wallet_path(id: f.id)
I've got this working now quite accidentally, but I don't understand what causes it to break when I explicitly specify what partials are to be used for rendering the resource/s. Can anyone explain it?
The index template for my Posts controller contained the following line, which was giving me an error:
<%= render partial: 'posts', collection: #posts %>
The error (in my browser) said:
NoMethodError in Posts#index
Showing /Users/applebum/Sites/rails_projects/eventful2/app/views/posts/_posts.html.erb where line #1 raised:
undefined method `any?' for #<Post:0x000001064b21f0>
Extracted source (around line #1):
1: <% if posts.any? %>
2: <div id="posts">
3: <% posts.each do |post| %>
4: <%= render partial: "posts/post", locals: { post: post } %>
Changing the problem line to
<%= render #posts %>
made the error disappear and the posts appear (displayed nicely in markup from the appropriate partials) as I had wanted and expected them to.
Here's my _posts.html.erb partial:
<% if posts.any? %>
<div id="posts">
<% posts.each do |post| %>
<%= render partial: "posts/post", locals: { post: post } %>
<% # render :partial => "comments/comments", :collection => post.comments %>
<% end %>
</div>
<% end %>
And the _post.html.erb partial it's referring to, if that matters:
<div class="post" id="post_<%= "#{post.id}" %>">
<div class="post_inner">
<%= link_to avatar_for(post.user, size: "small"), post.user.profile %>
<div class="post_body">
<div class="user-tools">
<% if can? :destroy, post %>
<%= link_to '<i class="fi-x"></i>'.html_safe, post, :method => :delete, remote: true, :class => "delete", :confirm => "Are you sure you want to delete this post?", :title => post.content %>
<% end %>
</div>
<h5 class="username">
<%= link_to post.user.name, post.user.profile %>
<span class="timestamp">• <%= time_ago_in_words(post.created_at) %> ago</span>
</h5>
<div class="content">
<%= post.content %>
</div>
<ul class="foot">
<li>Like<li>
<li>Share</li>
</ul>
</div>
</div>
</div>
And the relevant bits from the controller:
class PostsController < ApplicationController
respond_to :html, :js # Allow for AJAX requests as well as HTML ones.
before_filter :load_postable
load_and_authorize_resource
def index
#post = Post.new
#posts = #postable.posts
end
private #################
def load_postable
klass = [User, Event].detect { |c| params["#{c.name.underscore}_id"] } # Look for which one of these there's a ***_id parameter name for
#postable = klass.find(params["#{klass.name.underscore}_id"]) # Call find on that, passing in that parameter. eg Event.find(1)
end
Can anyone explain to me what's going on here? I couldn't find anything in the Layouts and Rendering guide at rubyonrails.org.
Thanks!
Your error comes from assuming :collection and #posts mean the same thing when rendering. From Rails Docs (point 3.4.5):
Partials are very useful in rendering collections. When you pass a collection to a partial via the :collection option, the partial will be inserted once for each member in the collection
So, if you use that, for each post, you will be doing post.any? which fails as any? isn't defined for a single post.
From the same docs, you should check if render returns Nil to see if the collection is empty:
<h1>Posts</h1>
<%= render(#posts) || "There are no posts." %>
PD: Use the partial to render only one post, not all of them.
GL & HF.
I just finished Hartl's RoR tutorial and am now trying to mess around with some more stuff.
Specifically: I'm trying to allow the user to create microposts on any page, by rendering the micropost partial in the header.html.erb file (which is rendered on every page).
the partial:
<%= form_for(#micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "micropost" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
Doing this has resulted in the error on line #1 of the partial: undefined method 'model_name' for NilClass:Class on any page which I have not fixed by adding #micropost = current_user.microposts.build in the controller method that links to said view. For example:
#in controllers/static_pages_controller.rb
def about
if signed_in?
#micropost = current_user.microposts.build
end
end
Would fix this error when I visit the about page
I've been trying to figure out a way to do a "blanket fix" that will work on all pages without me having to paste in the declaration everywhere, any ideas?
I think you have SessionController, is created as follow guide in Tutorial, so you can make a helper method in SessionController, example:
def post_micropost
if signed_in?
#micropost = current_user.microposts.build
end
end
Then, in your StaticsController, add a before_filter at the top of controller:
before_filter :post_micropost
So, in any action of StaticPagesController, user can post micropost also if they are signed in.
You don't need to use the form_for builder here; Rails also provides a form_tag helper for more generic forms:
<%= form_tag create_micropost_path, method: :post do %>
<%= text_area_tag :micropost_content, placeholder: "micropost" %>
<%= submit_tag "Post", class: "btn btn-large btn-primary" %>
<% end %>
This way, you don't need to build the object when loading every page, but microposts#create can still pull data from params[:micropost]. See here for more info.
You can add this before the form
<% #micropost ||= current_user.microposts.new %>
I have almost done! but I have an issue, in my Controller file have this:
def show
#user = User.find(params[:id])
#posts = #user.posts.paginate(page: params[:page])
end
Then I have this piece of code in my file show.html.erb:
<div class="span8">
<%= render 'follow_form' if signed_in? %>
<% if #user.posts.any? %>
<h3>Microposts (<%= #user.posts.count %>)</h3>
<div id='posts'>
<div class='page'>
<ol class="microposts">
<%= render #posts %>
</ol>
</div>
</div>
<% end %>
</div>
At the bottom of this file, I have a Javascript code that I have taken from the tutorial: https://github.com/amatsuda/kaminari/wiki/How-To:-Create-Infinite-Scrolling-with-jQuery
In the same folder I have the file index.js.erb with:
$("#articles").append("<div class='page'><%= escape_javascript(render(#users)) %></div>");
$("#posts").append("<div class='page'><%= escape_javascript(render(#posts)) %></div>");
In a partial _posts.html.erb have this:
<div class='article'>
<%= image_tag(user.picture_url, :width => 50) %>
<%= link_to user.name, user %>
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
</div>
The first one already works in my file index.html.erb, the problem is with the second piece of code, when I try to render the partial at #post, It brings the follow log:
**
'nil' is not an ActiveModel-compatible object that returns a valid partial path.
Extracted source (around line #2):
1: $("#articles").append("<div class='page'><%= escape_javascript(render(#users)) %></div>");
2: $("#posts").append("<div class='page'><%= escape_javascript(render(#posts)) %></div>");
**
How can I render that partial?
Thanks a lot :D
I usually use the .js.erb template to render the partial into a var then set it on the page using JS. Be sure to escape_javascript the template content otherwise you will get JS errors.
<% template_html = render :partial => '_feed_item.html.erb' %>
<%="$('div#content').html('#{escape_javascript(template_html)}</div>');" %>
I think the above should work in erb. I use HAML so mine looks like:
-template_html = render :partial => 'partial_template'
!="$('div#content').html('#{escape_javascript(template_html)');"
Cheers.
Based on your In script I'm calling to /users?page=, and I was wondering that It may calls the line, you must make sure that your controller is populating the #posts variable if your view is using this. Can you show the controller method users?
I have a nav menu with 2 tabs/links in the show.html.erb file, in UsersController.rb, I would like to use ajax to render different partial for the tabs.
In the show.html.erb I have a div named profile-data where I want to show the content.
So I do something like this:
The link structure:
<li><%= link_to "College friends", college_friends_path, :remote => true %></li>
<li><%= link_to "Highschool friends", highschool_friends_path, :remote => true %></li>
I define the routes:
match "college_friends" => "users#college_friends", :as => "college_friends"
match "highschool_friends" => "users#highschool_friends, :as => "highschool_friends"
And I define in my UserController.rb the necessary methods:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def college_friends
respond_to do |format|
format.js
end
end
def highschool_friends
respond_to do |format|
format.js
end
end
end
Last thing we have the JS files:
*college_friends.js.erb*
$('#profile-data').html("<%= escape_javascript(render(:partial => 'college_friends')) %>");
*highschool_friends.js.erb*
$('#profile-data').html("<%= escape_javascript(render(:partial => 'highschool_friends')) %>");
The partial code: _college_friends.html.erb
<% groups = #user.friends.group_by(&:college_name) %>
<% sorted_groups = groups.sort_by{|key, values| values.count}.reverse %>
<% sorted_groups.each do |collegename, friends| %>
<% next if collegename.blank? %>
<div class="contentbox">
<div class="box-header">
<h3><%= collegename %></h3>
<div class="meta-info">
<p><i class="icon-map-marker"></i> Malmö</p>
<p><i class="icon-user"></i><span class="count"> <%= friends.count %></span> vänner</p>
</div>
</div>
<ul class="friends-list">
<% friends.map do |friend| %>
<li><%= image_tag(friend.image) %>
<% end %>
</ul>
</div>
<% end %>
So I solve this problem but checking the source in and out. And I notice that the js files was not loaded in the app.
So I changed this:
<%= javascript_include_tag :defaults %>
To this:
<%= javascript_include_tag "application" %>
And it loaded all the necessary js files.
But then I get this error:
ActionView::Template::Error (undefined method `friends' for nil:NilClass):
You are getting
ActionView::Template::Error (undefined method `friends' for nil:NilClass):
Because
<% groups = #user.friends.group_by(&:college_name) %>
requires #user to be set.
As Brandon suggested, try
def college_friends
#user = User.find(params[:id])
respond_to do |format|
format.js
end
end
BUT
To achieve this, you need to change your link routes to include user id, similar to this
match "college_friends/:id" => "users#college_friends", :as => "college_friends"
and in your links
<li><%= link_to "College friends", college_friends_path(#user), :remote => true %></li>
As suggested in the comments above, you should have two partials named _college_friends.html.erb and _highschool_friends.html.erb
These will contain your HTML, which is what you're wanting to load via $('#profile-data').html()
Template Error:
You are not defining #user in college_friends and highschool_friends. So your view is requesting #user from that action, which isn't there.