Topic: collection_select in form partial for new and edit methods

hi,

i have a one to many relationship:
arrangement belongs to card
card has many arrangements


i have created a partial for my form which is used by the new and edit methods. here's a part of it:

<% form_for(@arrangement) do |f| %>
  <%= f.collection_select(:card_id,  @cards, :id, :name) %>
<% end %>

that would work on creating a new arrangement but as you can imagine, i get unexpected nil errors on the edit action, for this partial.

i suppose i could use something like this to avoid this problem:

<% if params[:action] == 'edit' %>
  #edit
<% else %>
  #new
<% end %>

but that's not too DRY and i'm sure Rails has something more elegant. any advice please?




also, if it helps, here's my edit function in the Arrangements controller.

def edit
  @arrangement = Arrangement.find(params[:id])
  @card = Card.find(@arrangement.card_id)
end

thanks.

Re: collection_select in form partial for new and edit methods

i think i found the problem, it was a silly typo on my part.
i made @card to be @cards, in my edit method in the controller.

but now the error reads:

undefined method `map' for #<Card:0xb5f5c73c>

ideas anyone?

Re: collection_select in form partial for new and edit methods

The edit action should look like this:

def edit
  @arrangement = Arrangement.find(params[:id])
  @cards = Card.find(:all)
end

The select gets populated through @cards, which should contain all Card values for the select list.
The selection of the current value in this dropdown (which you tried to use the @card for, is assume) is done by the form_for by using the card_id from the @arrangement variable, much like any other form control in the edit screen gets populated with the values of this variable.

PS: moved to "Controllers and Views"

Re: collection_select in form partial for new and edit methods

Duplex, thanks. that works.

makes sense too...earlier i was selecting one item, i dont know why i didnt realize that. thanks again.


on a related matter, i have this in the same view:

<% for occasion in Occasion.find(:all) %>
  <%= check_box_tag "arrangement[occasion_ids][]", occasion.id, @arrangement.occasions.include?(occasion) %> <%= occasion.title + "&nbsp;" %>
<% end %>

arrangement HABTM occasions and vice versa.


this is the output:

<input id="arrangement[occasion_ids][]" name="arrangement[occasion_ids][]" type="checkbox" value="1" /> Wedding&nbsp;
<input id="arrangement[occasion_ids][]" name="arrangement[occasion_ids][]" type="checkbox" value="2" /> Birthday&nbsp;
<input id="arrangement[occasion_ids][]" name="arrangement[occasion_ids][]" type="checkbox" value="3" /> Promotion&nbsp;
<input id="arrangement[occasion_ids][]" name="arrangement[occasion_ids][]" type="checkbox" value="4" /> Funeral&nbsp;
<input id="arrangement[occasion_ids][]" name="arrangement[occasion_ids][]" type="checkbox" value="5" /> New-born&nbsp;

my problem here is that, in the interest of keeping my pages XHTML valid, i'd like to make my checkbox elements id's contain characters frmo A-Z|a-z|0-9. unfortunately, i have square braces in there too. i read the api for this tag but didnt fully understand how to modify this to achieve what i want.



thanks.

Re: collection_select in form partial for new and edit methods

<% Occasion.find(:all).each_with_index do |occasion, i| %>
  <%= check_box_tag "arrangement[occasion_ids][]", occasion.id,
                    @arrangement.occasions.include?(occasion),
                    {:id => "arrangement[occasion_id_#{i}"} %>
  <%= occasion.title + "&nbsp;" %>
<% end %>

The last parameter of all *_tag helpers is a hash with options for the tag's attributes.

This should produce somethign like:

# <input id="arrangement_occasion_ids_1" name="arrangement[occasion_ids][]" type="checkbox" value="1" /> Wedding&nbsp;
# <input id="arrangement_occasion_ids_2" name="arrangement[occasion_ids][]" type="checkbox" value="2" /> Birthday&nbsp;

which has the positive sideeffect that you don't have duplicte id's which would be invalid HTML too

PS: Instead of the index, you could also use the occasions id, useful if you ever had to access those checkboxes with javascript ...

Re: collection_select in form partial for new and edit methods

oh i see, so you code the tag as usual and then explicitly specify id.

that worked fine too, thanks a lot.

i actually have 3 different sets of checkboxes on the same page (occasions, emotions and types) and thats a lot of code so i could just stick it in a partial and pass in the model instance var as a :locals, right?


btw, what do you call the find_by* and the each_with* functions? i mean the ones that use ORM to dynamically create function names because i'd like to learn more about these but i don't know what to search for.


Duplex wrote:

PS: Instead of the index, you could also use the occasions id, useful if you ever had to access those checkboxes with javascript ...

sorry, but could you please explain further? i'm afraid i don't quite follow.


thanks for the replies.

Re: collection_select in form partial for new and edit methods

you mix up 2 different things here:
1) find_by_*methods are generated by rails and explained quite well in the API of ActiveRecord::Base

2) each_with_index is Ruby method of the Array class, and simply is the same as doing @array.each do |element_in_array|, but it also passes the array index of the element into the block (like you would access an element through index: @array[0] => first element in the array etc...)

what i meant by using the id is not using the index of the array (which will always count from 0 upwards no matter the content) but use the value of the id attribute of the occation this checkbox is for.

That way, you can easily find the element for this occation through javascript. Might not be of interest for you here, but think of deleting a comment through ajax, then you need a way to remove the html tags belonging to this comment from the page ... if each comment now has the real (database) id in his (html)id value, you can easily target and delete it with javascript.

Re: collection_select in form partial for new and edit methods

thanks for clearing that out Duplex, i assumed each_with_index was something else since i saw it in your post the first time.

i was able to find the find_by_ methods. im still not sure what methods belong to which functions as i started using rails about 1.5 months ago so you pointing me to AR helped.

i understand what you meant by id's and indices too and i agree, thats a better codign practice, thanks for bringing that to my attention.









if it helps anyone, i put my checkbox code into a partial and render it from the main file since i have many sets of check boxes and will end up having some HTML elements around it.

this is my main view:

<p>
  <strong>Occasions</strong>
  <%= render :partial => 'tags_display', :object => @occasions, :locals => { :elements => "occasion", :f => f } %>
</p>

that renders _tags_display.html.erb, makes the object @occasions available to the partial as well as the other two locals, 'elements' and 'f'
btw, the @occasions is assigned the object in my controller.

i would do this for another set of checkboxes, for example:

<p>
  <strong>Blah</strong>
  <%= render :partial => 'tags_display', :object => @blah, :locals => { :elements => "blah", :f => f } %>
</p>

this its _tags_display.html.erb:
<%- tags_display.each_with_index do |iterator, i| -%>
  <%= check_box_tag "arrangement[#{elements}_ids][]", iterator.id,
    @arrangement.occasions.include?(iterator),
    {:id => "arrangement_#{elements}_id_#{iterator.id}"} %>
  <%= f.label "#{elements}_id_#{iterator.id}", iterator.title + "&nbsp;" -%><br />
<%- end -%>

the object you passed in the render method is available in this partial as a variable named tag_display (same name as the basename of the file) by default, in Rails



all that would render something like this:

<p>
  <strong>Occasions</strong>
  <input id="arrangement_occasion_id_7" name="arrangement[occasion_ids][]" type="checkbox" value="7" /> <label for="arrangement_occasion_id_7">Wedding&nbsp;</label><br />
  <input id="arrangement_occasion_id_8" name="arrangement[occasion_ids][]" type="checkbox" value="8" /> <label for="arrangement_occasion_id_8">Birthday&nbsp;</label><br />
</p>

Last edited by rebelx (2008-06-15 16:38:44)