Stuck Understanding Link_to - ruby-on-rails

I'm really struggling to understand how to link_to a parent from with a loop.
My milestones belong_to my orders and my orders have many milestones.
In my orders index, I have a simple calendar (table_builder) which lists all my milestones.
<%= calendar_for #milestones, :year => #date.year, :month => #date.month do |t| %>
<%#= calendar_for(#orders, :year => 2009, :month => 1) do |t| %>
<%= t.head('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday') %>
<%= t.day(:day_method => :milestone_due) do |date, orders| %>
<%= date.day %>
<ul>
<% for milestone in orders %>
<li><%= link_to milestone.name, order_path %> </li>
<% end %>
</ul>
<% end %>
<% end %>
This all works swimmingly well but the link doesn't work - I need it to link back to the parent order, not the milestone. It's driving me crazy now!
In my controller, I tried putting:
#milestoneorder = Order.find(params[:id])
But that says it can't find an order without an id.
I'm obviously missing something really basic here.

You need to tell order_path which Order to link back to:
<%= link_to milestone.name, order_path(milestone.order) %>
You could probably just shorten it to this too:
<%= link_to milestone.name, milestone.order %>
UPDATE
If there's a chance some of your milestones don't have orders, you can try something like this:
<% if milestone.order %>
<%= link_to milestone.name, order_path(milestone.order) %>
<% else %>
<%= milestone.name %>
<% end %>

