views:

396

answers:

3

I have a many-to-many relationship with a link box, and I want to pull those models together into one form so I can update from the same page. I'm really struggling with getting the check_box to even show all the elements of my array - I've scoured the net and been working on this literally all day, and I'm finding it difficult to apply the information I'm reading to my problem. I'm also extremely new to RoR and I've been following a bit of an outdated video tutorial (pre 2.0) so apologies for my code. So far, I've got it to output only one key pair in the array (the last one) - although outside the form, the code used in the tutorial works exactly how it should. Thats of little use though! Host is the model for which the main form is for, and Billing is the outside model that I'm trying to add to the form.

This is the code that works outside of the form from the tutorial:

<% for billing in @billings -%>
<%= check_box_tag('billing_title[]', billing.id, @host.billings.collect
{|obj| obj.id}.include?(billing.id))%> <%= billing.title %><br />
<% end -%>

I just need to know how to make it work inside the form. This is the aforementioned code that only retrieves the last array keypair after looping through them:

<% f.fields_for :billings do |obj| %><br />
<%= check_box_tag('billing_title[]', billing.id, @billings.collect
{|obj| obj.id}.include?(billing.id))%> <%= billing.title %><br />
<% end %>

The debug(@billings) :

--- 
- !ruby/object:Billing 
attributes: 
title: Every Month
id: "1"
attributes_cache: {}

- !ruby/object:Billing 
attributes: 
title: 12 Months
id: "2"
attributes_cache: {}

- !ruby/object:Billing 
attributes: 
title: 6 Months
id: "5"
attributes_cache: {}

Any help really appreciated.

A: 

i'm not sure about my answer, but i'll try...

<% f.fields_for :billings, @billings do |obj| %>
   # ...
<% end %>
j.
Thanks for the attempt :) though this gives me the exact same output as the method I tried - I've actually tried quite a few variations of it over the course of the day (yep, I've been working on this one problem for around 10 hours today argh!) cheers again :)
Craig Whitley
this code gives you only one checkbox?
j.
Yeah, this updated code gives me the same as the one I posted - one checkbox (the last in the array - 6 months') - mini-screengrab: http://i39.tinypic.com/xcvq6q.jpg
Craig Whitley
+1  A: 

Craig, it sounds like what you are looking for is accepts_nested_attributes_for which is a much better way of handling nested models in a form.

Rather than just steal all of Ryan's work and repost it here, I'll just give you a link to his screencast:

http://railscasts.com/episodes/196-nested-model-form-part-1

This is based off his complex forms series.

You should be able to taylor this tutorial to what you are trying to do, but if not I'd be happy to help.

EDIT:

Alright, after looking at your code, there are a few things causing you problems. :)

First of all, with associations, you don't need to pull an extra collection for billings, i.e.:

@host = Host.find(params[:id])
@voips = Voip.find(:all)
@custsupps = Custsupp.find(:all)
@payments = Payment.find(:all)
@billings = Billing.find(:all) # <-- This is not needed and causing your problems

The association setup in the model does this all for you. This is part of the magic of rails. :D

Now, it is important to note that when using associations you need to make sure that the objects are actually associated. In other words, if you have 3 Billings objects in your database and they are not associated with your Host object, they won't show up in the form.

If you are trying to associate a billing TO a Host using a checkbox you are going to want to take a different approach, because your form with only display Billings already associated with your Host.

If your just trying to edit or modify existing Billings that are associated to the Host, where the checkbox represents a 'paid' attribute (a boolean) for instance, then this approach is fine and your form code would look something like this:

<% f.fields_for :billings do |b| %><br />
  <%= b.check_box :paid %> <%= b.title %>
<% end %>

So perhaps clarify what your trying to accomplish from a functionality standpoint and we can find a better solution.

