In the model Code
there is a field named week_days
that could be something like this 1100110
.
Instead of a classic input field, I want to display 7 checkboxes in an ActiveAdmin form like this
form do |f|
f.inputs do
f.input :name
f.input :week_days
end
f.actions
end
so I thought to write a mapper like this in the model
def week_days_checkboxes
[
['sunday', 1],
['monday', 1],
['tuesday', 1],
['wednesday', 1],
['thursday', 1],
['friday', 1],
['saturday', 1]
]
end
def week_days_checkboxes=(week_days_checkboxes)
@week_days = week_days_checkboxes.reverse.join.to_i(2)
end
and then change the form input this way
f.input :week_days_checkboxes, as: :check_boxes
but it doesn't work. What do I have to do?
PS: the second value of each pair should be 1 or 0, basing on each value of the field week_days
Thanks a lot
It doesn't work for several reasons:
self[:column_name]= value
or write_attribute(:column_name, value)
(I think the latter is more intention revealing). For reading you could use self[:column_name]
or the read_attribute
method.['', '1']
regardless of what day you selected. Basically using uniform values is not the way to go in this situation.Returning an array of arrays from week_days_checkboxes
method will not display the list of choices since it represents the value not the choices. You have to explicitly set the collection.
f.input :week_days, as: :check_boxes, collection: week_days_for_select
Where week_days_for_select
returns an appropriate list.
And when your are editing an existing model the previously selected days won't be selected in the checkbox list. For that to work you need to return a list of values that match to the checkbox inputs' values.
One solution could be:
In your view:
f.input :week_days, as: :check_boxes, collection: Date::DAYNAMES
And then in your model create a wrapper for week_days
so it accepts a list and turns it into an integer and vice versa.
def week_days
return [] if read_attribute(:week_days).blank?
# first you have to convert the integer to a binary array
w = read_attribute(:week_days)
days_as_binary = Math.log2(w).floor.downto(0).map { |nth_bit| w[nth_bit] }
# alternatively
# days_as_binary = read_attribute(:week_days).to_s(2).split('').map(&:to_i)
# make sure it has an element for every day of the week
padded_binary = [0] * (7 - days_as_binary.size) + days_as_binary
# map the binary array to day names
padded_binary.each_with_index.map do |d, idx|
Date::DAYNAMES[idx] if d == 1
end.compact
end
def week_days=(values)
days = Date::DAYNAMES.map { |d| values.include?(d) ? 1 : 0 }
write_attribute(:week_days, days.join.to_i(2))
end
Although I think representing the selected days as an integer is not the best option you have. If you are using PostgreSQL, AR 4+ has good support for arrays - you could save a list of days to the database.