I have been trying trying to use two attributes from two different models so they can be used with the calendar_for method provided by the table_builder plugin in the index view. I have been through the Rails guides for http://guides.rubyonrails.org/active_record_querying.html#retrieving-multiple-objects and posts such as Ruby on Rails: How to join two tables however i must be doing something wrong.
My models are as follows:
Class Event < ActiveRecord::Base
belongs_to :user
and
class User < ActiveRecord::Base
User has_many :events
The different ways I have tried in the controller (not all at once) are:
#event.user.name
#users = User.joins(:event).where(:event => {:event_date => true})
#users = User.where(:event => :event_date)
Amoung others, my view looks like:
my view code:
<% calendar_for #users, :year => #date.year, :month => #date.month do |calendar| %>
<%= calendar.head('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday') %>
<% calendar.day(:day_method => :created_at) do |date, users| %>
<%= date.day %>
<ul>
<% for user in users %>
<li><%= link_to h(user.name), user %></li>
<% end %>
</ul>
<% end %>
<% end %>
</div>
I have tried changing the variables in the view accordingly however to no avail. I would like to show the users name and a link to the user on the specific day that their event is booked.
Two issues I see, though I'm not familiar with the calendar library.
First, make sure your query in the controller returns something useful. Of the three lines you gave us, the first doesn't even search, it calls methods on an undefined variable. The second is close to working, but you are searching for a date and matching it to true... How about:
#users = User.joins(:event).where('events.event_date is not null')
Furthermore, if you have a date range, you might include that in the search:
#users = User.joins(:event).where('events.event_date > ? and events.event_date < ?', start_date, end_date)
Next in the view, you aren't consistent with your variable naming. The controller sets up the #users variable, which you access once, but then later you are missing the # in front of it, which is not the same thing. I don't know what the calendar part wants as input, but at least the for loop should be:
for user in #users
That said, for loops are not very rubyish. The ruby way is to use each:
#users.each do |user|
...
end
or even better, to make all of your links:
<ul>
<%= #users.collect {|user| content_tag(:li, link_to h(user.name), user).join } %>
</ul>
edit
Based on more information, I think you are starting at the wrong place. Let's start with events.
Event.joins(:users).where('events.event_date is not null')
<% calendar_for #events, :year => #date.year, :month => #date.month do |calendar| %>
<%= calendar.head('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday') %>
<% calendar.day(:day_method => :event_date) do |date, events| %>
<%= date.day %>
<ul>
<% for event in events %>
<li><%= link_to h(event.user.name), event.user %></li>
<% end %>
</ul>
<% end %>
<% end %>
Related
I'm new to Rails and I'm doing my first project. Also, English is not my native language so bear with me, please.
The problem I'm having is that I have a form with multiple instances of the same model, the data is being created correctly but when I try to edit it the form is populated in the wrong way.
I'm making an app to check if everything goes according to the rules.
The items to be checked are in a nested association Chapters->Subchapters->Checks
Every time the checks are submitted a CheckRound is created and the information of every check is stored separately in CheckResults.
CheckRounds
has_many :check_results, inverse_of: :check_round, dependent: :destroy
accepts_nested_attributes_for :check_results, reject_if: proc { |att| att['observation'].blank? }
CheckResults
belongs_to :check_round, optional: true, inverse_of: :check_results
belongs_to :check
Chapters
has_many :subchapters
Subchapters
belongs_to: chapter
has_many: checks
Checks
belongs_to :subchapter
has_many :check_results
The form displays all the Chapters and the nested Subchapters and Checks.
Every Check displays its name and has a text_area as an input.
The user can fill none or many Checks.
<%= form_for(#check_round, :url => {:action => 'update', :client_id => #client.id, :project_id => #project.id}) do |f| %>
<% #chapters.each do |chapter| %>
<%= chapter.name %>
<% chapter.subchapters.each do |subchapter| %>
<%= subchapter.name %>
<% subchapter.checks.each do |check| %>
<%= f.fields_for :check_results do |result| %>
<%= check.name %>
<%= result.hidden_field(:check_id, :value => check.id) %>
<%= result.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s) %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
The controller is
def edit
#check_round = CheckRound.includes(:check_results).find(params[:id])
#chapters = Chapter.includes(subchapters: :checks).where("segment_id = ?", #project.segment_id).sorted
end
If for example, I submit that check.id = 3 has the observation = "bad" when I go to edit every check has "bad" in its observation regardless of its id.
I want to know how can I show in edit all the checks with a blank observation but the ones that were created.
Thanks in advance for your time!
Ok, From what i see 2 things that needs to fixed.
1st, your f.fields_for :check_results do |result|
needs an extra parameter to specify which check_results it exactly has to modify... somethings like this:
f.fields_for :check_results, #check_round.check_results.where(check_id: check.id) do |result|
in the exact same place so the check variable is specify the right way.
2de, you need to permit your nested parameters in your controller so they can be saved when u submit. Normally you should see a method called check_round_params in your check_round controller.
this one have to like this for everything to work:
def check_round_params
params.require(:check_round_params).permit(
/*your needed params*/,
check_results_attributes: [:id, :check_id, :observation, /*all your nested params*/]
)
end
In short, your update and your create actions work according to those permitted params, so you need define them there. check_results_attributes: is the way that rails understands those params are for nested models.
Here is some documentation you might find interesting:Nested attributes example
Here is the solution i've promised.
Sinds you have already defined that check results with blank observations had to be rejected and there will to much logic involved in your erb for its own sake, i would put it all in an helper method so your erb will be cleaner. Something like this:
#helpers/check_rounds_helper.rb
def edit_or_instantiate_nested_check_results(f, check_round, check, new_check_result)
if check.check_results
f.fields_for :check_results, check_round.check_results.where(check_id: check.id) do |result|
result.hidden_field(:check_id, :value => check.id)
result.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s)
end #end for the already present check results
# if u want to add a new check result event if the check is populated
f.fields_for :check_results, new_check_result do |new|
new.hidden_field(:check_id, :value => check.id)
new.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s)
end #end for the new check result
else #if there is no existing check result nest a form for a new one
f.fields_for :check_results, new_check_result do |new|
new.hidden_field(:check_id, :value => check.id)
new.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s)
end #end for the new check result
end #end if statement
end
Then in your view:
<%= form_for(#check_round, :url => {:action => 'update', :client_id => #client.id, :project_id => #project.id}) do |f| %>
<% #chapters.each do |chapter| %>
<%= chapter.name %>
<% chapter.subchapters.each do |subchapter| %>
<%= subchapter.name %>
<% subchapter.checks.each do |check| %>
<%= check.name %>
<% new_check_result = CheckResult.new(check_round_id: #check_round.id, check_id = check.id) %>
<%= edit_or_instantiate_nested_check_results(f, #check_round, check, new_check_result) %>
<% end %>
<% end %>
<% end %>
<% end %>
And that shoud be it ;). Let me know if it did the trick :D!
KR,
I believe it works like you want with this (code with some simplifications):
Check
class Check < ApplicationRecord
belongs_to :subchapter
has_many :check_results
def check_results_for_form check_round_id
results = check_results.where(check_round_id: check_round_id)
results.any? ? results : check_results.build
end
end
CheckRoundsController
def edit
#check_round = CheckRound.find(params[:id])
#chapters = Chapter.includes(subchapters: :checks).all
end
edit.html.erb
<%= form_for(#check_round, :url => {:action => 'update'}) do |f| %>
<ul>
<% #chapters.each do |chapter| %>
<li>
<%= chapter.name %>
chapter
<ul>
<% chapter.subchapters.each do |subchapter| %>
<li>
<%= subchapter.name %>
subchapter
<ul>
<% subchapter.checks.each do |check| %>
<li>
<%= check.name %>
check
<br>
<%= f.fields_for :check_results, check.check_results_for_form(#check_round.id) do |result| %>
<%= result.hidden_field(:check_id, :value => check.id) %>
<%= result.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s) %>
<% end %>
</li>
<% end %>
</ul>
</li>
<% end %>
</ul>
</li>
<% end %>
<ul>
<%= f.submit %>
<% end %>
Your problem is that you are repeating the display of the form fields for check_results. Look at line 7 of your view code:
<%= f.fields_for :check_results do |result| %>
This is displaying the fields for each check result on f.object (which is #check_round). However, this code gets repeated for each check in subchapter. That surrounding block gets repeated for each subchapter in chapter, and the block surrounding that gets repeated for each chapter in #chapters.
When the form is submitted, the params for check_results all have the same names, they are not distinguished by chapter, subchapter, or check. As a result, the only value that gets saved for observation is the last one submitted.
I think a solution for your case would be to only show the check_result form fields associated with the current check in the loop. One way to do that is to put a conditional in the loop starting on line 7 of your view code:
<%= f.fields_for :check_results do |result| %>
<% if result.object.check == check %>
<%= result.hidden_field(:check_id, :value => check.id) %>
<%= result.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s) %>
<% end %>
<% end %>
<% end %>
You could also just loop through the check_results independently of the loops for checks, subchapters, and chapters, but I'm assuming that you want to keep that order and context for the UI.
I have created a simple appointment system, and I now need to display something inside a loop if there's two or more appointments with the same date and time. The appointments are displayed in order of time, so they're just appearing one after the other.
Controller
def index
#todays_apps = current_user.appointments.order(time ASC)
end
View
<% #todays_apps.each do |app| %>
<%= app.business_name %>
<%= app.business_address %>
<%= app.time %>
<% end %>
I'm looking to display a message or icon the appointment shares a date and time with another appointment. Tried a collection of things with no luck.
You can group your collection by time and modify your iteration accordingly. You can group it like
#todays_apps.group_by(&:time)
The outcome will be something like
=> { timestamp1 => [app1,app2], timestamp2 => [app3], timestamp3 => [app4]}
Or you can try a quick hacky way like:
<% previous_time = nil %>
<% #todays_apps.each do |app| %>
<%= app.business_name %>
<%= app.business_address %>
<%= 'Your message or class or anything' if previous_time == app.time %>
<%= previous_time = app.time %>
<% end %>
Try Like this:
controller:
def index
#appointments = current_user.appointments.order("time ASC")
#todays_apps = #appointments.group_by(&:time)
end
View:
<% #todays_apps.each do |time, appointments| %>
<%= time %>
<% appointments.each do |appointment| %>
<%= appointment.business_name %>
<%= appointment.business_address %>
<% end %>
<% end %>
It will list all the appointments for particular time.
Thanks
Having a Bit of trouble displaying unique results from my database. I have a database called "Activities". Each Activity has an associated Sport through sport_id. There may be many activities with the same sport_id.
I want to display a list of all sports linked to the activities database without displaying (for example "Football") twice.
FYI : Venues have many Facilities and Facilities have many Activities.
Controller:
#sports = Sport.all
#activities = Activity.paginate(page: params[:page])
#facilities = Facility.where(venue_id: #venue.id)
View:
<% #facilities.each do |f| %>
<% #activities.find(:all, :conditions => "facility_id == #{f.id} ").each do |a| %>
<li><%= Sport.find(a.sport_id).name %>, (<%= a.facility_id %>)</li>
<% end %>
<% end %>
This shows:
Football, (2)
Hockey, (2)
Hockey, (2)
Football, (5)
I would like to display just:
Football
Hockey
Any ideas?
A simple solution would be to reduce your array with ruby in the view using: uniq!
<% #facilities.each do |f| %>
<% #activities.find(:all, :conditions => "facility_id == #{f.id} ").uniq! { |a| a.sport_id }.each do |a| %>
<li><%= link_to Sport.find(a.sport_id).name, Sport.find(a.sport_id) %></li>
<% end %>
<% end %>
Another way may be to perform a single query on your DB since Sport what you want to narrow down
In controller:
#sports = Sport.joins(activities: [facility: :venue]).where(facilities: { venue_id: #venue.id }).distinct
In view:
<% #sports.each do |sport| %>
<li><%= link_to sport.name, sport %></li>
<% end %>
I am not sure about your DB schema so I went with what I thought you would have done, but it might needs some tweakings.
I hope I helped you.
try to use reject before each
<% #facilities.reject{your condition here}.each do |f| %>
I have a form that takes bookings for an event for people. The form displays events vertically, and a name & checkbox for each of the possible people next to each event.
How should I best convey the two pieces of information that i need per checkbox? that is, the event_id and the person_id
I'm not totally sure wether I got you right. This is the model I assume you're talking about:
# event.rb
class Event
has_many :people
scope :possible_people, -> { #whatever .. }
end
# person.rb
class Person
belongs_to :event
end
# events_controller.rb
class EventsController
def index
#events = Event.all
end
end
And this might be a possible solution to change an events relation to people:
# index.html.erb
<ul id="events">
<% #events.each do |event| %>
<li class="event">
<%= form_for #event do |form| %>
<% event.possible_people.each do |person| %>
<%= check_box_tag "event[person_ids][]", person.id, #event.people.include?(person) %>
<% end %>
<%= f.submit_tag 'Save Event' %>
<% end %>
</li>
<% end %>
</ul>
The important part is <%= check_box_tag "event[person_ids][]", person.id, #event.people.include?(person) %> where you actually change the the relation of a specific person to the event.
Good luck ;)
Well, you can try out something like below line, I am assuming you have a multiselect checkboxes and i am passing a Hash of event_id => plate_id as value to checkbox.
<%= check_box_tag 'booking[event_people_ids][]', {booking.event_id => booking.plate_id} %>
You will get the value in params as:
booking => {event_people_ids =>["{"72"=>"3"}}
I ended up doing this:
<%= check_box_tag "booking[]", "#{event.id}-#{person.id}" %>
and then in then to process them:
params[:booking].each do |booking|
booking = booking.split('-')
a = {
:booking_id => #booking.id,
:person_id => booking[1],
:event_id => booking[0]
}
Appointment.create(a)
end
I was hoping for a more railish way to do it but this works.
I am trying to display the output of this find -
#test = User.joins(:plans => [:categories => [:project => :presentations]]).where(current_user.id)
Here is my output loop
<% #test.each do |p| %>
<%= p.plans %>
<% p.plans.each do |d| %>
<%= debug(d) %>
<% d.categories.each do |e| %>
<% e.project.each do |r| %>
<%= debug(r) %>
<% end %>
<% end %>
<% end %>
<% end %>
The loop works until it gets to project when it throws this error
undefined method `each' for "#<Project:0x000001033d91c8>":Project
If I change it to projects in the loop it gives this error
undefined method `projects' for #<Plan:0x000001033da320>
The debug at categories level shows this
--- !ruby/object:Category
attributes:
id: 2
name: test
short_name: tst
created_at:
updated_at:
category_id: 2
plan_id: 5
My relationships look like this
User
has_many :user_plans
Plan
has_many :user_plans
has_and_belongs_to_many :categories
Category
has_one :project
has_and_belongs_to_many :plans
Project
has_many :presentations, :dependent => :delete_all
Presentation
belongs_to :project
Do I need to changed my find ?
Thanks, Alex
Category has_one :project
so it is single object not collection thus no each method.
According to your relationship definitions, Category only has_one project, so why do you want to iterate over e.project? If you just want to show debugging output, replace
<% e.project.each do |r| %>
<%= debug(r) %>
<% end %>
with
<%= debug(e.project) %>
But if you want to go deeper, into presentations, do:
<%= debug(e.project) %>
<% e.project.presentations.each do |presentation| %>
<%= debug(presentation) %>
<% end %>
Your problem is that you are calling the array method .each on a single object.
category.project will give you a single Project object right? That's not an array, so you can't call each on it.
Replace this:
<% e.project.each do |r| %>
<%= debug(r) %>
<% end %>
with
debug(e.project)
While you're at it, here's some other advice: use descriptive variable names. Why does 'p' represent a test, 'd' represent a plan, 'e' represent a category, etc? Variable names should tell you what the object is. Similarly, i'd expect the variable #test to hold a Test object. In your code it seems to be an array. Use plural variable names for a variable that holds a collection of that type of object - eg #plans would be an array of Plan objects.
eg
<% #tests.each do |test| %>
<% test.plans.each do |plan| %>
<%= debug(plan) %>
<% plan.categories.each do |category| %>
<%= debug(category.project) %>
<% end %>
<% end %>
<% end %>
Isn't that more readable?