I find there are a few very common patterns I use, I''d like to share
what
I''ve learned and I was also looking for others input, I''m
going to add a
wiki page to talk about this stuff. A built in way to handle this might also
make a good addition to the rails core.
PATTERN 1: you have an object and a has_many association, and you want to
give the user the ability to create/modify/remove those associations
automatically given 4 text fields, groups of text fields, or whatever. In my
case, I want to go further and make it automatically rearrange them if the
first is deleted, so I don''t store an index number in the database.
There
may be some situations I have not accounted for, from what I understand
MySQL pulls the records out in the same order they''re inserted, so the
order
they appear should always be the same.
I have a local service order and a number of phone lines they want on the
order, 4 text fields is all they deal with:
def phone_lines
# gather 1-4 phone lines
return if authenticate
@phone_lines = [
@order.phone_lines[0] || LocalBTN.new,
@order.phone_lines[1] || LocalLine.new,
@order.phone_lines[2] || LocalLine.new,
@order.phone_lines[3] || LocalLine.new, # simply add more lines like
this and more lines are displayed on the form
]
if @request.post?
invalid = false
@phone_lines.each_with_index do |line, i|
line.phone_number =
@params[''line''][i.to_s][''phone_number'']
if line.phone_number != ''''
line.type = (i == 0) ? ''LocalBTN'' :
''LocalLine'' # must be sure in
case BTN is deleted
line.order = @order # set order_id
line.billing_number = @phone_lines[0].phone_number
line.save or invalid = true
else
unless line.new_record?
line.destroy
end
end
end
return redirect_to next_action unless invalid # this is plan
selection, start with BTN
end
end
In my view, I can display them easily like this:
<% @phone_lines.each_with_index do |@line, index|
row = (index % 2) + 1 # row is used to assign alternating colors
line_number = index + 1
color = (line_number > 1) ? '''' : ''color:
#D22; '' %>
<%= render :partial => "components/object_error", :locals =>
{:object_error
=> @line} %>
<tr>
<td class="ordrow<%= row %>" width="220"
valign="top">
<label for="address_line<%= line_number %>">
<strong><span style="<%= color %>font-size:
16px;">Line <%= line_number
%>:</span></strong><br/>
<small><%= line_number == 1 ? ''This will be the main billing
number.'' :
''Additional phone line.'' %></small>
</label>
</td>
<td class="ordrow<%= row %>" valign="top">
<%= text_field "line", "phone_number", "index"
=> index, "maxlength" => 20,
"class" => "ord-widget" %>
</td>
</tr>
<% end %>
I have found this to be the most efficient code I could come up with. If
anybody has done something similar and can offer some input, I would
appreciate it.
PATTERN 2: You have the first object with a belongs_to association to a
second table full of objects. You want the user to be able to pick from the
second table full of objects one object, using a radio button, and update
the first object. This one is a bit simpler.
Here is the controller code, in this example we are choosing a rate_code for
the order object:
def plan_selection
return if authenticate
if @request.post?
if @params[''rate_code''].nil?
@errors = "You didn''t select a rate plan. Please choose
from the
list below."
else
begin
@order.rate_code =
WirelessRateCode.find(@params[''rate_code''][''id''])
if @order.save
return redirect_to next_action
end
rescue ActiveRecord::RecordNotFound
# ignore this and redisplay the page
end
end
end
@rate_code = @order.rate_code || WirelessRateCode.new
end
And the view:
<% @order.product.rate_codes.each_index do |index|
rate_code = @order.product.rate_codes[index]
row = (index % 2) + 1
checked = TRUE if rate_code == @rate_code %>
<tr>
<td class="ordrow<%= row %>" width="450"
valign="top">
<label for="<%= rate_code.id %>">
<strong
onMouseOver="this.style.textDecoration=''underline'';"
onMouseOut="this.style.textDecoration=''none'';"><%=
radio_button "rate_code",
"id", rate_code.id, "id" => rate_code.id
%> <%= rate_code.name
%></strong><br/>
<small><%= rate_code.description %></small>
</label>
</td>
<td class="ordrow<%= row %>" valign="top"
align="center">
<%= rate_code.included_minutes %>
</td>
<td class="ordrow<%= row %>" valign="top"
align="center">
<%= rate_code.price %>
</td>
</tr>
PATTERN 3: Similar to pattern 2, but in this case you are selecting
MULTIPLE things you want added to the page, using an intermediate join
table. I haven''t finished my design for this one, this is a
complicated
beast so far, seems like it could be really simple, going to give it a lot
of thought at this point. It seems a has_and_belongs_to_many relationship
would work best in this case to make the model simpler, but you may want to
have the intermediate table more discrete in your model, in either case,
collecting the checkboxes and updating the intermediate table in the least
number of transactions is the difficult part. I am thinking just delete
everything matching the association on every post request and inserting them
all over again.
Feel free to submit your thoughts if you have encountered any of these
patterns.
-Jeff