I currently have the following snippets of code:
<tbody>
<% #requests.each do |request| %>
<% #user = User.where(id: request[:student_id_id]).first %>
<tr>
<td><%= #user.last_name.titleize %></td>
<td><%= #user.first_name.titleize %></td>
<td><%= #user.preferred_name.titleize %></td>
<td><%= render :partial => 'documents/resumelink' %></td>
<td><%= mail_to #user.email.downcase %></td>
<% if request.allowed_companies.nil? || request.allowed_companies.empty? %>
<td> <a class="no_selected_companies_button">No Restrictions</a></td>
<% else %>
<% #allowed_request = AllowedRequest.where(professor_id_id: #current_user[:id], student_id_id: #user[:id]).first %>
<td><%= link_to("View Companies", allowed_request_path(#allowed_request) , class: "selected_companies_button") %></td>
<% end %>
</tr>
<% end %>
</tbody>
in a partial view. The documents/_resumelink partial looks like this:
<% if (#document != nil) && (#document.resume_file_name != nil) %>
<%= link_to 'View Resume', #document.resume.url %>
<% else %>
<%= link_to 'No Resume available', '#' %>
<% end %>
In the documents_controller I have it so it will before_action set_document (with no exceptions). That definition looks like:
def set_document(user=#current_user)
#document = Document.find_by owner_user_id_id: user[:id]
end
So, what I want to be able to do is pass the #user variable to my set_document function in the render: partial => 'documents/resumelink' line in the first view and have it display a link to the resume (if there is one).
Any ideas?
I think your issue is more systemic than specific; and as such I'd like to give some ideas
Controller
When you use before_action, it runs before your Rails action is run
This means when you send a request to Rails, it loads the before_action filter, and then the relevant action that you've requested. Partials do not form part of this process:
This means if you want to make various variables available to different parts of your application, you need to appreciate that it works as follows:
Request -> Router -> Controller#Action -> View -> Partial
To make use of the appropriate data, you need to ensure it's defined at the correctpart of the above process. The problem you have is you're not doing that -- how can you make the #user variable available to a method before the #user variable has been defined?
This is not a specific issue for you now; but will be something you will need to keep in mind for next time.
Partial
If you're looking to include data in a partial, you will need to use the locals argument for the render method:
<%= render partial: "documents/resumelink", locals: { your_local_var: #value } %>
--
So, what I want to be able to do is pass the #user variable to my set_document function in the render
As per my simple flow chart above, your partial will not invoke the set_document method, as a partial is literally just an extension of your view
You'll need to have the #user variable set in the controller (I set the ActiveRecord association for you) and then pass it as a local to your partial:
#app/models/request.rb
Class Request < ActiveRecord::Base
has_many :users, foreign_key: "student_id_id"
end
#app/models/user.rb
Class User < ActiveRecord::Base
belongs_to :request, foreign_key: "student_id_id"
end
#app/controllers/documents_conroller.rb
Class DocumentsController < ApplicationController
before_action :set_document
def index
#requests = Request.all
end
private
def set_document(user=#current_user)
#document = Document.find_by owner_user_id_id: user[:id]
end
end
This will allow you to call the following:
#app/views/requests/index.html.erb
<% #requests.each do |request| %>
<% user = request.users.first %>
<%= render partial: "documents/resumelink", locals: {user: user} %>
<% end %>
You can do this way
<%= render :partial => 'documents/resumelink', :locals => {user => #user} %>
A variable user will be accessible in the partial.
Another way:
Variable #user is accessible without any changes in your code, because it is instance variable (not local)
If I understand what you're trying to do, I would do it differently.
Assigning variables (especially instance vars) in views is not a good practice, making DB queries from views is also quite bad.
What you can do instead is:
#requests = Request.where(your_conditions).includes(users: :documents)
and then i a view you can do
#requests.each do |request|
request.user.email
render partial: 'documents/resumelink', document: request.user.document
(actually you can do it already but solution proposed above will save you a lot of SQL queries)
Related
I'm currently having the issue for when I try to use a partial inside of invoices/_form.html.erb, it goes into parts/_index.html.erb and breaks.
Inside of the parts_controller I have:
def _index
#parts = Part.all
end
#unsure if this is needed
Inside of invoices_controller I have:`
def new
#invoice = Invoice.new
#parts = Part.all
end`
Inside of invoices/_form.html.erb I have:
<%= render :partial => "parts/index" , :part => #parts %>
And inside of invoices/new.html.erb I have:
<h1 style="padding-left:120px">New Invoice</h1>
<%= render 'form', invoice: #invoice, part: #parts %>
<%= link_to 'Back', invoices_path, class: "btn btn-default col-md-2" %>
So what this code is attempting to do is display the index page of parts so the user is able to see all current parts they have in stock, and how many of that part is in stock. The parts/index page is the exact same as the the default index page for parts, but it just has a link removed.
The line of code that gives me an issue in parts/index is:
<% #parts.each do |part| %>
And what's confusing me about that is that I should be passing it an object that has data inside of it, since it's declared in both the controller for parts, and the invoice controller. Am I missing something super simple with my syntax, or is what I'm trying to do not the right way to do it? I'm still a noob to rails, so sorry if what I'm trying to get across doesn't make too much sense.
so here is the problem:
<%= render :partial => "parts/index" , :part => #parts %>
you are sending :part to your _index.html.erb partial while using #parts
you need to update your render call to following:
<%= render :partial => "parts/index" , locals: {parts: #parts}%>
and your loop to:
<% parts.each do |part| %>
you provide to _index.html.erb variable part but try to render #parts.
1. you don't need method _index, when your patial _index.html.erb render that not get variable #parts from method _index. I think it's wrong.
2. You need to render in _index.html.erb variable which it's provided from _form
<% part.each do |part_| %>
I'm trying to loop over my 'offers' collection in a partial, but each 'offer' has a column 'featured' which is a boolean which defaults to false. I'm trying to loop over the collection and only display the offers which have the featured column set to true.
I currently have:
<%= render #offers %>
Trying below but comes back with 'undefined method 'featured'
<%= render #offers if #offer.featured == true %>
Any help would be fantastic
In your controller, set up another collection:
#featured_offers = Offer.where(featured: true)
And render that instead:
<%= render #featured_offers %>
To correct your immediate code, you're calling .featured on #offer - which doesn't exist.
You'll either need to loop through #offers and use logic on offer, or use conditions inside the partial (which is highly inefficient):
<% #offers.each do |offer| %>
<%= render offer if offer.featured %>
<% end %>
or
<%= render #offers %>
#_offer.html.erb
<% if offer.featured %>
This is super inefficient
<% end %>
--
#jason is correct with his recommendation of using a where clause
You may even want to go a step further and set up a scope:
#app/models/offer.rb
class Offer < ActiveRecord::Base
scope :featured, -> { where featured: true }
end
#offers = Offer.featured
You can even chain the scope:
#offers = Offer.where(user_id: params[:id])
<%= render #offers.featured %>
I use Devise gem for authentication.
In database I have users table and posts table in my database schema (and Post controller).
In post controller I want to find all posts assigned to specific user. I have user_id in posts table.
How to get all user's posts or how to check if specific post is assigned for SIGNED IN user.
I thought about something like this (of course is only pseudocode:
current_user.id == Post.where(params:[post_id]).user_id
So how to get current user id in Devise and how to check the current user id is the same like eg. user_id assigned to viewing post (I want to add 'edit' function when current user is post owner) and how to find all post which current user is owner.
Associations
Firstly, your user_id column in your posts table is what's known as a foreign_key
Foreign keys are used in relational database systems to give you the ability to call associative data from a single record. Simply, it means that you'll be able to use the ActiveRecord associations to call the data you require, rather than having to call it individually:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :posts
end
#app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
end
This will give you the ability to use the following call:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
#posts = current_user.posts
end
end
You'll be best served looking up the has_many association:
Fix
In regards to showing your posts for your users, you need to be sure that you have the correct "flow" set up. What I mean is you need some condition to know whether your user is signed in & that #posts is set:
#app/views/posts/index.html.erb
<% if #posts.present? %>
<% #posts.each do |post| %>
<%= post.title %>
<% end %>
<% end %>
Maybe this is the first time you use Devise. You can access current_user inside controllers or views. I imagine you could do something like this
In controller (posts_controller.rb):
#posts = current_user.posts
In view (posts/show.html.erb, I guess):
if current_user.id = #post.current_user
#render something here
end
Get all post which current user is owner.
#posts = Post.where(:user_id => current_user.id)
and on your view
<%-# commented : checking if #posts is empty -%>
<% if #posts.empty? %>
<span>Sorry, post is empty </span>
<% else %>
<%= #posts.each do |p| %>
<% if p.user_id == current_user.id %>
<% link_to "edit", edit_path(p) %>
<% end %>
<% end %>
<% end %>
There are many ways you could get current_user posts. I'll go the long way.
we need
an action
an action view and a partial
a route
a link_to
* action *
def my_posts
#posts = current_user.posts.all.order(created_at: 'DESC')
end
* view *
my_posts.html.erb
<% if #posts.present? %>
<%= render 'posts' posts: #posts %>
<% else %>
<h1>You don't have any posts yet! create one</h1>
<% end %>
_posts.html.erb
<%posts.each do |post| %>
<%= post.title %>
<% end %>
index.html.erb
<%= render 'posts' posts: #posts %>
route
get 'post' => 'posts#my_posts', as: :my_posts
link_to
<%= link_to 'My posts', my_posts_path %>
I may be late but someone can find it useful :)
I have a partial that needs to have some controller logic run before it can render without issue. Is there some way to associate the partial with some controller logic that is run whenever it is rendered?
For example, this is what my current code looks like:
MyDataController:
class MyDataController < ApplicationController
def view
#obj = MyData.find(params[:id])
run_logic_for_partial
end
def some_method_i_dont_know_about
#obj = MyData.find(params[:id])
# Doesn't call run_logic_for_partial
end
def run_logic_for_partial
#important_hash = {}
for item in #obj.internal_array
#important_hash[item] = "Important value"
end
end
end
view.html.erb:
Name: <%= #obj.name %>
Date: <%= #obj.date %>
<%= render :partial => "my_partial" %>
some_method_i_dont_know_about.html.erb:
Name: <%= #obj.name %>
User: <%= #obj.user %>
<%# This will fail because #important_hash isn't initialized %>
<%= render :partial => "my_partial" %>
_my_partial.html.erb:
<% for item in #obj.internal_array %>
<%= item.to_s %>: <%= #important_hash[item] %>
<% end %>
How can I make sure that run_logic_for_partial is called whenever _my_partial.html.erb is rendered, even if the method isn't explicitly called from the controller? If I can't, are there any common patterns used in Rails to deal with these kinds of situations?
You should be using a views helper for this sort of logic. If you generated your resource using rails generate, a helper file for your resource should already be in your app/helpers directory. Otherwise, you can create it yourself:
# app/helpers/my_data.rb
module MyDataHelper
def run_logic_for_partial(obj)
important_hash = {}
for item in obj.internal_array
important_hash[item] = "Important value" // you'll need to modify this keying to suit your purposes
end
important_hash
end
end
Then, in your partial, pass the object you want to operate on to your helper:
# _my_partial.html.erb
<% important_hash = run_logic_for_partial(#obj) %>
<% for item in important_hash %>
<%= item.to_s %>: <%= important_hash[item] %>
<% end %>
Or:
# app/helpers/my_data.rb
module MyDataHelper
def run_logic_for_partial(item)
# Do your logic
"Important value"
end
end
# _my_partial.html.erb
<% for item in #obj.internal_array %>
<%= item.to_s %>: <%= run_logic_for_partial(item) %>
<% end %>
EDIT:
As commented Ian Kennedy points out, this logic can also reasonably be abstracted into a convenience method in your model:
# app/models/obj.rb
def important_hash
hash = {}
for item in internal_array
important_hash[item] = "Important value"
end
hash
end
Then, you'd access the important_hash attribute in the following manner in your partial:
# _my_partial.html.erb
<% for item in #obj.important_hash %>
<%= item.to_s %>: <%= item %>
<% end %>
What you're trying to do runs against the grain of how Rails controllers/views are designed to be used. It would be better to structure things a bit differently. Why not put run_logic_for_partial into a helper, and make it take an argument (rather than implicitly working on #obj)?
To see an example of a view "helper", look here: http://guides.rubyonrails.org/getting_started.html#view-helpers
My question is: why doesn't .becomes pass errors over to the new object? Isn't this the expected behaviour?
I have the following single table inheritance classes in a rails app:
class Document < ActiveRecord::Base
validates :title, :presence => true
end
class LegalDocument < Document
end
class MarketingDocument < Document
end
I want to use the same controller and set of views to edit both LegalDocuments and MarketingDocuments, so I am using DocumentsController < ApplicationController with the following edit and update actions:
def edit
#document = Document.find(params[:id])
end
def update
#document = Document.find(params[:id])
if #document.update_attributes(params[:document])
redirect_to documents_path, :notice => "#{t(:Document)} was successfully updated."
else
render :action => "edit"
end
end
and the following in my edit view:
<%= form_for #document.becomes(Document) do |f| %>
<% if f.object.errors.present? %>
<div class="error_message">
<h4><%= pluralize(f.object.errors.count, 'error') %> occurred</h4>
</div>
<% end %>
<div>
<%= f.label :title %>
<%= f.text_field :title, :class => "inputText" %>
</div>
<%= f.submit %>
<% end %>
If title is filled in, the documents update correctly.
If title is left blank, I am returned to the edit view BUT the error is not shown.
From debugging, I know it's not showing because f.object.errors is nil. However, from debugging, I also know #document.errors is NOT nil, as expected.
My question is: why doesn't .becomes pass errors over to the new object? Isn't this the expected behaviour?
Yes, I noticed that too.
Just change f.object.errors.present? by #document.errors.any? ( or #document.errors.present?).
If you really want to use f.object.errors.present?, write becomes in the controller (both edit and update actions) instead of in the view:
def edit
#document = Document.find(params[:id]).becomes(Document)
end
def update
#document = Document.find(params[:id]).becomes(Document)
# ....
end
And then in the view:
<%= form_for #document do |f| %>
<% if f.object.errors.present? %>
<p>Errrorsss....</p>
<% end %>
#.....
It happens because the url of the form is build according to #document.becomes(Document) (=> PUT document/:id) but #document is created according to its "true" class (a subclass of Document).
If you have pry (highly recommended), write:
def update
#document = Document.find(params[:id])
binding.pry
# ...
end
And then inspect #document. You will see that #document is an instance of LegalDocument or the other subclass even though you called #document.becomes(Document) in your form.
So in final f.object and #document are not the same.
This explains why you can't see f.object.errors when validation fails.
Edit
The 'best way' to deal with STI and form is NOT to use becomes:
<= form_for #document, url: { controller: 'documents', action: 'update' }, as: :document do |f| %>
<% if #document.errors.any? %>
# or if f.object.errors.any?
# handle validation errors
<% end %>
# your form...
<% end %>
This enables you:
to have only one controller (documents_controller)
to have only one resource (resources :documents)
it keeps trace of your subclasses: a LegalDocument will be store as a LegalDocument. No conversion: You don't have to store its class before the conversion to Document and then reassign it later.
Plus, your subclass is available in your form, so you can (let's imagine) build a select for the type.
your controller looks cleaner: #document = Document.find params[:id] nothing more. Just like a classic resource.
If you want to share this form across different actions(typically edit and new):
<%= form_for #document, url: { controller: 'media_files', action: action }, as: :media_file do |f| %>%>
# edit.html.erb
<%= render 'form', action: 'update' %>
# new.html.erb
<%= render 'form', action: 'create' %>
Pretty much it is a bug and it should work as you initially expected. The following patch to address the issue looks like it was pulled back in October
https://github.com/lazyatom/rails/commit/73cb0f98289923c8fa0287bf1cc8857664078d43