I want to show fields from the database dynamically.i.e, if suppose
in one case my #user object reads from users table
#user = User.new
in another case #user object reads from posts table
#user = Post.new
Then dynamically my view(new page) shows the fields based on the model selected.
<% for column in #object.class.column_names %>
<%= #object.send(column) %>
<% end %>
EDITED (To exclude some columns from the view)
exclude_columns = ['id', 'created_at', 'updated_at']
<% for column in #object.class.column_names
next if exclude_columns.include?(column) %>
<%= #object.send(column) %>
<% end %>
This might be more than you are asking for, but perhaps you want to have a look at the presenter pattern. This will let your view make use of one single interface while hiding away the view logic.
I think this post will give you a nice introduction to the topic: http://mikepackdev.com/blog_posts/31-exhibit-vs-presenter
I'm going to disagree with Salil. you should never do assigns in the view (ERB). If you're doing that, you need to move towards a helper or a presenter depending.
Why would you not just walk the tree? #users should, by name, return a collection of users. #user.posts would walk the relationship tree. I'm curious why you're fighting the rails default way of working. Can you share more code to help us understand?
Related
I have something like this in my show action:
def show
#blog = Blog.find(params[:id])
#new_comment = #blog.comments.build
end
Now in the view I do two things:
I render the _form.html.erb for the Comment so that the user can add a comment to this blog
I want to be able to iterate over the existing comments for this blog in order to display them.
I am having problems with number 2. The issue is that as I iterate over the associated comments with this:
<% #blog.comments.each do |comment| %>
It is grabbing that #new_comment while iterating over the associated blogs. How can I exclude that built-but-not-yet-persisted #new_comment?
It would be nice if something like #blog.comments.each_with_id worked, or even #blog.comments.persisted.each. Ultimately I only want to iterate over the persisted comments associated to this blog.
I would prefer not to have to nest a conditional that asks if the comment is persisted?. I'm hoping there is an iterator out there for my current situation that just grabs the persisted records.
How about if you create the new comment without using the association to the blog instance? Then it won't show up when you iterate through #blog.comments.
def show
#blog = Blog.find(params[:id])
#new_comment = Comment.new(blog: #blog)
end
in your view loop, you can skip if comment.new_record?
<% #blog.comments.each do |comment| %>
<% next if comment.new_record? %>
EDIT per your comment:
if you dont want filter out during the iteration, you can reject new records before iterating. however, i wouldn't recommend this approach, as youre creating an entirely new array of records for little reason. Your performance shoundnt really take a hit assuming blogs dont have thousands of comments, but its still not a great practice.
<% #blog.comments.reject(&:new_record?).each do |comment| %>
if you truly want to separate the logic from the view and controller, you can make another variable entirely dedicated to blog comments, prior to building a new one, so that its not included during the iteration.
# controller
def show
#blog = Blog.find(params[:id])
#current_comments = #blog.comments
#new_comment = #blog.comments.build
end
#view
<% #current_comments.each do |comment| %>
for what its worth, i'd still recommend the first apprach
You could add a class method to your Comment model such as this:
def self.persisted
reject { |comment| comment.new_record? }
end
Then call
#blog.comments.persisted
The downside is that it's not after this you don't have an ActiveRecord::Relation anymore and might break your scopes chaining. Make sure you're using it last in your ActiveRecord queries.
I'm using Thumbs_Up gem to let users vote on a post(called Topic). I've done the voting part but now I want to display the users who voted on that particular post. I'm very new to Ruby On Rails, and I'm stuck at the view part.
Here's my controller: app/controllers/topics_controller.rb
def vote_who
#topic = Topic.find(params[:id])
#vote_list=#topic.voters_who_voted_for
end
In my index.html.erb, I want to display the names of there users who voted on that post. This should be right next to the vote button. Kind of like this,
But how do I send back information from the controller?
Or is this approach completely wrong?
IMHO, it's more preferable to show all voters in the 'show' view of the each particular Topic (imagine you have 100 voters for one topic, what this table in index view will look like?).
This way you don't need any distinct action (due to Rails conventions 1 action == 1 view (in general)). Just put in your show action:
def show
#topic = Topic.find(params [:id])
#vote_list = #topic.voters_who_voted_for
end
Then in your view (show.html.erb) you need to do something like this:
<% #vote_list.each do |voter| %>
<%= voter.email %> #or login, or name, or any user's attribute you want to display in your list
<% end %>
Or even try to simplify this: leave show action in your TopicsController as it is (don't create any #vote_list variable, only find right Topic), and call voters_who_voted_for method inside your show view, e.g.:
<%= #topic.title %>
<%= #topic.body %>
<%= #topic.voters_who_voted_for %>
I'm trying to build a condition based on wether or not a "user" is a "member". Basically I need a way of checking if the current_user.id matches any of the user_id of any members. The non-working code I have right now is:
<% if current_user = #page.members %>
you can view this content.
<% end %>
I'm looking for something along the lines of: "If current_user.id exists in the "user_id" of any members."
Something like this, based on the field names in your question:
<% if #page.members.map(&:user_id).include? current_user.id %>
You can view this content
<% end %>
Assuming your #page.members variable contains an array, you can use the include? method:
<% if #page.members.include? current_user %>
you can view this content.
<% end %>
If you're using an array of ids, you will of course need to change the test slightly to look for the current user's id:
<% if #page.members.include? current_user.id %>
you can view this content.
<% end %>
#member_ids = #page.members.map{|m| m.id()}
then check for the condition as below
#memeber_ids.include? current_user.id()
Has said before include? should do the thing.
I'm just answering to tell you about a gem called CanCan, that gives you easy access for authorization "helpers".
Why you should use CanCan instead of doing what you are actually doing?
Don't reinventing the weel most of the times it's a goob practice.
You are placing business logic on the view (bad practice).
CanCan most likely has been developed thinking on security, and all the best practices in mind.
You save some developing hours.
Sorry if I repeated myself.
I have a form where I'd like to create a parent record and a child record at the same time. For a simple example let's say its a Company with the first Employee.
in my controller I do something like:
def new
#company = Company.new
#company.employees.new
end
and in my view this:
<%= form_for(#company) do |form| %>
<div>
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<%= form.fields_for :employees do |employee_form| %>
<div>
<%= employee_form.label :name %>
<%= employee_form.text_field :name %>
</div>
<% end %>
<% end %>
and back in my controller again:
def create
#company = Company.new(params[:company])
#company.employees << Employee.new(params[:company][:employees_attributes]["0"])
# save stuff
end
Question 1:
I couldn't get the employee collection on the company to be populated with the single employee created in the form. When I looked at the params I found the [:employees_attributes]["0"] stuff.
What I have works, but is there a cleaner way to do this?
Question 2:
If the validation doesn't pass for the employee I get a generic "Employees is invalid" instead of the Name required validator message. I get I am calling save on the collection and rails is doing its best to bubble a validation error up, but is there a cleaner way to do this so I can get the errors specific to the employee?
In Short
How can I clean this up so the related models are created automatically from the params, and so that I get the validation messages for a single employee.
Thanks for looking.
1) fields_for arranges for the child objects attributes to be nested inside the parent objects attributes in the params hash that gets sent back to the controller action. To get Rails to automatically update the child objects tell the parent model to accept nested attributes using the accepts_nested_attributes_for declaration.
2) There is an errors object for every ActiveRecord object. Loop through the errors list and display the messages.
Best way to achieve this is to create a partial and a view helper method that will take render the errors for you. then replace the generated errors messages in the forms with a call to your render_error_messages method. You have all the code to do this already in the generated forms. You just need to refactor that code into a partial, create the helper - which should accept an array of model names as a parameter then do what you want with the info. Wither render a partial for each model or render a partial that will deal with child objects as well as the parent object. Totally your call.
3) Change your new action to build rather that create a new child object so instead of
def new
#company = Company.new
#company.employees.new
end
do this
def new
#company = Company.new
#company.employees.build
end
4) Watch those Railscasts to see how accepts_nested_attributes works
http://railscasts.com/episodes/196-nested-model-form-part-1
and
http://railscasts.com/episodes/197-nested-model-form-part-2
Update
So how does the above information leave you in relation to your questions.
1) What I have works, but is there a cleaner way to do this?
You've fixed the new action as per point 3 above right? Now your create action can look like this
def create
#company = Company.new(params[:company])
# save stuff
end
Which is much cleaner as it has reverted to the original generated create action.
You may not think that's much of an update and therefore not that much cleaner. Well in isolation you'd be right. But consider that you could add as many relationships as you like ad add as many fields_for declarations as you like nd you could turn the user -> employee relationship into a has_many (I know that you wouldn't). You could do all that and your create and update actions stay EXACTLY the same and that's why it's cleaner.
2) is there a cleaner way to do this so I can get the errors specific to the employee?
Given my response in point 2 above you know that there is an errors object on the employee object as well as on the user object right? You also know now that you can loop through that errors object to get the messages right?
So you could do this
<% if #user.employee.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.employee.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.employee.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
At the risk of repeating myself I'll just say that you should refactor your error messages view code into a partial that will take any object as a parameter then you can call it from any view thus enabling you to change the styling and the functionality for all your forms.
Hope that's clearer
This question relates to cleaning up the view and giving the controller more of the work.
I have many cases in my project where I have nested variables being displayed in my view. For example:
# controller
#customers = Customer.find_all_by_active(true)
render :layout => 'forms'
# view
<% #customers.each do |c| %>
<%= c.name %>
<% #orders = c.orders %> # I often end up defining nested variables inside the view
<% #orders.each do |o| %>
...
<% end %>
<% end %>
I am fairly new to RoR but it seems that what I'm doing here is at odds with the 'intelligent controller, dumb view' mentality. Where I have many customers, each with many orders, how can I define these variables properly inside my controller and then access them inside the view?
If you could provide an example of how the controller would look and then how I would relate to that in the view it would be incredibly helpful. Thank you very much!
I don't think there is anything drastically wrong with what you're doing. Looping through the customers and outputting some of their attributes and for each customer, looping through their orders and outputting some attributes is very much a view-oriented operation.
In the MVC architecture, the controller has responsibility for interacting with the model, selecting the view and (certainly in the case of Rails) providing the view with the information it needs to render the model.
You might consider extracting the code into a view helper though, if you have that exact code repeated more than once. You could even genericize it, passing in the name of a model and association. I haven't tested it, but you should be able to do something like this:
def display_attributes(models, association, attribute, association_attribute)
content = ''
models.each do |m|
content << "<p>#{m.attribute}</p>"
associated_models = m.association
associated_models.each do |am|
content << "<p>#{am.association_attribute}</p>"
end
end
content
end
Then in the view, you could use the helper like this:
<%= display_attributes(#customers, orders, name, name) %>
Obviously you would change the HTML markup within the helper method to suit your requirements. Note that if you're not using Rails 3 then you'll want to escape the output of the attribute names in the helper method.
I don't think there's anything wrong with your code. I'd just suggest for you to use a :include in your find
#customers = Customer.find_all_by_active(true, :include => :orders)
to reduce the number of queries.
I see nothing wrong with the code as you showed.
You are mixed up about the "intelligent controller, dumb view" approach though, i tend to prefer the "skinny controller, fat model", so indeed the view should be dumb, but you put the intelligence inside your model, and your helpers (or use a presenter), but definitely not in the controller.