serializing a non-form input for a link_to_remote call--how to do it?

Hey All,

I'm still working on my 'add people to this project' feature for my / projects/edit view. I've got a very nice AJAX search feature (thanks to the kind help here) that allows users to generate a list of people to potentially add to the project they're editing. I've also got an AJAX 'add this found person to this project' feature MOSTLY working-- am writing for help on fixing the last issue w/that.

Model wise, I've got Project & Person in a many-to-many, and ProjectPerson as the join model (so, Project has_many :people, :through => :project_person). The join model ProjectPerson has just one attribute of its own--role. My problem is that I can't figure out how to allow the user to edit this field before adding the association between the Project and the Person.

Here's the code I'm using to do the add. The AJAX search feature renders a partial that spits one of these out for each person meeting the search:

  <td><%= h p.person.nom %> </td>   <td><%= h p.person.organization.abbreviation %></td>   <td><%= select(p, :role, Person::ROLE_NAMES, :selected => p.person.typical_role) %> </td>   <td class='list-actions'>     <%= link_to_remote('Add',                         :url => {:controller => "project_people",                                  :action => "create",                                  :person_id => p.person,                                  :project_id => p.project,                                  :role => p.role #<-- Problem is here.                                  },                         :update => 'roster',                         :method => 'post'                         ) %>   </td>

('p' here is an instance of ProjectPerson). The select() helper gives me a nice drop-down, and p.person and p.project are both getting properly transmitted to the project_people.create action, but p.role is always blank. I suspect this is b/c at the time this is transmuted into HTML/javascript, p.role is blank, and there's nothing in my link_to_remote() call to tell the browser to go get whatever the user put in the drop-down corresponding to the call to select(). If that's it, I don't know how to do that (and when I go to 'view source' on the page the AJAXy bits don't show up, so I don't know how to investigate that). Assuming anybody's read this far, can you advise? (Do I need to use remote_form_for maybe?)

Inicdentally, if I change the indicated argument to:

  :role => p.person.typical_role

Then I do indeed get the contents of person.typical_role written to project_person.role. But I really want to allow the user to override that.

A thousand thanks in advance!

-Roy

Roy,

the way you have it set up now, the "role" in the url of the link_to_remote is getting generated on the server side and isn't linked to the select list.

Your instincts are right: you should be using remote_form_for here.

Ah, okay then--more reading/experimenting for me... :wink:

Thanks!

-Roy

Drat--remote_form_for is not working as desired. I've replaced my partial with this here:

  <% remote_form_for(:project_person, p, :url => {:controller => "project_people", :action => "create"}) do |pp_form| %>     <td><%= h p.person.nom %> </td>     <td><%= h p.person.organization.abbreviation %></td>     <td><%= pp_form.select(:role, Person::ROLE_NAMES, :selected => p.person.typical_role) %> </td>     <td><%= submit_tag 'Add' %></td>   <% end %>

But when I click the submit button for this, the browser posts the *outer* form--the one for editing the project. I tried making that submit_tag call pp_form.submit_tag, but that just got me a (undefined method `submit_tag' for #<ActionView::Helpers::FormBuilder: 0x4987c3c>).

Can you not nest a remote form inside a regular form_for block?

Thanks!

-Roy

But when I click the submit button for this, the browser posts the *outer* form--the one for editing the project. I tried making that submit_tag call pp_form.submit_tag, but that just got me a (undefined method `submit_tag' for #<ActionView::Helpers::FormBuilder: 0x4987c3c>).

Can you not nest a remote form inside a regular form_for block?

Nesting forms is not legal html.

Fred

  <td><%= h p.person.nom %> </td>   <td><%= h p.person.organization.abbreviation %></td>   <td><%= select(p, :role, Person::ROLE_NAMES, :selected => p.person.typical_role) %> </td> <td class='list-actions'>    <%= link_to_remote('Add',                        :url => {:controller => "project_people",                                 :action => "create",                                 :person_id => p.person,                                 :project_id => p.project,                                 :role => p.role #<--

<%= link_to_remote('Add',                         :url => {:controller => "project_people",                                  :action => "create",                                  :person_id => p.person,                                  :project_id => p.project},       :with => "'role='+encodeURIComponent($F('project_role'))"

(change project_role to the id of your select tag)

Fred

Thanks Fred! The problem is that I don't know how to control what that id winds up being. I tried throwing an :id argument on my call to select, like so:

  select(p, :role, Person::ROLE_NAMES, :selected => p.person.typical_role, :id=>"p_role")

(Figured I'd need the empty brackets b/c there are going to be a series of these--one per found person). But even still I get IDs like "__ProjectPerson:0x4a43734_role".

BTW--do I take it that, not only is nesting <form> elements verboten HTML, but having a series of separate form elements is also no good? I tried taking my remote_form_for stuff entirely outside the main form, but it seemed to have no effect...

Thanks!

-Roy

<%= link_to_remote('Add',                       :url => {:controller => "project_people",                                :action => "create",                                :person_id => p.person,                                :project_id => p.project},                       :with => "'role='+encodeURIComponent($F('project_role'))"

(change project_role to the id of your select tag)

Thanks Fred! The problem is that I don't know how to control what that id winds up being. I tried throwing an :id argument on my call to select, like so:

select(p, :role, Person::ROLE_NAMES, :selected => p.person.typical_role, :id=>"p_role")

select(p, :role, Person::ROLE_NAMES, {:selected => p.person.typical_role}, :id=>"foo")

(Figured I'd need the empty brackets b/c there are going to be a series of these--one per found person). But even still I get IDs like "__ProjectPerson:0x4a43734_role".

dom ids aren't allowed to contain

BTW--do I take it that, not only is nesting <form> elements verboten HTML, but having a series of separate form elements is also no good? I tried taking my remote_form_for stuff entirely outside the main form, but it seemed to have no effect...

Not sure what you mean by that.

> select(p, :role, Person::ROLE_NAMES, {:selected => > p.person.typical_role}, :id=>"foo") > (Figured I'd need the empty brackets b/c there are going to be a > series of these--one per found person). But even still I get IDs like > "__ProjectPerson:0x4a43734_role".

dom ids aren't allowed to contain

Taking those out does not seem to affect the id of the select input-- it's still unpredictable objectid goo. Can you tell me how to control what those IDs are set as? Or advise on how I can otherwise get a handle on the value of the select input so I can stuff it in the :with argument of link_to?

> BTW--do I take it that, not only is nesting <form> elements verboten > HTML, but having a series of separate form elements is also no good? > I tried taking my remote_form_for stuff entirely outside the main > form, but it seemed to have no effect...

Not sure what you mean by that.

Earlier in the thread I asked if it was verboten to have a remote_form_for nested w/in a form_for. You replied saying (I thought) that it was illegal to have nested <form> elements per the HTML standard. So I'm asking if it's also illegal to have multiple <form> elements in general? Would <form></form><form></form> also be illegal? If not, conceivably I could move my remote_form_for block outside the form_for block and have a hope of it working. (My experiment with doing that did not work.)

Thanks!

-Roy

select(p, :role, Person::ROLE_NAMES, {:selected => p.person.typical_role}, :id=>"foo") (Figured I'd need the empty brackets b/c there are going to be a series of these--one per found person). But even still I get IDs like "__ProjectPerson:0x4a43734_role".

dom ids aren't allowed to contain

Taking those out does not seem to affect the id of the select input-- it's still unpredictable objectid goo. Can you tell me how to control what those IDs are set as? Or advise on how I can otherwise get a handle on the value of the select input so I can stuff it in the :with argument of link_to?

I rather mangled my previous mail: to set the id of the select tag, you need

select(p, :role, Person::ROLE_NAMES, {:selected => p.person.typical_role}, :id=>"foo") (note the extra {})

BTW--do I take it that, not only is nesting <form> elements verboten HTML, but having a series of separate form elements is also no good? I tried taking my remote_form_for stuff entirely outside the main form, but it seemed to have no effect...

Not sure what you mean by that.

Earlier in the thread I asked if it was verboten to have a remote_form_for nested w/in a form_for. You replied saying (I thought) that it was illegal to have nested <form> elements per the HTML standard. So I'm asking if it's also illegal to have multiple <form> elements in general? Would <form></form><form></form> also be illegal? If not, conceivably I could move my remote_form_for block outside the form_for block and have a hope of it working. (My experiment with doing that did not work.)

You can have as many forms as you want.

Fred

I rather mangled my previous mail: to set the id of the select tag,

you need

select(p, :role, Person::ROLE_NAMES, {:selected =>

p.person.typical_role}, :id=>"foo") (note the extra {})

Kick-ass--thanks! I'm finding it tough to know which args should get batched up together into explicit hashes. I suppose that's a part of the learning curve here...

Now since this call is part of a loop through a collection, I had to add a counter var to unique-ify the ids:

  <% row_num = 0 %>   <% for p in ppl %>     <% row_num += 1 %>     <% role_id = "p_role_#{row_num.to_s}" %>     <tr class = "<%= cycle('line-even', 'line-odd') %>">       <td><%= h p.person.nom %> </td>       <td><%= h p.person.organization.abbreviation %></td>       <td><%= select(p, :role, Person::ROLE_NAMES, {:selected => p.person.typical_role}, :id => role_id) %> </td>       <td class='list-actions'><%= link_to_remote('Add',                             :url => {:controller => "project_people",                                      :action => "create",                                      :person_id => p.person,                                      :project_id => p.project,                                      :role => p.role},                             :update => 'roster',                             :method => 'post',                             :with => "'role='+encodeURIComponent($F('#{role_id}'))"                             ) %> </td>

      </tr>

I'm fine w/that, tho it does feel a bit ghetto to me. Is there a better way?

Thanks so much for taking the time!

-Roy

I rather mangled my previous mail: to set the id of the select tag,

you need

select(p, :role, Person::ROLE_NAMES, {:selected =>

p.person.typical_role}, :id=>"foo") (note the extra {})

Kick-ass--thanks! I'm finding it tough to know which args should get batched up together into explicit hashes. I suppose that's a part of the learning curve here...

Basically you can only omit the hash braces when it's the last
argument (how would ruby know where to draw the line).

Now since this call is part of a loop through a collection, I had to
add a counter var to unique-ify the ids:

<% row_num = 0 %> <% for p in ppl %>    <% row_num += 1 %>    <% role_id = "p_role_#{row_num.to_s}" %>    <tr class = "<%= cycle('line-even', 'line-odd') %>">       <td><%= h p.person.nom %> </td>       <td><%= h p.person.organization.abbreviation %></td>       <td><%= select(p, :role, Person::ROLE_NAMES, {:selected => p.person.typical_role}, :id => role_id) %> </td>      <td class='list-actions'><%= link_to_remote('Add',                            :url => {:controller => "project_people",                                     :action => "create",                                     :person_id => p.person,                                     :project_id => p.project,                                     :role => p.role},                            :update => 'roster',                            :method => 'post',                            :with => "'role='+encodeURIComponent($F('#{role_id}'))"                            ) %> </td>

     </tr>

I'm fine w/that, tho it does feel a bit ghetto to me. Is there a
better way?

Well I'd probably use each_with_index rather than the for loop, but
that's largely a question of taste. It probably looks neater when it's
not line wrapped to fit in an email.

Fred

Hi Roy,

Roy Pardee wrote:

Can you not nest a remote form inside a regular form_for block?

No, you cannot nest forms. That's what the W3C spec says. You can put multiple forms on a page. And you can make them _appear_, visually, to be nested. But you can't have a form tag within a form tag.

HTH, Bill

@Roy,

It appears that you're using the row_num (or index of each_with_index) to artificially create a unique dom id so that you can submit that dom id with the link_to_remote. Is that accurate? If so, I'd suggest that you use the person.id rather than creating an artificial one. Specifically, your 'role_id' could be "p_role_#{p.id}". Assuming that your ppl collection returns each person only once then you've got unique dom_ids every time.

Even better, if you're in Rails 2.x, you can use the dom_id helper method to create the id for you. You'd call it like this:

dom_id(p, :select_role_for) => 'select_role_for_person_12'

The first parameter is a model object. The helper uses the class name and id to form the base name in the dom id (here, person_12). The optional symbol after that is used as a prefix, giving you a nice, expressive id for your select element. Put those together and you could have something like this:

<% ppl.each do |employee| %> <tr class="<%= cycle('line-even', 'line-odd') %>">   <td><%= h employee.person.nom %></td>   <td><%= h employee.person.organization.abbreviation %>   <td><%= select(employee, :role, Person::ROLE_NAMES, {:selected=>employee.person.typical_role}, :id=>dom_id(employee, :role_for) %></td>   <td class='list-actions'>     <%= link_to_remote 'Add', :url=>project_people_path(employee.project, :person_id=>employee.person), :method=>:post, :update=>'roster', :with => "'role=' + encodeURIComponent($F('#{dom_id(employee, :role_for)}'))" ) %>   </td> </tr> <% end %>

HTH, AndyV

[mailto:rubyonrails-talk@googlegroups.com] On Behalf Of AndyV