Dustin M.
Thanks for replying dustmoo! I've actually seen that tutorial and already have my accepts_nested_attributes_for inplace in my Host model, at the moment I haven't even got to the stage of submitting my form yet - I can send the array to the form page, but I can't build my check_boxes using the field_for function. Its a right headache! but I'd really appreciate your help in working this out, I'm almost at the point of scrapping RoR and going back to php as I've been working on this for too long - but really I want to sort this out - I don't like quitting :) what info can I supply you?
Craig Whitley
http://pastebin.com/Q4znSMyAHeres a pastebin if it helps!
Craig Whitley
You know, I must have been asleep at the wheel yesterday, I just looked at your code again and see the problems. :D I'll post a fix soon.
Dustin M.
Thats an interesting point dustmoo - so I could put it in a partial and it would work just the same? Could I put the fields_for in a loop of its own to create the list of check_boxes? Thanks loads for the help!
Craig Whitley
I'm glad its something obvious in a way haha, means it shouldnt (in theory) be too difficult to fix? This is the only page in the whole backend that needs this function, so once its over with I can carry on with the creating :) your helps really appreciated thanks.
Craig Whitley
lol.. well not too obvious, I think it is just a disconnect between what you are trying to accomplish and Rails conventions. :) Read my edits and we'll go from there. :)
Dustin M.
Partials are useful if you want to use AJAX to dynamically add and remove new Billings from the form, like in Ryan's examples. I removed the comment because that will come after we get the functionality working. :D
Dustin M.
Ahh yeah, I should really clarify. Maybe a pic will help? This is what I knocked up in PHP:http://i41.tinypic.com/70x2rr.jpgI just wanted a single page where I could do the whole CRUD for this particular model in one form. So the way I did it in php was:1) Generated all the checkboxes from the different tables. I.e. did a select query on each table, and at the same time I checked against the link table to see if the host did or didnt have the selection. If they did, I set a variable to "CHECKED" - if not I set the variable to "". That generated the screenie I posted above.
Craig Whitley
Then after submission, I first did an INSERT on whichever boxes were checked, and otherwise did a delete (both on the link table).So if a box had been unchecked, it deleted it - if it had been checked, it inserted it - hope this isnt too complex, i'm quite new to programming so theres probably a million different (and easier) ways to do the same thing!
Craig Whitley
Great Craig, so it appears to me that you want a separate billings, voip and support models so you can add and remove the options available to the customer under their host account? (Just so I am totally clear) And you basically want the recreate the form above?
Dustin M.
Thats correct :) I reduced it down to just the billings for this, as I figured if I get it sorted once I can duplicate the method for the others. This is slightly different from the php version, as I also want to be able to edit the actual host details on the same page (whereas with the php version I had the checkboxes on a seperate page - although I could change that fairly easily now its written!). So yeah.. a central location to edit all of the options available to the host. Incidentally, I have models and controllers already generated and linked to the Host model for the others :)
Craig Whitley
Great, I think for sake of simplicity I am going to put together something and then post it. :) Check back in about 15 minutes or so..
Dustin M.
Well Craig, it really is a bugger having maximum flexibility and edit both in the form. The easiest way of course would be to include those options in your Host model, but if you want to be able add and remove them dynamically on the back end that won't work for you.To basically activate and deactivate options through association is troublesome. At least, from a convention standpoint. I don't know how to do it easily.. I can hack something together that works, but I don't know how much that will help you.
Dustin M.
Thanks for the help dustmoo, appreciate it :) I think after all this then maybe I'll finish this site in php and come back to rails at a later date (I have another website idea after this one which I'm sure will fit rails nicely!) - again though, cheers for giving it a go for me, appreciated :)
Craig Whitley
Great, I would use ajax and a custom controller for this type of thing, not nested attributes, because this falls outside of convention. Anyway, good luck, it was a good brain exercise at least. :)
Dustin M.
A: 

Looks to me like it's a problem with the for-loops...

In your example code, you are looping through the @billings (an array of billings objects passed from your controller), and calling the current one billing.

In your form code, you are looping through the :billings (symbol referencing the billings ActiveRecords) and calling the current one obj. Then, confusingly, your code re-uses the variable name obj again in the @billings.collect function... but this should have local private scope and not effect your main loop, but it is hard to interpret on sight.

Anyway, since the stuff inside your form loop doesn't reference obj, it is using the variable billing--which does not change when you loop through, but keeps the last value it had: the last item from the previous loop! This explains why you only see the last tag. (Also, the debug(@billings) function does its own looping through the records of sorts--it is not inside the fields_for do loop.)

I would start with changing the first line of your form code to re-use the variable name billing again, like this:

<% f.fields_for :billings do |billing| %><br />

Ruby's block syntax can be pretty confusing at first--or, heck, anytime you're reading code you didn't write yourself--but when you break it all down to the basic components, it's usually just looping through a set of items and performing some function on each one. Also watch out for the confusion between similar-looking names: @billing, @billings, :billings and billings are all different things to Ruby and Rails. Good luck!

ewall
Thanks for this :) I've changed the line you mentioned, and now I'm getting an error: undefined method `title' for #<ActionView::Helpers::FormBuilder:0x48a0c08>This must relate to the billing.title which is used to label my form: <%= billing.title %>Although if I remove the label completely, I then (again) only get a single (this time unlabelled) check box for the last item in the array. This is all very confusing, but I really appreciate your help. I mean the debug shows the key pairs are there in the billings array - I just cant seem to loop through the array! Thanks again
Craig Whitley