It doesn't sound like you have a route setup for order.
In the routes.rb file
resources :orders
or if you do, you aren't passing in an id for the order.
link_to "link text", order_url(:id => #order)
link_to "link text", order_url(#order) # <== shortened
lastly, the problem may be that order is nil. If it is nil, you will also get the 'can't find route' error.
UPDATE
<% orders.each do |order| %>
<li><%= link_to milestone.name, order_path(order) %> </li>
<% end %>
UPDATE 2
The problem is in the names. I think that you are getting get milestones from t.day not orders.
<%= t.day(:day_method => :milestone_due) do |date, milestones| %>
<%= date.day %>
<ul>
<% for milestone in milestones %>
<li><%= link_to milestone.name, order_path(:id => milestone.order_id) %> </li>
<% end %>
</ul>
<% end %>

Related

Rails: how to create a list with conditional links

I am working on a RAILS application where I create a view which lists all available resources of a given model species.rb.
The view partial is:
<% i= #s
for species in #species %>
<%= species.name %>, <%= species.author.surname %> <%= species.author.initial_name %>
<% i -= 1
end %>
Some of the resources species have an related article, others have only the name. I would like to loop through the partial and add a link only to the entries which have an associated article.
Something like: add link if species.article is present else just put species.name without link + loop through it for all entries.
How can I do this?
UPDATE:
Thanks to #jvillian and #fool-dev I was able to progress. In my case I wanted to add a link if the resource has a description in a description row of its table.
<% #species.each do |species| %>
<div class="entry">
<p><i><%= link_to_if(species.txtlandscape.present?, "#{species.name}, #{species.author.surname}, #{species.author.initial_name}. 2014", :controller => 'projects', :action => 'show', :id => species) %></i></p>
</div>
<% end %>
Now that a link is added I was wondering if it can be used to load a partial to a target such as in, where ArticleRequest is a JS function I have:
<% # species.each do | species | %>
<div id="species-<%= species.id %>" class="species-entry">
<a onClick="ArticleRequest('/species/show/<%= species.id %>', 'species-<%= species.id %>');">
<p><%= species.name %></p>
</a>
</div>
<% end %>
Until I find a way to do this with link_to_if, I will use something like:
<% for species in #species %>
<% if species.txtlandscape.present? %>
<a onClick="ArticleRequest('/species/show/<%= species.id %>', 'species-<%= species.id %>');">
<p><%= species.name %>, <%= species.author.surname %> <%= species.author.initial_name %></p>
</a>
<% else %>
<p><%= species.name %>, <%= species.author.surname %> <%= species.author.initial_name %></p>
<% end %>
<% end %>
According to the docs, it seems you should be able to do:
<% #species.each do |specie| %>
<%= link_to_if(specie.article, specie.name, specie_article_path(specie.article)) %>
<% end %>
I made the path name up, you'll have to make that match your actual routes.
BTW, this:
for species in #species
Is super non-idiomatic.
You can do this, see the below
<% for species in #species %>
<% if species.article.present? %> #=> I thin it will be articles because table name is articles, anyway, you know better
<%= link_to species.name, link_path(species.article) %>, #=> on the link_path it will be your proper link just replace this
<% else %>
<%= species.name %>,
<% end %>
<%= species.author.surname %> <%= species.author.initial_name %>
<% end %>
You can do this with Rails each method like below
<% #species.each do |species| %>
<% if species.article.present? %> #=> I thin it will be articles because table name is articles, anyway, you know better
<%= link_to species.name, link_path(species.article) %>, #=> on the link_path it will be your proper link just replace this
<% else %>
<%= species.name %>,
<% end %>
<%= species.author.surname %> <%= species.author.initial_name %>
<% end %>
Or you can use link_to_if it is also most easier to understand
<% #species.each do |species| %>
<%= link_to_if(species.article.present?, "#{species.name},", link_path(species.article)) %>
<%= species.author.surname %> <%= species.author.initial_name %>
<% end %>
Hope it will help.

Updating attributes from check_box

I'm using nested models in my first Ruby on Rails app and now I've run into a problem. I have a Survey model that has_many :questions which in turn belongs_to :survey and the model Question has_many :answers. My Answer model then belongs_to :question.
Now I want to update a boolean attribute :guess within my :answers controller. To this end I've created a new action :quiz_guess which looks like this:
def quiz_guess
Answer.update(params[:id], guess: false)
puts("Guess saved")
redirect_to(:action => 'show', :id => #survey.next, :survey_id => #survey.id)
end
my view looks like this:
<%= form_tag quiz_guess_questions_path, :method => :put do %>
<% for question in #survey.questions do %>
<li><%= h question.content %></li>
<% for answer in question.answers do %>
<li><%= h answer.content %>
<%= form_for :guess do |f| %>
<%= f.check_box(:guess, :method => :put) %>
<% end %>
</li>
<% end %>
<% end %>
<% end %>
Obviously I've read the documentation, as well as this blog post, but I don't understand how to implement it in my controller and view.
With this code I'm not able to update the correct :guess attribute but rather I update a different one (with a different :id). Can anybody shed some light as to what I do wrong?
I ended up looking at this Railscast where Ryan Bates shows how to update multiple attributes with fields_for. This is my new view:
<%= form_tag quiz_guess_questions_path, :method => :put do %>
<% for question in #survey.questions do %>
<li><%= question.content %></li>
<% for answer in question.answers do %>
<li>
<%= fields_for "answers[]", answer do |f| %>
<%= answer.content %>
<%= f.check_box :guess %>
<% end %>
</li>
<% end %>
<% end %>
<%= submit_tag "Guess" %>
<% end %>
and my new controller:
def quiz_guess
Answer.update(params[:answers].keys, params[:answers].values)
flash[:notice] = "Guess saved successfully."
redirect_to questions_url
end

Rails - Sorting deep nested resources on index page from dropdown by primary model

So I have three models: Topic, Post and Paragraph. Each Topic has many Posts and each Post has many Paragraphs. What I need to achieve is to sort the Paragraphs by Topic on paragraphs/index.html.erb.
I of course have the dropdown menu including all the topics:
<form>
<select>
<% #topics.sort { |a,b| a.name <=> b.name }.each do |topic| %>
<option><%= topic.name %></option>
<% end %>
</select>
<input type="submit">
</form>
I followed the advice on: Filter results on index page from dropdown, but I couldn't manage to come up with a way to connect Topic params first to Post and then to Paragraph. I simply have no idea how to go with it, and there doesn't seem to be many examples out there, so any ideas are hugely appreciated.
Before start, check if you specified accepts_nested_parameters_for ... in post.rb and topic.rb
OK, now we need to adjust routing to make the magic happens. Just add to routes.rb:
patch 'paragraphs' => 'paragraphs#index'
#we'll use PATCH for telling index which topic is active
Paragraphs#index remains the same:
def index
#topics = Topic.all
end
The rest we'll do in view. So, index.html.erb:
<h1>Listing paragraphs sorted by Topic</h1>
<% names_options = options_from_collection_for_select(#topics, :id, :name, selected: params[:topic_id]) %>
<%= form_tag({action: "index"}, method: "patch") do %>
<%= select_tag :topic_id, names_options,
{prompt: 'Pick a topic', include_blank: false} %>
<%= submit_tag "Choose" %>
<% end %>
<% #topics = #topics.where(:id => params[:topic_id]).includes(:posts => :paragraphs) %>
<% #topics.each do |topic| %>
<option><%= topic.name %></option>
<h2><%= topic.name %></h2>
<% topic.posts.each do |post| %>
<h3><%= post.content %></h3>
<% post.paragraphs.each do |paragraph| %>
<%= paragraph.content %><br>
<% end %>
<% end %>
<% end %>
Viola!

Ruby on Rails: one check_box for several submit_tag

I need to have one check_box for several purposes.
For example: I have a list of files. User can choose some of them to be deleted or analysed.
I have the following code but it accepts only one submit_tag "Delete selected".
<% if #files%>
<%= form_tag destroy_multiple_files_path, method: :delete do %>
<%= submit_tag "Delete selected" %>
<% #files.each do |file| %>
<% if (arraydb.file=="no") %>
<p><td> <%= check_box_tag "files[]", file.id %></td><%= file.name %></p>
<% else %>
<div class="my_profile_info">
<p><td> <%= check_box_tag "files[]", file.id %></td> <%= file.name %></p>
<td class="Info">
Info
</td>
</div>
<% end %>
<%end%>
<%end%>
<%else%>
<%end%>
I would like to have submit_tag "Analyse" as well.
I tried something like this but of course it did not work.
<% if #files%>
<%= form_tag destroy_multiple_files_path,analyse_multiple_files_path method: :delete,method:post do %>
<%= submit_tag "Delete selected" %>
<%= submit_tag "Analyse" %>
<% #files.each do |file| %>
<% if (arraydb.file=="no") %>
<p><td> <%= check_box_tag "files[]", file.id %></td><%= file.name %></p>
<% else %>
....
routes.rb:
resources :files do
collection do
delete 'destroy_multiple'
end
end
controller:
def destroy_multiple
#files = File.find(params[:files])
#files.each do |item|
item.destroy
end
end
Thanks in advance.
You can indeed have multiple submit buttons, you just have to give them names:
<%= submit_tag "Delete selected", :name => 'delete' %>
<%= submit_tag "Analyse", :name => 'analyse' %>
You can then check what the commit param contains in the controller and act accordingly:
if params[:commit] == 'delete'
# delete things
elsif params[:commit] == 'analyse'
# analyse things
end
The rest of the form will be submitted as usual.
this worked for me:
<%= form_tag destroy_multiple_files_path, method: :get do %>
<%= submit_tag "Delete selected", :name => 'delete' %>
<%= submit_tag "Analyse", :name => 'analyse' %>
controller:
if params[:commit] == 'Delete selected'
# delete things
elsif params[:commit] == 'Analyse'
# analyse things
end
routes.rb:
resources :files do
collection do
get :destroy_multiple
end
end

How can I refactor out needing so many for-loops in rails?

I need help refactoring this multi-loop thing. Here is what I have:
Campaign has_many Contacts
Campaign also has many Models which are templates: (Email, Call, and Letter).
Because I am looking for overdue on each, I created an array called Event which I'd like to loop through that contains ['email', 'call', 'letter'].
I need a list of all the Emails, Calls and Letters that are "overdue" for every Contact that belongs to a Campaign. Overdue is determined by a from_today method which looks at the date the Contact was entered in the system and the number of days that needs to pass for any given Event. from_today() outputs the number of days from today that the Event should be done for a given Contact.
Here is what I've done, it works for all Emails in a Campaign across all contacts. I was going to try to create another each do loop to change the class names.
Wasn't sure where to begin: named_scope, push some things into a method, etcetera, or -- minimum to be able to dynamically change the class names so at least it loops three times across the different events rather than repeating the code three times:
<% #campaigns.each do |campaign| %>
<h2><%= link_to campaign.name, campaign %></h2>
<% #events.each do |event| %>
<%= event %>
<% for email in campaign.emails %>
<h4><%= link_to email.title, email %> <%= email.days %> days</h4>
<% for contact in campaign.contacts.find(:all, :order => "date_entered ASC" ) %>
<% if (from_today(contact, email.days) < 0) %>
<% if show_status(contact, email) == 'no status'%>
<p> <%= full_name(contact) %>
is <%= from_today(contact,email.days).abs%> days overdue:
<%= do_event(contact, email) %>
</p>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
Just to add to Patrick's answer, I would also use the :collection option of render to simplify this a bit further, e.g. have a partial _contact.html.erb to render each contact:
<% if (from_today(contact, email.days) < 0) %>
<% if show_status(contact, email) == 'no status'%>
<p> <%= full_name(contact) %>
is <%= from_today(contact,email.days).abs%> days overdue:
<%= do_event(contact, email) %>
</p>
<% end %>
<% end %>
<% end %>
and then render the contacts collection with
= render :partial => "contact", :collection => #contacts
I also wouldn't do a find in the view, instead I would setup all the variables in the controller, and probably move all the conditional code into a helper. It's preferable to keep as much logic as possible out of the views.
I'd put the output for each resource into a partial, like so:
<% #campaigns.each do |campaign| %>
<h2><%= link_to campaign.name, campaign %></h2>
<%= render 'events', :events => campaign.events %>
<% end %>
then in app/views/campaigns/_events.html.erb
<% events.each do |event| %>
<%= event %>
<%= render 'emails', :emails => event.emails %>
<% end %>
then in app/views/campaigns/_emails.html.erb
<% emails.each do |email| %>
<h4><%= link_to email.title, email %> <%= email.days %> days</h4>
<%= render 'contacts', :contacts => email.contacts.all(:order => "date_entered ASC", :email => email) %>
<% end %>
then in app/views/campaigns/_contacts.html.erb
<% contacts.each do |contact| %>
<% if (from_today(contact, email.days) < 0) %>
<% if show_status(contact, email) == 'no status'%>
<p> <%= full_name(contact) %>
is <%= from_today(contact,email.days).abs%> days overdue:
<%= do_event(contact, email) %>
</p>
<% end %>
<% end %>
<% end %>

Resources