I have a single page with 72 time slots each with a radio button beside it for available / unavailable.
I want the physician to be able to select whether they're available or not, then click "Update" when they're done.
This means the form must accept all 72 records.
I (think) I can work out how to do this with some very low level programming. But I don't know what the 'rails way' is?
form_for
, but use form_tag
insteadPlease don't use this idea if it's bad (which it probably is). But what I had in mind was to first generate all possible hours in the next 3 days
time_now = Time.now
current_time = time_now
three_days = time_now + 3.days
hours = []
(current_time.beginning_of_hour.to_datetime.to_i .. three_days.beginning_of_hour.to_datetime.to_i).step(1.hour) do |date|
hours << Time.at(date)
end
@hours = hours
Then, using these start times as keys in a hash, assuming available: false
as the value in the hash (unless Availability.where(user: current_user).all
says otherwise), and passing that to the view to set the default positions of the radio buttons, then the physician can update the radio buttons as they please and submit when they're done.
The problems are
i) I have no idea if this is a good/bad/ugly approach ii) (the main question) how would I go about creating such a form with a radio button for each key/value pair in the hash? (one single submit button)
Note: so far, the only forms I know in rails are via form_for
, but I'm keen to use whatever is best practice for this type of form
There is no 'rails way' for this. Rails is opinionated in many ways. But, not in this way.
It seems to me you have at least two parts to your problem. First is constructing an appropriate data structure. I think you can clean up your existing code a little by doing:
time_now = Time.now
start_time = time_now.beginning_of_hour.to_datetime.to_i
end_time = (time_now + 3.hours).beginning_of_hour.to_datetime.to_i
@hours = (start_time..end_time).step(1.hour).map {|t| Time.at(t)}
...which is mostly cosmetic. (Please note that I'm using 3.hours
instead of 3.days
just to keep the output shorter.) It saves you a couple of assignments and uses .map
instead of that whole pushing to array
bit that you're doing.
I'm speculating that you'll want an array
of hash
es, not an array of time
s. Perhaps something like:
time_now = Time.now
start_time = time_now.beginning_of_hour.to_datetime.to_i
end_time = (time_now + 3.hours).beginning_of_hour.to_datetime.to_i
@hours = (start_time..end_time).step(1.hour).map {|t| {time_slot: Time.at(t)}}
...and that you'll probably want to include whether the time_slot
is currently available:
time_now = Time.now
start_time = time_now.beginning_of_hour.to_datetime.to_i
end_time = (time_now + 3.hours).beginning_of_hour.to_datetime.to_i
@hours = (start_time..end_time).step(1.hour).map do |t|
time_slot = Time.at(t)
{time_slot: time_slot, time_slot_available?: time_slot_available?(time_slot)}
end
...which requires a time_slot_available?
method. For testing purposes, I've stubbed this as:
def time_slot_available?(time_slot)
[true, false].sample
end
...which will end up giving you something like:
[
{:time_slot=>2020-08-21 08:00:00 -0700, :time_slot_available?=>false},
{:time_slot=>2020-08-21 09:00:00 -0700, :time_slot_available?=>true},
{:time_slot=>2020-08-21 10:00:00 -0700, :time_slot_available?=>false},
{:time_slot=>2020-08-21 11:00:00 -0700, :time_slot_available?=>false}
]
Naturally, time_slot_available?
would check the database. This particular approach is bad because it probably yields and N+1, but this is just for illustration purposes.
Now, you could create a single, massive form as you are describing. Or, you could generate a radio button with no form for each time_slot
and then attach some javascript that makes a ajax call each time a button is clicked - setting the time_slot
as available or not available, as appropriate.
I like the latter from a UX perspective (the work gets saved as the user clicks buttons), but it is a choice of preference, style, programming skill, and probably other stuff.