Access attributes in controller while looping in view - ruby-on-rails

I have a loop in my view:
<% #deals.each do |d| %>
<% end %>
Deal and ListItem share the :code attribute.
If I wanted to count all the list_items that shared :code with a certain deal, how can I do this in the controller while iterating through the loop?
I understand it's bad form to access Models in the view correct? So that would rule out doing something like this in the loop right?:
<%= "#{ListItem.where(:code => d.code).count}" %>
Can I use a variable or some custom method to achieve this? Please let me know if I can clarify. Thank you!

You want to do all the calculation on the server in your controller action before rendering the view.
So in addition to gathering the #deals, you will also set an instance variable for the count.
def index
#deals = Deals.all # or whatever your finder is
#common_listitem_count = ListItem.where(code: #deals.pluck(:code)).count
end
Then just call the count instance variable in the view:
<%= #common_listitem_count %>

Related

Is it ok to call ActiveRecord Methods in the view Rails

Using Rails 4
I am wondering (and having a hard time finding an answer) if it is OK to call an ActiveRecord method directly from the view, such as:
<%= Article.where(approved: true).count %>
or
<%= Article.where("short_answer is NOT NULL and short_answer != ''").count %>
I realize the normal practice would be to store these in an instance variable inside of the controller, but since I am using a partial, I cannot do that.
Is doing this ok? Can it hurt? Is there a better way to go about this (such as a helper method)? Thank you!
Is doing this ok? Can it hurt?
It's definitely okay, but the problem is that you'll be calling another db query - which is the most "expensive" part of a Rails app.
#instance_variables are set once, and can be used throughout the view:
#app/views/articles/show.html.erb
#Referencing #article references stored data, not a new DB query
<%= #article.title %>
<%= #article.description %>
<%= #article.created_at %>
Because the above all uses the stored #article data, the database is only hit once (when #article is created in the controller).
If you call AR methods in the view, you're basically invoking a new db call every time:
#app/views/articles/show.html.erb
#Bad practice
<%= Article.select(:name).find(params[:id]) %>
<%= Article.select(:description).find(params[:id]) %>
<%= Article.select(:created_at).find(params[:id]) %>
To answer your question directly, you would be okay to call that data IF you were only counting database-specific data.
IE if you were trying to count the number of #articles, you'd be able to call #articles.size (ActiveRecord: size vs count)
The prudent developer will determine which data they have in their controller, and which they need to pull from the db... doing all their db work in the controller itself:
#app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def index
#articles = Article.where(approved: true) #-> could use a scope here if you wanted
end
end
#app/views/articles/index.html.erb
<%= #articles.size %>
Nithin's answer is great but won't get past the consideration that you have to determine whether you need to call the db explicitly, or use already-invoked data.
Finally, in regards to using a partial, if you have to pass that data every time, you may wish to use some sort of conditional data to determine whether you need to call the db or not:
#app/views/shared/_partial.html.erb
<% approved ||= Article.approved_articles.size %>
<% short ||= Article.short_answer_presence.size %>
This will allow you to set locals IF you want, and also have "defaults" set if they aren't set.
You should mostly do
class Article < ActiveRecord::Base
....
scope :approved_articles, where(approved: true)
scope :short_answer_presence, where("short_answer is NOT NULL and short_answer != ''")
end
In your controller method
#approved_articles_count = Article.approved_articles.count
#short_answer_presence_count = Article.short_answer_presence.count
and use those variables in view.
In case of partials, as said my Raman you can do that.
<%= render partial: "form", locals: {approved_articles_count: #approved_articles_count, short_answer_presence_count: #short_answer_presence_count} %>
You can always pass these variables inside a partial using locals:
<%= render partial: "form", locals: {zone: #zone} %>
Its always a good practice to define the instance variables in controller, it does not hurt but you don't end up doing business logic inside a view.

dynamic fields in the rails view

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?

Calling specific instances of a user's objects on their show page

I'm trying to list a user's wanted ads in their show page when they access /users/:id.
In my controller I have:
def show
#user = User.find(params[:id])
#wanted_ads = WantedAd.where(:user_id => params[:id])
And in my show.html.erb I have:
<%= #wanted_ads %>
Binding.pry says #wanted_ads is nil. On the actual page I get #<ActiveRecord::Relation:0x007fa3b5a93408>. This is seemingly a simple thing I'm trying to do -- what am I missing?
The where function returns a ActiveRecord::Relation.
So, you can call first to get the first element, last to get the last one or all to get all elements stored in an array called #wanted_ads.
#wanted_ads = WantedAd.where(:user_id => params[:id]).all
You can then go through this array and choose the attributes you want to pass to the view for each element.
Just a tip:
You should have in your User model an ActiveRecord relation, like this:
has_many :wanted_ads
And in your WantedAd model, like this:
belongs_to :user
And with this, you have a relation of one-to-may.
Then, you can do this:
def show
#user = User.includes(:wanted_ads).find(params[:id])
end
And then, in your view:
<% #user.wanted_ads.each do |wanted_ad| %>
<%# do something %>
<% end %>
where returns a collection of objects, not just a single object. So depending on what's returned you'll want to either call first to get the single instance that ought to return, or call each to iterate over the wanted ads and display them on your page.
You're seeing an instance of ActiveRecord::Relation in pry because of the underlying query mechanism that lazy loads the results. More details can be found here.
Assuming your #wanted_ads is not nil and you want to loop through all the wanted ads...
<% #wanted_ads.each do |wanted_ad| %>
<%= wanted_ad.some_attribute_of_wanted_ad %>
<% end %>
I would also suggest you be aware of SQL injection with the following code in your controller.
#wanted_ads = WantedAd.where(:user_id => params[:id])
As it should be
#wanted_ads = WantedAd.where(":user_id => ?", params[:id])

Rendering rails partial with dynamic variables

I'm trying to render a partial based on the taxon the user is inside. In my application.html.erb layout I have the following line of code:
<%= render 'spree/shared/women_subnav' if #enable_women %>
In the taxons controller, inside the show method, I have:
#taxon_id = params[:id].split('/').first
And in taxons#show I have:
<% if #taxon_id == params[:id].split('/').first %>
<%= "#enable_#{#taxon_id}" = true %>
<% end %>
When I run this I get a SyntaxError. But in taxons#show If I just enter:
<% if #taxon_id == params[:id].split('/').first %>
<%= "#enable_#{#taxon_id}" %>
<% end %>
without the '= true' then the page renders, outputting '#enable_women'. So I know it's getting the correct variable, I just need that variable to be set to true. What am I missing?
Thanks so much.
First of all I would like to give you some heads-up:
calling first on a user submittable input is not a great idea (what if I submit ?id=, it would return nil) also non utf-8 encoding will crash your app such as: ?id=Ж
Controllers are beast! I see you are setting the value of a true/false instance_variable in the view, please use controllers do define the logic before rendering its output. especially when parameter dependant.
so for a solution:
in your controller as params[:id] should suggest an INT(11) value:
def action
# returning a Taxon should be a good idea here
#taxon = Taxon.find(params[:id])
# as I would give a Taxon class an has_many relation to a User
#users = #taxon.users
end
and in your action's view
<%= render :partial => "taxons/users", collection: #users %>
of course you would have the great ability to scope the users returned and render the wanted partial accordingly.
if you want more info about "The Rails way" please read:
http://guides.rubyonrails.org/
Have fun!
use instance_variable_set
instance_variable_set "#enable_#{#taxon_id}", true
just a reminder that it's better to do these things inside a controller.

Rails: textfield list to array of strings

I want to take input from a textfield and turn it into an array of strings. After having submitted the post request, I want to display again the textfield, but including the values of the textfield in an array.
I have a view that would look like:
<% form_tag "/list2array" do -%>
<%= text_area_tag "mylist" %>
<div><%= submit_tag 'save' %></div>
<% end -%>
<% #myArray.each do |item| %>
<%= item %>
<% end %>
And as a start for the controller:
class List2ArrayController < ApplicationController
def index
end
def save
#myArray = params[:mylist].split("\r\n")
end
end
However, after the post, I only get an empty textfield without values in the array from the previous POST.
Do I need to use the model layer for my experiment? How? Or do I need to modify my controller?
Sort answer: Yes. You need to use some form of data store, either models or you can store it in the session. The are no continuations of state.
If you have a model you can add an attribute called mylist and mylist_array (you can use serialize for the array). Then either with a setter, or a before_validations callback set the value of mylist_array as you do in your example.
On a slightly contradictory note: adding the following to the end of your save method, will make your experiment sort of work, but you will need to fix your form post url first or add in a route for it manually.
render :index

Resources