#75
Oct 15, 2007

Complex Forms Part 3

In this third and final episode on complex forms I will show you how to edit a project and multiple tasks all in one form. This includes removing and adding tasks dynamically as well. See the show notes for updated code.
Download (25.7 MB, 16:11)
alternative download for iPod & Apple TV (20.5 MB, 16:11)

Resources

Update (3/29/2008)

The code for this screencast isn’t the greatest, it requires the :index option for the fields and does not work well in Rails 2. Instead I recommend going with the approach I show in Advanced Rails Recipes. Here’s the code (posted by permission). Check out the recipe for details on how it works.

If you want, you can also view the original code.

<!-- projects/edit.rhtml -->
<% form_for :project, :url => project_path(@project), :html => { :method => 'put' } do |f| %>
  <%= render :partial => 'fields', :locals => { :f => f } %>
  <p><%= submit_tag "Update Project" %></p>
<% end %>

<!-- projects/new.rhtml -->
<% form_for :project, :url => projects_path do |f| %>
  <%= render :partial => 'fields', :locals => { :f => f } %>
  <p><%= submit_tag "Create Project" %></p>
<% end %>

<!-- projects/_fields.rhtml -->
<p>
  Name: <%= f.text_field :name %>
</p>
<div id="tasks">
  <%= render :partial => 'task', :collection => @project.tasks %>
</div>
<p><%= add_task_link "Add a task" %></p>


<!-- projects/_task.rhtml -->
<div class="task">
  <% fields_for_task(task) do |task_form| %>
  <p>
    Task: <%= task_form.text_field :name %>
    <%= link_to_function "remove", "$(this).up('.task').remove()" %>
  </p>
  <% end %>
</div>
# models/project.rb
class Project < ActiveRecord::Base
  has_many :tasks, :dependent => :destroy

  validates_presence_of :name
  validates_associated :tasks
  
  after_update :save_tasks

  def new_task_attributes=(task_attributes)
    task_attributes.each do |attributes|
      tasks.build(attributes)
    end
  end
  
  def existing_task_attributes=(task_attributes)
    tasks.reject(&:new_record?).each do |task|
      attributes = task_attributes[task.id.to_s]
      if attributes
        task.attributes = attributes
      else
        tasks.delete(task)
      end
    end
  end
  
  def save_tasks
    tasks.each do |task|
      task.save(false)
    end
  end
end


# models/task.rb
class Task < ActiveRecord::Base
  belongs_to :project
  validates_presence_of :name
end


# projects_controller.rb
def new
  @project = Project.new
  @project.tasks.build
end

def create
  @project = Project.new(params[:project])
  if @project.save
    flash[:notice] = "Successfully created project and tasks."
    redirect_to projects_path
  else
    render :action => 'new'
  end
end

def edit
  @project = Project.find(params[:id])
end

def update
  params[:project][:existing_task_attributes] ||= {}
  
  @project = Project.find(params[:id])
  if @project.update_attributes(params[:project])
    flash[:notice] = "Successfully updated project and tasks."
    redirect_to project_path(@project)
  else
    render :action => 'edit'
  end
end


# projects_helper.rb
def fields_for_task(task, &block)
  prefix = task.new_record? ? 'new' : 'existing'
  fields_for("project[#{prefix}_task_attributes][]", task, &block)
end

def add_task_link(name) 
  link_to_function name do |page| 
    page.insert_html :bottom, :tasks, :partial => 'task', :object => Task.new 
  end 
end 

Validation

Displaying the validation errors can be tricky depending on your needs. It may be as simple as displaying the project validations:

<!-- projects/_fields.rhtml -->
<%= error_messages_for :project %>

This will mention if there are task validation errors, however it will not state exactly what the errors are because these are stored in each task model.

If you need to display the task error messages. One way is to do this:

<!-- projects/_task.rhtml -->
<% @task = task %>
<%= error_messages_for :task %>

The error_messages_for method requires an instance variable with the same name, so that is why it needs to be set right there. This is an ugly work around. If you want to do it in a cleaner way or handle the error messages differently then I recommend creating your own validation helper method instead. I may go into more details in a future episode.

If you want to share your validation troubles feel free to add a comment!

RSS Feed for Episode Comments 241 comments

1. Jorge Calás Oct 15, 2007 at 01:54

Great! As always.

Thanks very much for sharing all this, helping us to be better programmers.

Watching your screencast has become my every monday's firt task ;)


2. Ilya Sabanin Oct 15, 2007 at 04:27

I believe that you should delete :id from parameters before model going to save or you will get error like this: "Can't mass-assign these protected attributes: id".

Here is the code of method in the Project model that will fix that issue:

def task_attributes=(task_attributes)
    task_attributes.each do |attributes|
      unless id = attributes.delete(:id)
        tasks.build(attributes)
      else
        task = tasks.detect { |t| t.id == id.to_i }
        task.attributes = attributes
      end
    end
  end


3. pimpmaster Oct 15, 2007 at 04:36

That was probably the most intense Railscast yet. Unbelievable how much ground you covered.

Excellent stuff as always

+1 vote for a future episode on associated validations.

:)


4. Andrew Turner Oct 15, 2007 at 06:21

Have you tried out this technique in Rails 2.0 preview? The task_attributes= method doesn't seem to be getting called on @project.update_attributes.

However, the Task#update method is still getting called, so I wonder if this was another change in Rails 2.0, eliminating the need for the after_update filter on Project.


5. George Oct 15, 2007 at 07:56

Hi Ryan,

Nice screencast! :)

One thing came to mind, though: instead of sending destroy flags from the client side, wouldn't it be easier to set the flag in Project#task_attributes= for any record for which there's no attributes passed in?


6. Ryan Bates Oct 15, 2007 at 08:22

@Ilya & Andrew, I have yet to test this extensively in edge rails and 2.0 preview. I'll do that and try to post the fixes in the show notes.

@George, that's an interesting idea. I hadn't thought of that! I'm wondering if it will be anymore difficult to carry the state across when validation fails. I'll have to play around with it. Thanks!


7. TheCompWiz Oct 15, 2007 at 10:05

Just out of curiosity... is there any reason you chose to use "task.save" as opposed to "task.update_attributes"?


8. Ted Oct 15, 2007 at 10:51

Hey Ryan, another great episode.

I hope somebody convinces you do to a part 4 covering the error messages :-)

Just a matter of style preference, shouldn't save_tasks in Project be marked as private? Perhaps it's just my old-school tendencies but I try to make everything private by default and only expose what needs to be exposed. I understand if you just didn't bother in order to keep the episode on topic.


9. Taalas Oct 15, 2007 at 12:25

Hey Ryan,
thanks for another curious ingenious screencast. Before I was doing that Javascript stuff with rendering a partial into a string and then inserting this with js. But your method is more rubyish.

But one thing is getting me a headache. You are using :index => nil in the form helpers for text_field. I tried to use that with select but it does not work. The index is rendered anyway. Do you have any clue?


10. Wes Oct 15, 2007 at 13:10

Taalas,

You have to pass an empty hash as an option before the :index => nil

Something like this will work:
f.select :field, [ "option 1", "option 2" ], {}, :index => nil

Figured this out from http://dev.rubyonrails.org/ticket/589


11. Taalas Oct 15, 2007 at 13:25

Hi Wes,

Thanks for that. I was stuck often because of that "empty hash" thing...
I don't know why I always tap into that trap.


12. Etandrib Oct 15, 2007 at 14:04

JS question:

How do I make the mark_for_destroy function more generic so I can apply it to multiple models (ie. phones, email addresses, etc.)

right now it is the following:
  $(element).up('.phone').hide();

I'm not proficient in JS so any basic help is appreciated. Thanks!


13. Brandon Oct 15, 2007 at 15:25

As I was watching this episode, I notice in your application layout you have a helper called show_title?, related to your "Pretty Page Titles" from episode 30. I was wondering how that particular little snippet works.

I love these screencasts! I've been working on Rails for almost a year now, and you seem to come out with the one that I need just as I need it.


14. Ryan McGeary Oct 15, 2007 at 16:53

Nice episode. I agree that the ":index => nil" snippets are a bit ugly, so I submitted a patch that adds support to pass the index option to the fields_for helper:

http://dev.rubyonrails.org/ticket/9883

Please feel free to "+1" the patch if this patch looks of interest to you.


15. erv2 Oct 15, 2007 at 16:58

Thanks ryan, this has to be one of the longest railscasts.

i would like an episode on error messages. i have two models, one is post, the other is photo. photo is a model using image_column, users can upload 'photo' while they create a post, but it's also important to display the error message of 'photo' while saving, as image_column does validations like format check and size check. i guess quite a lot people are doing it this way, so i really hope there will be an episode on error messages.

once again, thanks ryan for a great episode!!


16. Brian Oct 15, 2007 at 17:43

I love this technique and use it on a few projects I'm working on. A few bits of contention from an accessibility stand point:
1. You should use label tags instead of plain text.
2. One thing that I'd like to see more of is unobtrusive javascript. It's pretty easy to pick up and makes pages more accessible. I think it'd be great for you to push this since you're in a position to reach a lot of people.


17. Michael Oct 15, 2007 at 20:13

Nice tutorial Ryan, I took at stab at pluginizing this technique and have uploaded the plugin and a sample project using it at: http://code.google.com/p/multimodel-forms/

If anyone has any suggestion feel free to email me (my email is on the project home page), or just add it under "Issues". Im currently working on view helpers so try and simplify the views alot.


18. Joe Oct 15, 2007 at 21:25

This is the most awesome yet! I agree with the many others! "Complex Forms Part 4" Single Form Multiple Model Validations! That would be so much help to so many people. Thanks for your amazing work.


19. Joe Oct 16, 2007 at 02:07

I have a question. Let's say my project has many tasks, but it also has many tags and I want the project form to be able to accommodate building two models in reference to a project on the same form. How would one refactor the following *_attributes=(*_attributes) methods to avoid duplication?

  def task_attributes=(task_attributes)
    task_attributes.each do |attributes|
      if attributes[:id].blank?
        tasks.build(attributes)
      else
        task = tasks.detect { |t| t.id == attributes[:id].to_i }
        task.attributes = attributes
      end
    end
  end

  def tag_attributes=(tag_attributes)
    tag_attributes.each do |attributes|
      if attributes[:id].blank?
        tag.build(attributes)
      else
        tag = tags.detect { |t| t.id == attributes[:id].to_i }
        tag.attributes = attributes
      end
    end
  end


20. nicolash Oct 16, 2007 at 03:51

"maybe I will make it [display error messages] a futur episode depending on the demand"
... there is demand ;-)
beside "the demand" there is an other reason to go into this topic: "additional options for presenting model errors -(Changeset #7870)"


21. magistrator Oct 16, 2007 at 06:03

I've got problems with validation of tasks. The has_many relation silently puts up additional validators, which are invoked at the time you try to save the projects model.

The problem is that these hidden validators affect the errors of the project by adding messages like "tasks is invalid" for each invalid task. (In fact, in my case the model names are different, so I'm giving the approximate messages.)

Now, I am seeing two issues with that. First, is there any way to localize the messages produced by has_many? Second, is it anyhow possible to disable adding these messages at all (I want only one message for all failures and I want it customized).

Anyway, thanks for the great job, Ryan!


22. Michael Voigt Oct 16, 2007 at 12:44

Hi Ryan,

very nice screencast. Can you send me the sourcecode of the rails project, i am so sluggardly ;-)

Michael


23. Etandrib Oct 16, 2007 at 13:51

@ Joe

I have the same question. It seems there is a lot of refactor that needs to be done to accommodate multiple sub models.
I posted this question to "refactor my code" to see what the community can come up with. Here is the URL. http://refactormycode.com/codes/87-combine-contact-attributes-in-model


24. Brian Oct 16, 2007 at 14:22

Refactored! Check it out


25. Matthew Lang Oct 17, 2007 at 02:35

Hi Ryan,

Great screencast! Instead of text boxes, should it just be as easy to use multiple select lists?

Thanks,

Matthew


26. piter Oct 17, 2007 at 02:50

There is a bug in railscasts code. When you point your browser to non existent episode (for example http://www.railscasts.com/episodes/100), than your application will crash. I think some <tt>begin/rescue/end</tt> could help ;]


27. Niels Slot Oct 17, 2007 at 03:00

Ryan: You didn't post the code for the javascript mark_for_destroy function.

Here it is for everyone who needs it:

function mark_for_destroy(element) {
    $(element).next('.should_destroy').value = 1
    $(element).up('.task').hide();
}


28. Joe Oct 17, 2007 at 05:03

I have a problem that I can't figure out for the life of me.

I understand the part about
Task: <%= f.text_field :name, :index => nil %>

I must be formatting something wrong or not writing this part write, but how do you set a select list to :index => nil?

Here is my code:
<%= f.select(:phone_type, [ ['',''], ['Home', 'Home'],
['Work', 'Work'], ['Cell', 'Cell'], ['Fax', 'Fax'],
['Other', 'Other'] ]) %>

I have tried :index => nil, tried :selected => nil after the array, but am receiving 'Conflicting types for parameter containers. Expected an instance of Array, but found an instance of Hash.' in my log file. Any help please?


29. Matthew Lang Oct 17, 2007 at 06:36

@Joe,

Any luck there Joe with using select lists? I'm also trying this out but I'm getting the same message as yourself.

Any breakthroughs then mail me.

matthew.langm at gmail.com


30. Matthew Lang Oct 17, 2007 at 07:09

@Joe,

You might want to try add the :index option to the html_options like this

<%= f.collection_select(:phone_type, @phone_types, "id", "name", {}, :index => nil) %>

Hope this helps!


31. Gilles Oct 17, 2007 at 09:50

Great screencast ! But i have one issue, i have read on how to use this technique with select or collection_select, but what about a date_select, the above workaround doesn't work for it ? Any clue to get out of this, anyone plz ?


32. Darryl Oct 17, 2007 at 16:05

I stumbled on the problem with select tags, and using html_options instead of options.

Related to that, checkboxes and radio buttons don't have html_options, so you have to manually create the elements (using check_box_tag and radio_button_tag respectively).

Otherwise, because Rails tries to be helpful when dealing with unchecking checkboxes, you get weird behaviour when updating a project, along the lines of getting extra, but blank, tasks.


33. Ryan Bates Oct 17, 2007 at 16:56

Thanks for the comments everyone. I'll try to answer some...

@TheCompWiz, there's no need to call update_attributes here because the attributes have already been set earlier. A simple save is all that's needed.

@Ted, yeah, I should have made the save_tasks method private. Good catch.

@Brandon, the show_title? is another helper method which just checks an instance variable I set earlier. Here's the code:
http://pastie.caboo.se/108319

@magistrator, I wish there was a way to disable this automatic validation message but I'm not sure how. Instead what I have done is built a custom error message handler which I can instruct to ignore certain validation error messages.

@Michael, I'll try to put up the full project source code in a day or so.

@Gilles & Darryl, I wish I had an easy answer for the date select or checkbox fields not working, but unfortunately I don't. You'll have to make them manually.

On top of this, checkboxes are tricky because the browser only sends them if they are checked. This means they won't be organized into the proper task attribute hashes. You either need to keep track of them separately or use some javascript to set a hidden field when they are changed.


34. Joe Oct 17, 2007 at 21:59

I am having problems with validations on the task attributes on update. All the validations work wonderful on create. But for some reason, and I am assuming it is because the task_attributes=(attributes) method just assigns all the attributes to a task on update. How can one make sure the same validations pass for a task on update of a project when the task attributes are directly assigned?


35. Joe Oct 17, 2007 at 22:03

I also forgot about the after_update :save_tasks method where the save is set to false. Any ideas?


36. Joe Oct 18, 2007 at 02:09

Found the problem. validates_associated :tasks in the project.rb file. It is missing from the source listed above, but I was able to catch the error watching the video again. Thanks.


37. Rich Thornett Oct 18, 2007 at 07:09

Great stuff, Ryan, thank you!

My question - what about transactions? In this simple example, it's probably not a big deal if there's an error saving tasks after the project has updated, but in general, I want all of my models to be saved or none (if failure). The rails before/after methods are convenient, but I see them more as ways to fulfill pre- and post- conditions v. implementing core save operations. I'm in the midst of implementing a multi-model save now and have overridden save/save! to handle the logic of saving all the models in a single transaction. I'm not sure this is the best way, but it allows me to wrap all model updates in a single transaction.

We use acts_as_ferret, which is very slick and convenient but makes me nervous for similar reasons - indexing occurs after save, so if indexing fails it doesn't rollback the database. Thus the index can get out of sync with the model.

These are big issues in Java land, but I rarely hear them talked about in the Rubyverse. Was curious if anyone else shared these concerns. Perhaps they are simply not as important to the types of apps that Rails developers tend to build?

+1 on any future episodes dealing with associations, errors or validation for multi-model save operations. This seems to be a corner of Rails where good practices aren't well documented (if even established).


38. Jason Jason Oct 18, 2007 at 07:22

Quick question:

Why handle the destroying through javascript and hidden fields? Why not quickly trash it at the scene via ajax?


39. Ryan McGeary Oct 18, 2007 at 07:39

@Jason Jason, By handling the destroy through hidden fields, it gives the user the opportunity to Cancel all changes. Nothing persists until the form is submitted which makes for a nice user experience. Of course different apps warrant different behaviors.


40. Ryan Bates Oct 18, 2007 at 10:03

@Joe, sorry about that, source is updated to include that line now.

@Rich, I haven't tested this fully, but I believe if an exception is raised in a callback (save_tasks method) then it will properly rollback the save of the project and other tasks. Is it not working for you?


41. john Oct 18, 2007 at 11:59

Ryan,
Validation and error displays are tricky. Maybe a collection good practices can serve as a valuable episode.

Here're some questions:
1. validates_associated on tasks is nice: what if I want to create a blank project without any task? I don't want it preventing me from creating a project; and later I want to edit this blank project and add tasks.

2. Will "task.new_record?" in the partial fails in a blank project case (no task) if you try to edit it, since task will be a nil object?


42. Gilles Oct 18, 2007 at 12:37

For those who are dealing with date_select tag with this method of updating related models in one form, one easy workaround i have found is to use a text_field instead of the date_select to enter your date data … it's not a perfect one, but …
You can also do it by hand by usind select_date_tag and so on …


43. Gilles Oct 18, 2007 at 12:55

One last question for me about this episode : Is it possible to use this technique with HABTM relationships ?


44. Rich Thornett Oct 18, 2007 at 13:14

Thanks for the feedback, Ryan, I think you're right. It never occurred to me that save() and the accompanying life-cycle methods were wrapped in a transaction w/in the framework, but after your comment I did some experimenting and that appears to be the case. (Adding an after_save method that raises an exception and calling save() rolls back a model update that otherwise saves successfully.)

It's news to me (good news, I think, though I'm still digesting the implications) that ActiveRecord is running save() + life cycle methods in a transaction. Will have to dig deeper into the ActiveRecord code - thanks for the insight!


45. Skyblaze Oct 18, 2007 at 13:33

Anyway i still think that short particular arguments like this are better explained in mini-pdf. Will you release them in the future?


46. Ryan Bates Oct 18, 2007 at 15:50

@john, if the project has no tasks then it is treated as an empty array so neither of the issues you mentioned should be a problem. But I haven't tested it extensively, does it not work?

@Gilles, I'm pretty sure you can adapt this to work with HABTM. One thing you'll have to do is decide how the removal process will work. Does it just remove the join or the end model? Everything else is very similar.

@Skyblaze, no PDF, but I'll be writing a few tutorials on railsforum.com in the near future showing this technique with possible additions.


47. Rich Thornett Oct 18, 2007 at 17:36

Just to close out the transaction discussion, this from the Rails doc:

Save and destroy are automatically wrapped in a transaction.

Both Base#save and Base#destroy come wrapped in a transaction that ensures that whatever you do in validations or callbacks will happen under the protected cover of a transaction. So you can use validations to check for values that the transaction depend on or you can raise exceptions in the callbacks to rollback.

http://caboo.se/doc/classes/ActiveRecord/Transactions/ClassMethods.html


48. Igor Oct 19, 2007 at 14:39

I think that we could simplify this example a lot when not worrying weather task is new or updated.
On each update we could first delete all existing tasks and then add all submitted:

def task_attributes=(task_attributes)
  tasks.clear
  task_attributes.each do |attributes|
    tasks.build(attributes)
  end
end

This way we could remove all marking of new and deleted records and save_tasks method.
We should probably had to mark tasks association as :dependent => :delete_all and add transaction in controller so we don't lose tasks if some validation fails.


49. chrisff Oct 19, 2007 at 23:22

@Gilles, for the date_select, use

:index=>''

rather than

:index=>nil


50. RainChen Oct 20, 2007 at 00:37

[quote]
after_update :save_tasks

  def save_tasks
    tasks.each do |t|
      if t.should_destroy?
        t.destroy
      else
        t.save(false)
      end
    end
  end
[/quote]

when @project.save, it automatically save the @project.tasks. Then Rails calling the callback save_tasks ?
Would the same task do update twice ?


51. chrisff Oct 20, 2007 at 01:19

For moving error messages from failed task validations up to the project error messages, I added this "after_validations" method to Project:

http://pastie.caboo.se/109132


52. Brandon Oct 20, 2007 at 03:39

More on complex forms and validation is very welcome. I'm trying to do the reverse of your relationship here. I'm trying to create a User that belongs_to a group. I've discovered how to do that with "build_group", but I need validate the created group before saving the user, if the user is creating a new group. Otherwise, I just want to validates_presence_of the group selected in a drop down. The whole validation system, with callbacks and conditionals, is pretty hard to get a good overview handle on.

I love your work. Thank you!


53. tonghoho Oct 22, 2007 at 03:42

what about adding task has many sub_tasks
In field_for what name I have to use
"project[task_attributes][][sub_task_attributes][]" is not work.


54. Carl Youngblood Oct 22, 2007 at 10:16

+1 for another screencast discussing error messages and validations in greater detail.


55. Ryan Bates Oct 23, 2007 at 14:07

@Igor, interesting idea. Destroying the tasks each time would probably work, however I hesitate to do this. There may be extra state (such as created_at) that we need to stay persistent. There might be other associations with the task that would be lost as well.

@RainChen, from my experience calling @project.save will only save the tasks if the project itself is new as well. This is why I only handle the callback on after_update. Although I haven't tested this extensively.

@tonghoho, I don't think having two "[]" in a form field name will work properly. Instead you'll have to keep track of the sub tasks separately. Sorry I can't go into detail.


56. AJ Oct 24, 2007 at 06:26

Thank you for the cast. Instead of complicating things with the destroy logic and doing all nasty stuff, how about a call to link_to_remote to destroy the element?


57. HappyCoder Oct 24, 2007 at 09:47

I have a little bit different situation. In this screencast we have project-task but I have task-project situation (contact-group in my case)

So, the form had been saving even though there were validation errors in Group. valid? saved the day

if @contact.group.valid? && @contact.save
            flash[:notice] = 'New contact has been created'
            redirect_to
         end

In the view:
<% if @contact && @contact.errors %>
<%= error_messages_for 'contact' %>
<% if @contact.group %>
<% @group = @contact.group %>
<%= error_messages_for 'group' %>
<% end %>
<% end %>


58. James Herdman Oct 24, 2007 at 10:00

This might be Edge Rails only, but instead of setting an instance variable for <tt>error_messages_for</tt> you can do this:

<code>
%lt!-- in _task.rhtml --&gt;
&lt;%= error_messages_for(:task, :object =&gt; task) %&gt;
</code>

I've noticed that this method really dislikes date-time select boxes.


59. James Herdman Oct 24, 2007 at 12:22

For future Googlers, I didn't find the real solution, but what I ended up doing was leveraging the Chronic gem to parse natural language date-time expressions.


60. Joan Oct 25, 2007 at 10:52

I've using the code from this episode, weirdly enough, no matter I try to remove a task or edit task value, the code "@project.update_attributes(params[:project]) " in project controller has always turned out to be false, therefore redirect me to the edit page, where the task values ain't changed. I've spent a day to figure out what's wrong, still got no clue.


61. Henry Oct 27, 2007 at 02:42

Let me just say Ryan: thou are the man. My need for this and your creation of these podcasts came hand in hand. Props. Also, thanks a lot @Ilya for the error fixes when using the latest edge.

@Joan: try 'puts params[:project]' before that line of code in your controller and then check your development.log to see what it's spitting out. If it's not normal, then my guess if that you have a form builder problem in one of your views. Good Luck!


62. Joan Oct 28, 2007 at 11:45

Thank you, Henry. I finally was able to found out the problem, it's actually caused by the failure of validation in task model.


63. steve Oct 29, 2007 at 10:12

nice cast, there is one bug though.

if you use the after_update instead of the after_save callback and you are creating a project for the first time (aka, not updating) and you create a task then delete it and then hit save the deleted task will still be created. this happens because save_tasks callback is not called on the create of the project model so you don't do the check for should_destroy?. one fix is to just use after_save instead of after_update, although i guess this results in a double save on the initial creation of a project.

another fix is to add a before_create alongside the after_update:

  before_create :bc

  def bc
    fields_to_destroy = []
    fields.each do |field|
      fields_to_destroy << field if field.marked_for_destroy?
    end

    fields_to_destroy.each {|field| fields.delete(field) }
  end

i think this is optimal because it won't even hit the db for new tasks on a new project that are marked for delete. but changing the callback to before_save is much simpler and less prone to bugs


64. steve Oct 29, 2007 at 10:19

sorry for any confusion: replace 'field' with 'task'

here is a better example:
  before_create :bc

  def bc
    tasks.dup.each do |task|
      tasks.delete(field) if task.marked_for_destroy?
    end
  end


65. Greg Oct 30, 2007 at 16:58

Steve, I believe that issue is actually handled by the logic in the view code:

<% if task.new_record? %>
  <%= link_to_function "remove", "$(this).up('.task').remove()" %>
<% else %>

When you're creating a new project, you just delete the entire field for the task rather than marking it for deletion.


66. Tim Oct 30, 2007 at 23:25

I have items that belong to a studyform. I am trying to save both an item name and and item type into each studyform. My _item partial has:
<% fields_for "studyform[item_attributes][]", item do |f| %>
 <p>Item: <%= f.text_field :name, :index => nil %>
Type: <%= f.text_field :type, :index => nil %>
<% if item.new_record? %>etc

The name saves into the items database but the type is always null. Any help would be appreciated.


67. Paul Damer Nov 01, 2007 at 11:26

Has anyone made this work with check boxes?

The helper inserts a hidden field after checkboxes to have it send 0 if the box isn't checked.

Unfortunately it means that when it is checked both the 1 and the 0 get sent and it thinks there are 2 sets of attributes to save. It can also mean the values get applied to the wrong rows of data.


68. Duncan Beevers Nov 02, 2007 at 02:14

You shouldn't use .detect to pull up the appropriate task. To keep the task associated with the Project, use the association finder method tasks.find_by_id(params[:id])

This way you avoid instantiating a lot of unnecessary task models. This is essential when a model may have thousands of sub-models.


69. Joe Nov 04, 2007 at 09:59

Does this still work with the latest Rails trunk? I get this error: Can't mass-assign these protected attributes: id


70. Nathan Youngman Nov 05, 2007 at 14:38

@Ryan: I may have an easier method of removing tasks, but I'll need to test it when combined with the concepts in this set of screen casts.

I'm using line items of an invoice, but I'll use your terminology. So I have a collection of @tasks from the database, and a new empty array: new_tasks. I loop through the posted params, searching for an existing entry in @tasks, which I then update. If not found, I build a new task instead. Either way I append that task to the new_tasks array.

When I assign the @project.tasks = new_tasks and save @project, Rails handles the removals for me, simply by the fact that they don't exist in the collection... smart! They don't exist in the collection because they weren't posted and added to new_tasks. So I have no need to mark for destroy or even call destroy.

I have a bunch of code in the controller, and I'm not using the virtual attributes you mentioned... so I'm really looking forward to combining these to methods and seeing if it all works!


71. Skyblaze Nov 06, 2007 at 05:37

For me it works perfectly but now i have to add several "third" models that are related to my second model. How can i do that. Anyway an issue with this code is the fact that we will have input fields with same ids...


72. Sarah G Nov 07, 2007 at 15:43

Ryan -- Thanks for the screencasts. They are extremely helpful.

@Paul, I made it work with checkboxes. I used check_box_tag. I use the checkbox value in the model (in def choice_attributes) to set the value of the field I want set, rather than setting it directly with the checkbox, like so:

attributes[:correct] = correct_choice_id.include?(attributes[:counter_id]) ? 1 : 0

It's a workaround but it works (and work s for radios, too). Then on the form end, I manually check the box like so:

<% is_checked = choice.correct == 1 ? true : false; %> and render the check box this way:
<%= check_box_tag('question[correct_choice_id][]',choice.id, is_checked,{}) %>

(that's on edit -- on new, when I don't have question.correct.choice I have named the checkbox with a counter from within the partial, and run essentially the same thing. I'm not sure if this will be clear or not but it does work for me so hopefully it helps.
 


73. andy Nov 08, 2007 at 15:55

I've found a couple of issues here that maybe only affect me, but I'll post in case someone else experiences this.

First, the "should_destroy" attribute was showing up in the first item regardless of which item was removed. The issue was that the 'should_destroy' hidden fields had no value and so did not end up in the correct array. I switched to hidden_field_tag with a default 0 value and it's fixed.

Second, when passing strings with non-alpha chars the URL encoding for javascript was running once for each layer of the array. So '-n/a-' was becoming "-n%25252Fa-", I did a 3.times{ attr[:string] = URI.unescape(attr[:string])} within the task_attributes= method. Hackish, but it works.


74. Jose Boza Nov 15, 2007 at 07:58

I'm having the same problem as Paul, is there an easy solution anywhere?


75. Chad Nov 18, 2007 at 13:46

Thank you so much for putting this together. It was incredibly helpful! Please add my vote to your doing a part 4 on validation.


76. Michael Nov 18, 2007 at 22:17

Thanks a lot for this Ryan.

+1 for a separate validations screencast!


77. Kevin Baird Nov 20, 2007 at 06:31

Ryan -

Thanks for the great screencasts.

I was a bit confused by the workaround in the should_destroy? method, until I remembered that MySQL lacks a genuine BOOLEAN datatype.

With PostgreSQL, you can define a boolean field in your model (without a trailing ?), and a predicate method (with the trailing ?) is automatically there in your model. Handy.


78. Jason Whitfield Nov 21, 2007 at 15:52

Strange problem here..

I can edit and delete tasks with no problem, but when I try to create a new task (or several at once) the very first of the previously saved tasks gets wiped out. I copied Ryan's code line for line and am running this in Rails 2.0.

Anyone else face this issue?


79. Jason Whitfield Nov 21, 2007 at 15:53

ALmost forgot to mention, I only have this problem when I have the marked_for_destroy hidden field and js link in that partial. I remove them and can add tasks with no problem. Odd..


80. Abraham Nov 22, 2007 at 20:42

How do I reference the project[task_attributes][] fields for auto_complete_field functions... Does anyone have an example using auto_complete_field and fields_for?


81. Pascal Ehlert Nov 25, 2007 at 04:08

Awesome screencast series, thank you so much!

Only one question.. I'm trying to do this with a habtm relationship (possible topic for another screenie? ;)) and unfortunately it doesn't save the linking table when updating attributes. :(

Does anyone have an idea how to tell rails to manually save it?


82. Ryan Nov 27, 2007 at 07:19

Ryan, this was a terrific tutorial. I'd definitely be interested in seeing more on validations, etc.

I'm also curious as to how you might take a progressive enhancement approach. The current method seems to require Javascript. I tried using a TaskController to provide web services, but if I make it polymorphic (Project and ToDoList), all hell breaks loose... or at least, it looks really ugly.

I like the fact that nothing is lost until the entire project is saved, so I'd prefer to keep it that way, but adding and removing tasks becomes difficult without Javascript.


83. Gernot Kogler Nov 27, 2007 at 07:42

Thanks so much for another great screencast series. However, I found it a little bit complicated and cluttered with all these hidden fields, javascript and such. So I took a slightly different approach. First, I modifed the task partial so that it passes always a model id:

  <%= fields_for "project[tasks][#{task.new_record? ? 0 : task.id}]", task do |task_form| %>

Now I can determine which tasks to create, update and delete in the method task_attributes= like this:

  def task_attributes=(task_attributes)
    task_attributes.each do |id, attributes|
      if id.to_i == 0
        tasks.build(attributes)
      else
        task = tasks.detect{|p| p.id == id.to_i}
        tasks.attributes = attributes
      end
    end
    tasks.reject{|t| t.new_record? || task_attributes.has_key?(t.id.to_s)}.each {|t| t.mark_for_destroy}
  end

Works like a charm and keeps my views clean.


84. BurmajaM Nov 29, 2007 at 07:04

Hi Ryan.

I find this series very useful. I've implemented some stuff using the technique you've described and it works great, but I'm interested how could I make file upload using this very technique (gmail like upload). Can you help me please?


85. Scotty Nov 29, 2007 at 16:13

I found this railscast series to be particularly useful! After stumbling across Jamis Buck's post about moving associated creations to the model your screen cast really cleared up the concept for me.

Validations had me puzzled, but I finally figured it out. I've posted what worked for me in my situation over on my <a href='http://psifire.livejournal.com/119168.html'>Live Journal</a> page... since I don't have a real blog.


86. gorn Nov 30, 2007 at 11:42

@ Gernot Kogler: I think that your idea can not work - I have been experimenting quite a lot with it and I think that all duplicate named imputs will get lost (for example if you have twho or more new tasks). The trick of putting the new items to the array only works if there is bare [] in the name of INPUT field. SO I suggest changing your conditional so it gives empty string for new tasks.


87. Rene Dec 04, 2007 at 17:30

Ryan,

great tutorial.

I unfortunately ran into a problem while using the code. My Task model has more than one field (name, priority and estimated cost) and they all are required (i.e. there is a validates_presence_of rule for each of them).

The problem happens when the user updates an existing task. More precisely when the user deletes the content of one of the text field and then clicks the remove link for that task.

As you can guess, since the remove links just hides the task, an incomplete task is sent to the controller and since validation happens before the after_callback is called, validation fails, the task is not destroyed and the controller redirects to the update page (with an error saying that the task is invalid)

Fortunately, the solution is not too difficult, all we need is to add a method in the Task class that returns false if the task should be destroyed and the pass that method as an "if" parameter to all validation rules for the task.

def do_validation
  !should_destroy?
end

validates_presence_of :name, :priority, :estimated_cost, :if => :do_validation


88. Rene Dec 04, 2007 at 17:39

Ryan,

would it be possible to have an RSS feed for tracking the comments for each episode?

In some cases, the comments/questions and answers are quite interesting too.

Thanks again for the great work


89. pimpmaster Dec 04, 2007 at 18:50

+1 for Pascal's suggestion

habtm would rock for this


90. John Barton Dec 07, 2007 at 09:00

I have a patch that avoids the empty {} if you use the index => nil with a select tag at http://dev.rubyonrails.org/ticket/10416


91. thomas Dec 20, 2007 at 01:19

I also see URL-escaped characters on child nodes. Problem is I can't unescape them. They stay escaped for some reason. :/

rails 2.0.1


92. Scott Windsor Dec 20, 2007 at 15:03

I came up with a solution for checkboxes based on Ryan's suggestion of using javascript.

application helper
http://pastie.caboo.se/131145

application javascript
http://pastie.caboo.se/131144


93. AJ Dec 22, 2007 at 01:40

Your screencast helped a lot solving the problem which I previously solved in a rather complicated manner.
I have a question to ask.
What if my project has 3 different type of tasks ->
A task can be either a
 - File
   -File Upload(Model : File)
   -HTML ADD (Model : File)
 -Link (Model : Link)
Now, I want three links at the bottom of the project to add file/html/link where html and file use the same model.
Now, in case of add html i want to redirect to a WYSYWIG editor(displaying it in the same page could get dirty) from where user can write the html and save it. I think a popup would be helpful in this case. And, then saving would redirect to the same form, from where you can save the project. Any heads-up on how this can be implemented ?
+1 for screencast on this.


94. Tom Jan 09, 2008 at 05:51

Hi guys, this is a great tutorial! i have tried to get it to work with photos aswell but I can't seem to get it right. I am using a polymorphic photo table which may well be the problem. Does anyone have any suggestions on how i could get it to work?


95. Michael Jan 11, 2008 at 20:39

@Gernot Kogler:

What happens if you create more than one new task? i don't see how your code will work, the params parser is designed to ignore all same named parameters after the first rather than to collect them. That is how the checkbox hack works.


96. Clovis Jan 14, 2008 at 00:01

Im getting this error:
Symbol as array index

on def task_attributes

why this ?


97. Dan Jan 16, 2008 at 09:14

What kind of javascript is this?
"up('.task')"

I have never seen an "up" (or "next") function that moves through the HTML DOM and return an element of a certain class. It sounds useful. Can someone post an online reference for this function?


98. mrb_bk Jan 16, 2008 at 09:58

Does anyone have any insight into how to adapt this example for has_many :through associations? The build aspect of this example breaks it -- I've poste dabout it here -- http://railsforum.com/viewtopic.php?id=14580

any help would be awesome!


99. John Jan 17, 2008 at 05:32

Any thought about how to handle security related issue such as mass assignment? Is there a clean way to combine your approach with using attr_accessible?


100. Juan Jan 20, 2008 at 10:36

I follow the tutorial and everything worked great. My only problem is that with what I am I add a new "task"(picture in my case) it overwrites the previous picture. If I add more than one picture, it overwrites previous pictures and adds the new ones. Any ideas on how to fix this? it works great if I don't wrap the file_field in an if statement, but I really don't want it to show up before a picture that has already been submitted....just want the ability to remove. Thanks

<div class="picture">
<% fields_for "housing[picture_attributes][]", picture do |pic_form| %>
<p>
<% if picture.new_record?%>
Upload A picture:
<%= pic_form.file_field :uploaded_data , :index => nil %>
<%= link_to_function "remove", "$(this).up('.picture').remove()" %>
<%end%>
<% unless picture.new_record? %>
<img src="<%=picture.public_filename(:thumb)%>" />
<%= link_to_function "remove", "mark_pic_for_destroy(this)" %>
<%= pic_form.hidden_field :id , :index => nil %>
<%= pic_form.hidden_field :should_destroy , :index => nil, :class => 'should_destroy' %>
<%end%>
</p>
<% end %>


101. AdamD Jan 23, 2008 at 14:39

I'm really running into road block after road block trying to figure out how to get associated validation working properly. At this point all I can get is an error message that says 'Model is invalid' rather than the preferred child-model's attribute error message.

We really need a screen cast on how to get this kind of validation working.


102. bob Jan 28, 2008 at 03:44

FzrmBT great site thx http://peace.com


103. luu Jan 29, 2008 at 09:32

here's how I handle error msgs. If someone can suggest improvements, I'd appreciate it.

In this example: post = parent model, asset = child model

I check @post.errors. if it's not empty, I display some generic msg, like: you have one or more errors, pls fix then submit again.

@post.errors is an activerecord object, an array of attr=>msg like this

['title'=>"title can't be blank",'body'=>"body can't be blank',...]

Next, I display the error msg for each field right above it, like this

<label for="post_title">Title</label><br />
<%= error_msg_for @post, 'title' %>
<%= f.text_field :title, :size => 60 %>
<br />
<br />

<label for="post_body">Body</label><br />
<%= error_msg_for @post, 'body' %>
<%= f.text_area :body, :cols => 60, :rows => 15 %>

in my child model, asset, do similar thing

<% fields_for "post[asset_attributes][]", asset do |f| %>
<%= error_msg_for asset, 'name' %>
<%= f.text_field :name, :size => 15 %>
...
<% end %>

that is, display the error msg for each field in asset (child model). If you have a grandchild model, you can do the same probably.

finally, here's my method for displaying the error msg

def error_msg_for( record, field=nil)
return '' if record.errors.empty?
 if field
 a = []
 record.errors.each do |attr,msg|
  a << msg if attr == field
 end
 %(<div class="field_error">#{field} #{a.join('<br>')}</div>) unless a.empty?
else # if field == nil
 %(<div class="errorExplanation">There was a problem with the data you submitted. Please fix the error(s) below then re-submit.</div>)
end
end

so I can put

<%= error_msg_for(@post) %>

without a field parameter at the top of the form, it'll display the generic "you have an error" msg when there's any error in the parent or child.

you may notice I check for possible multiple error msgs for the same field. I don't know if this ever happens or not.

also, the field name needs to match exactly with the attribute. if you're not getting the error msg back as expected, dump out errors array to see what's in it.


104. luu Jan 30, 2008 at 00:36

Juan, I found for some reason it only works if you have a file_field for ALL the pics. So for the existing pics, I'd simply hide them

<%= pic_form.file_field :uploaded_data , :index => nil, style=>'display:none' %>

not pretty, but it works


105. Jamie Hill Jan 30, 2008 at 09:10

Has anyone come up with a way to get rid of the HTML validation errors rails creates with double underscores and non-unique id attributes when setting :index => nil ?


106. Dmitri Koulikoff Feb 07, 2008 at 15:20

partial: http://pastie.caboo.se/149027
In the log the array is messed.
"set_mappings"=>[{"file_name_pattern"=>"*pricelistsample*"}, {"format"=>"1", "file_name_pattern"=>"*availability*"}, {"format"=>"1", "id"=>"953125641"}, {"id"=>"996332877"}]

It looks like rails collect the array in a wrong way. What could it be?


107. Dmitri Koulikoff Feb 08, 2008 at 06:47

It is known problem which is not resolved in rails 2.0 and has the Ticket #10101
http://dev.rubyonrails.org/ticket/10101

Thanks for attention.


108. Bryan Richardson Feb 10, 2008 at 10:16

Hi Ryan,

Hopefully you're still reading comments for this Railscast. This series has helped me out with my current application, thanks!

One quick question: if I use a check_box in my partial when using fields_for, it always tries to submit two entries for every one displayed on the page. I'm assuming this is because the check_box helper adds a hidden field for when the box is not checked. Is there any way around this?

Thanks! -- Bryan


109. CarbonJoeTunis Feb 14, 2008 at 08:14

for those having the problem of tasks not being saved or deleted on Update. i had the same problem and i just tracked it down (i'm usin Rails 2.0 as well and didn't have to change anything from Ryan's example -- nice work by the way!). anyway, my issue was i inadvertently had a single = sign in the task_attributes method. i had this:

photo = photos.detect { |p| p.id = attributes[:id].to_i }

so an assignment was happening, rather than the correct:

photo = photos.detect { |p| p.id == attributes[:id].to_i }

that fixed my issue.


110. mercury Feb 15, 2008 at 06:38

I'm curious: what is the best way to limit the number of fields the user can add during both create and update? For instance in my application I'm using this to add picture upload fields, but a user has a max of 9 pictures to they're allowed to upload.

As a side note: Loved these 3 Eps! Thank you so much for taking the time to demo RoR to the masses. Many blessings to you as you work toward your 100th episode!


111. Eddie Feb 20, 2008 at 07:02

I'd love to see how validation errors are highlighted in fields that are dynamically added to the form - if you added some task fields and these failedvalidation, when the default view for the form is reloaded (to highlight the errors) the additional fields that were added in the previous step (that failed validation) wouldn't be there...


112. Harlan Crystal Feb 21, 2008 at 09:03

Is there any strategy for using this technique with radio buttons? The problem being that instead of having separate "button groups" for each task, you end up with one huge button group for all the radio buttons across tasks, because they all use the same "name" for radio widgets in each task.


113. Shawn Banks Mar 06, 2008 at 04:49

 am wondering if someone may be able to point me in the right
direction.

I have a survey that I am trying to do that has in it many one too
many relations. Thanks to railscasts complex forms series, I was able to
get them all working. I have come to a road block and hoping someone
can help. The survey capture a companies profile, where things get sticky
is
I now need to create a relationship that will allow a user to enter in
categories that the company might sell products in. Here is the
predefined list.

* Wood based panels
* Complete pre-fabricated buildings and houses
* Wood kitchen cabinets
* Wood windows and doors
* Wood flooring
* Millwork
* Mouldings
* Engineered wood products
* Other wood based building products
* PVC windows
* Steel doors
* Insulation
* Stone-processed products
* Basement envelope systems
* Other non-wood building products

This list is set in stone and no others can be added by the user. So a
company can have many categories.

What I started was created a first created two tables(categories and categories_companies) in addition to the original company profile(companies).

I created the models and put in them the following lines:
model categorization

   1. belongs_to :company
   2. belongs_to :category

model company

   1. has_many :categorizations
   2. has_many :categories, :through => :categorizations

model category

   1. has_many :categorizations
   2. has_many :companies, :through => :categorizations

I am now stumped. I am really new to this and object orientation programming.

I think that I should in the company controller have a statement to grab all the categories from the DB and display them in the new company view form to allow a company to pick which categories they sell in.

Also I need to add a relationship to allow users to add products to each category with details on each
product.

I am not sure on how to approach this as well I am very new to Rails.
I come from a coldfusion background. If it would help, I could send you
any existing code I have.

I hope someone can help, Thanks in advance!


114. Kevin Triplett Mar 12, 2008 at 17:03

@chrisff

Your association validation error message pastie rocks!

Here's an enhancement to it that strips out the useless "is invalid" error messages so you're only left with the useful association validation messages:

http://pastie.caboo.se/165073


115. Shawn Mar 14, 2008 at 08:35

I am having the same issue as Harlan Crystal I am using Radio buttons but they are being all grouped into one big group


116. Skalbert Mar 15, 2008 at 08:24

Thanks a lot!


117. Jesus Bernardo Ruiz Mar 25, 2008 at 12:22

Hi i have the following problem. I followed the screen cast to create
complex forms from railscats so i create a partial view for my
equipments list, so the client can add several different equipments, I
also made another view in case looking for the equipment inside the
select box. When i choose a equipment from the view (find_equipment) i
created there is no problem when there is only one equipment inside
the client form, but when I another equipment and I try to select it
from the select view (find_equipment) it does not update the select
box it should, it update the first select box.

I have a similar problem with observe_field, it should update the
properties for a certain product, but it only update the first select
list the other lists are not updated.

Here is the source code:

_equipment_services.html.erb
<div class="equipment">
  <% fields_for "voip[equipments_services_attributes]
[]",equipment_services do |e|%>
  <p class="titulo">Informaci&oacute;n Equipos</p>
  <p>
    Activo Fijo
    <
%=e.select("equipment_id",Equipment.find(:all,:conditions=>"estado=6",:orde­r=>"numero_activo_fijo").collect{|
p|[p.numero_activo
_fijo, p.id]},:index=>nil)%>

        <% if equipment_services.new_record? %>
          <%="Estado Activo"%> <%=e.hidden_field("estado",:value=>6)
%>
          <%= link_to_function "Eliminar",
"this.up('.equipment').remove()" %>
        <%else%>
          <=% link_to_function "Eliminar",
"mark_for_destroy(this,'.contact')" %>
          <%= e.hidden_field(:id,:index=>nil) %>
          <%= "Estado" %>
          <%=
e.select("estado",Code.find(:first, :conditions=>["id=5"]).elements.collect{|
p| [p.descripcion,p.id]},:index=>nil) %>
          <%=
e.hidden_field(:should_destroy,:index=>nil, :class=>"should_destroy")
%>
        <%end%>
        <%=link_to "Crear Nuevo
Equipo",add_equipment_equipment_path,:popup =>
["Agregar_Nuevo_Equipo","height=600, width=700"]%>
        <%= link_to "Buscar Equipos",
{:action=>"find_equipment", :controller=>"show_select"}, :popup =>
["Show_Equipment","height=60
0, width=700"]%>
  </p>
<%end %>
</div>

javascript function
function set_value(id,value)
{
  opener.document.getElementById(id).value=value;
  window.close();

}

find_equipment.html.erb

<table>

  <tr>
    <% form_tag find_equipment_show_select_path, :method=>"get" do%>
      <td>Buscar Usuario:</td>
      <td><%=select_tag "busqueda","<option value='1'>Modelo</
option><option value='2' selected='true'>N&uacute;mero Activo Fijo</op
tion><option value='3'>N&uacute;mero Serie</option><option
value='4'>Direcci&oacute;n MAC</option>"%>
      <td><%=text_field_tag :search, params[:search]%></td>
      <td colspan=3><%=submit_tag "Buscar", :name=>nil%></td>
     <%end%>
  </tr>
<tr>
  <th>Modelo</th>
  <th>Marca</th>
  <th>Activo Fijo</th>
  <th>N&uacute;mero de serie</th>
  <th> Tipo Equipo </th>
</tr>
<%
    switch=false
    for equipment in @equips
%>
  <tr class="<%= if switch
                   "TRPAR"
                else
                    "TRIMPAR"
                end %>
  <% switch=!switch %>">
    <td><%=equipment.modelo%></td>
    <td><%=Code.getDescripcion(equipment.marca)%></td>
    <td><
%=link_to_function("#{equipment.numero_activo_fijo}","set_value('voip_equip­ments_services_attributes__equipment_id','#{equi
pment.id}')")%></td>
    <td><%=equipment.numero_serie%></td>
    <td><%=Code.getDescripcion(equipment.code_id)%></td>
  </tr>
    <% end %>
</table>

Can anyone help me please? i'm kind of lost here


118. David Blankenship Mar 26, 2008 at 06:27

Great series! I did have one question about the Complex Forms parts, and that is how to degrade if javascript is disabled. Is there a way to allow link_to_function to "submit" to another url so you can recreate the objects and then add a new task and render the original form or am I thinking about it all wrong?


119. Ryan Bates Mar 29, 2008 at 15:39

Thank you everyone for your excellent comments. Sorry I haven't gotten around to respond to them all.

I just updated the code in the notes to use a better approach. This is taken from the recipe I wrote for Advanced Rails Recipes. Hopefully it will solve the majority of these problems.


120. Ryan Bates Mar 29, 2008 at 15:57

@David Blankenship, good question. I would probably hide the link when javascript is disabled, then you could handle adding tasks through a non multi-model form by going through a tasks controller "new" action for each task after the project has been created.


121. Jeff Dean Apr 05, 2008 at 14:11

I've blogged about how to get around the issue of the radio buttons having the same ids at "http://zilkey.com/2008/4/5/complex-forms-with-correct-ids":http://zilkey.com/2008/4/5/complex-forms-with-correct-ids


122. Chris Apr 05, 2008 at 15:23

The code you've posted here isn't the same as that in the Advanced Rails Recipes book (e.g. the task partial is significantly different). I'm wondering which code is more up-to-date, particularly in the light of errata item 31586 posted at the book's site.

Many thanks for your very fine Railscasts.


123. Ryan Bates Apr 08, 2008 at 23:50

@Jeff, thanks for posting this! I included the link in the show notes.

@Chris, good eye. I modified the code slightly from the Advanced Rails Recipes book by moving some into the "fields_for_task" helper method. For simplicity, the recipe in the book does this inline in the view using a local variable, but recommends moving it into a helper method. Since that note wasn't included here I wanted to encourage the best practice of not setting variables in the view. The code itself still does the same thing, it's just moved around a bit.


124. Scott Vesely Apr 28, 2008 at 07:52

I have, probably, a dumb question:
For forms such as this, where we're adding multiple tasks at once to a new form, I understand (I believe) that they're adding new post variables like project[]task[] etc, and editing existing values like project[1]task[3] etc.

My question: wouldn't it be clearer, and perhaps more semantically correct, to ALWAYS assign a sequential integer as the array index (so that new record combinations might also look like project[4]task[2]) and simply rely on a rested id tag in order to identify which records have previously been saved (existing) and those that lack an id (new)?

I'm sure I'm missing something, but it just feels like that postulated format ties in better with the object model by utilizing the correct location for the id data, and relieves the problem of the post parser having to make educated guesses as to which data pairs should be grouped into an individual object.


125. Dan Apr 29, 2008 at 13:23

Is there anyway for fields_for to look at the scope of the enclosing form_for and change it's scope accordingly?

Depending on the form, sometimes I need the fields_for to be scoped as

"parent[object_attributes]"

other times I would like it to be

"parent's parent[parent][object_attributes]"

I have been passing it around to the partial using :locals, but it seems like there must be a better way.


126. Matt Apr 30, 2008 at 11:04

Ryan - thanks for the great screencasts - I hope you keep them coming!

WARNING: Solution doesn't work with Edge Rails (2.0.3) when Task has nested fields! (Unless I'm not building the names of the inputs correctly... but I'm fairly certain I am!)

I've found this (Rails2) solution has problems when Task itself has nested fields - at least in Edge Rails (2.0.3). The request parameter parser won't parse the request into the new_task_attributes array correctly.

Example: Let's say Task has_one :person, and Person comprises fields 'first_name' and 'last_name'. You'd probably end up with a form input similar to the following (of course you'd choose what the actual name of the attribute 'person_attributes' would be):

project[new_task_attributes][][name]=taskname1
project[new_task_attributes][][person_attributes][first_name]=person1-first
project[new_task_attributes][][person_attributes][last_name]=person1-last
project[new_task_attributes][][name]=taskname2
project[new_task_attributes][][person_attributes][first_name]=person2-first
project[new_task_attributes][][person_attributes][last_name]=person2-last

Edge Rails (2.0.3) creates an very wrong parameters hash for this:
 "project"=>
  { ...project-data...,
   "new_task_attributes"=> [
      {"person_attributes"=>{"first_name"=>"person1-first"}},
      {"person_attributes"=>{"last_name"=>"person1-last"}, "name" => "taskname1"},
      {"person_attributes"=>{"first_name"=>"person2-first"}},
      {"person_attributes"=>{"last_name"=>"person2-last"}, "name" => "taskname2"}
     ],
   }

Just a warning to folks out there who are trying this solution, but have nested fields as well.


127. Thalas May 01, 2008 at 22:13

While everything works, I am not able to update tasks using Firefox. I can in IE! I can create new records though! Any ideas what I need to check? Thanks.


128. Dylan May 19, 2008 at 16:45

In the revised example I found that I had to use the index option on my fields when I was specifying a to_param method for my equivalent Tasks model


129. Roy May 20, 2008 at 14:28

Genius code--thanks so much!

That said--is the after_update callback absolutely necessary?

It looks to me like it works if you call task.save from within Project.existing_task_attributes= (right after the line "task.attributes = attributes").

Is that right?


130. Jose May 23, 2008 at 15:17

Hello Ryan,

I have used this sublime recipe as the basis for a kicked-up-a-notch version, with a more complex database schema and it's working perfectly so far.
The point where I'm having problems is if, for instance you also have a Category model that has_many :tasks and you have to select which category the task :belongs_to before clicking the add_task_link.
What I have tried -unsuccessfully- to do is when you call page.inset_html, to pass in something like :object => Task.new(:category_id => what_should_i_put_here?), being (as you might have figured out) "what_should_i_put_here?" the main problem. I can get the category_id value from the form field, but I can not insert it within the page.insert_html method.
Thank you.


131. bijou May 29, 2008 at 02:29

the codes didn't work for me well (updated version or no) and i guess i can blame the complexity or disorderliness of my project code. with the help of a friend, i was able to find a way around my predicament with these complex forms. i use collection_select instead of text_fields too. anyway, while my friend was a genius being able to figure out how to do the updating (edit or adding a new record) of multiple forms on the spot, he wasn't able to figure out how to delete records (plus he had somewhere to go to).

but wow, i still can't believe how i managed to figure out a very simple way to do it. it's a bit of simple common sense, actually.

i used the javascripty "remove" in conjunction with the server side's "destroy".

<% if task.new_record? %>
<%= link_to_function "Remove field", "$(this).up('.task').remove()" %>
<% else %>
<%= link_to 'Cancel task', task, :confirm => 'Are you sure?', :method => :delete %>
<% end %>

hope that will help anyone...


132. Lucas Castro Jun 02, 2008 at 15:01

Ryan, i am getting an weird error when i combine the use of "fields_for" and "collection_select" at the partial code.

There's someone having the same issue, any tip?

http://railsforum.com/viewtopic.php?id=18598


133. Kevin Whinnery Jun 09, 2008 at 09:29

The excerpt from the book on this topic was insanely helpful. Thanks for all the good work on this and other screencasts and reference materials. I've learned more about Rails from you than any other single source, hands down.


134. victor thompson Jun 13, 2008 at 15:53

@Scott, and everyone fighting the checkbox problem with dup fields. I fixed your code. The else if in the javascript didnt run right and I found it more suitable to remove the name field of the hidden field rather than attempt to toggle its value--which did not seem to work.

My solution also allows you to still use check_box instead of check_box_tag. The only mod to the form is to add :onchange => "toggle_name(this);" to the checkbox form element. Then simply declare the toggle_name function in your appplication.js file as such:

http://pastie.org/214828


135. victor thompson Jun 13, 2008 at 16:34

The code I posted does not appear to work properly under IE due to its nextSibling implementation. I'll try to find a more robust solution on monday.


136. Pege Jun 18, 2008 at 08:30

This works great but I have a problem. My partial is an invoice item field with amount, price and total sum (and of course there can be many of these). I'd like the total sum to be calculated by Rails when amount or price is changed. Because the form fields have no unique id's, I cant' get this to work with remote_function.

It works if I calculate with JavaScript by traversing trough the DOM like so:

<% item_form.text_field :amount, :onkeyup =>
";$(this).up('.invoice_item').down('.sum').value=$(this).up('.invoice_item').down('.price').getValue()*$(this).getValue();" %>

But I can't use the $(this).up... thing in remote_function :update => ... and can't use RJS because there's no unique id for the fields.

Is there any solution for this?


137. Harry Jun 20, 2008 at 17:02

I'm using Rails 2.1 and the updated code above and getting "undefined method `new_record?' for "1":String" when I update a record.

The error comes from this line in the #existing_task_attributes method:
tasks.reject(&:new_record?).each do |task|


138. Harry Jun 21, 2008 at 09:07

All good. The error was mine.


139. Seth Jul 01, 2008 at 13:25

Great article and a lifesaver BUT.. when you start doing things like time_select the custom forms_for_x is no longer rendered properly (namely the hidden fields generated by this time_select).. Not sure if this would effect other rails tags.. if so it's not as versatile as once thought and would have a limited use.


140. John Jul 02, 2008 at 11:30

On rails 2.1 - updates I get:

can't convert String into Integer

app/models/poll.rb:47:in `[]'
app/models/poll.rb:47:in `existing_choice_attributes='
app/models/poll.rb:46:in `each'
app/models/poll.rb:46:in `existing_choice_attributes='
/Library/Ruby/Gems/1.8/gems/activerecord-2.1.0/lib/active_record/base.rb:2361:in `send'
/Library/Ruby/Gems/1.8/gems/activerecord-2.1.0/lib/active_record/base.rb:2361:in `attributes='
/Library/Ruby/Gems/1.8/gems/activerecord-2.1.0/lib/active_record/base.rb:2360:in `each'
/Library/Ruby/Gems/1.8/gems/activerecord-2.1.0/lib/active_record/base.rb:2360:in `attributes='
/Library/Ruby/Gems/1.8/gems/activerecord-2.1.0/lib/active_record/base.rb:2261:in `update_attributes'

If I update existing_choice_attributes's choice.id.to_s to choice.id it works, but will destroy my choices when updating the same form twice.


141. AJ Jul 04, 2008 at 06:04

BUG: In case number of attributes is more than one in the partial, browser does not groups it right and sends wrong data.
Example:
Data expected :
new_part_attributes[]["ordering"]=>"1",
new_part_attributes[]["inline"]
=> "false"
new_part_attributes[]["wfile"]=>#<ActionController::UploadedStringIO:0xb602ea20>

new_part_attributes[]["ordering"]=>"2"
new_part_attributes[]["inline"]=>"true"
new_part_attributes[]=><ActionController::UploadedStringIO:0xb602cd38>

Data Send By Browser:
"new_part_attributes"=>[{"ordering"=>"1", "inline"=>"false", "wfile"=>#<ActionController::UploadedStringIO:0xb602ea20>}, {"ordering"=>"2", "inline"=>"true"}, {"wfile"=>#<ActionController::UploadedStringIO:0xb602cd38>}]

Possible solution is send index also in the array, so that it groups it right.


142. AJ Jul 04, 2008 at 07:41

And also, what about doing the same thing for Gmail type file upload ?

In that case, your previous code, in which you used to set should_destroy? would work better.


143. Marjan Jul 11, 2008 at 14:26

I get the same "can't convert String into Integer" error as John (3 posts above)

Any help would be much appreciated.

Thanks


144. Marjan Jul 11, 2008 at 14:56

Umm, found the solution to the problem above.

Looks like I forgot to remove the ":index => nil" part from the task helper


145. Dominic Amann Jul 17, 2008 at 19:25

Ok, so I bought the book, and keyed in the example except I changed the names of tables to fit my project. It didn't work. I beat around the bushes for a couple of days, but couldn't find my bug.

Then I keyed it in using the exact line for line coding from the bug. Still same problem.

Then I found this page, and downloaded the code.

After adding a named route for projects (the code does not have it - I had to figure this part out), it still doesn't work. I get no errors, just neither my project nor my tasks ever get saved.

Has anyone else actually got any of this to work? I mean I would hate to be completely wasting my time - I could code it the hard way in far less time than I have already wasted here.


146. Dominic Amann Jul 17, 2008 at 19:41

I am pretty sure my problem is in that routing thing that is not provided in the code, railscast or the book. Perhaps the version he uses has different defaults for routing or something. Anyhow, I have wasted 5 good coding days on what looked like a good idea, but just won't work for me at all - and the logs give no clues that I can use.


147. Dominic Amann Jul 22, 2008 at 15:36

Ok, so I got it working by mashing together some code from above into the code in the book, and replacing all the instances of project_path with actions and ids as appropriate in my controllers and views.


148. mellifluidic Jul 22, 2008 at 16:16

@Dominic: I (after a bit of futsing with mistyped fields) have indeed gotten this to work for the simple model used in the railscast. What version of Rails are you running?

@All: While implementing this technique with my own application, I got through the first and second casts in this series without problem, but when I went through the steps in this one, I now get the following in my log:

/!\ FAILSAFE /!\ Tue Jul 22 15:49:37 -0700 2008
  Status: 500 Internal Server Error
  Conflicting types for parameter containers. Expected an instance of Hash but found an instance of Array. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value.

I'm gonna keep digging and see if I can figure out why something is getting sent an array that is supposed to be a hash (my guess is that it is the params hash), but I figured I would drop a line and see if anyone could quickly spot my problem here. I'm still pretty new to Rails, so any help is appreciated.

Thanks


149. mellifluidic Jul 22, 2008 at 17:53

@Dominic: I started making my last post before you made yours, so I didn't see that as I wrote. Sounds like you are good to go though.


150. glennpow Jul 23, 2008 at 10:57

So, has anyone effectively made this work with compound models?
EX. - Where a Project can have multiple Tasks, which can have multiple Milestones, which can have multiple Assignees,...

I seem to have everything working other than getting the parameter names to be correctly parented.

I've thought of several solutions such as passing into each partial a 'parent_attr' local which (if not nil) would be prepended to the field names. But I don't think this is the most elegant method.

any other suggestions would be greatly appreciated.


151. mellifluidic Jul 23, 2008 at 13:55

I fixed my issue - it was with my method of calling a collection_select in my form. I had :index => '' under my html options. I think I tried this at some point while I was trying to get thing to work in part II, but found another solution and failed to take out that bit (Incidentally, the solution which worked was calling the collection_select directly on the task_form, which I had trouble doing at first because I didn't realize that when you did that you had to drop what would have otherwise been the first argument, task, in this case). So anyway, I think I'm good to go.

@glennpow: I think I'm going to have to do what you are trying to accomplish as well, so I will share any good solution I may come up with - I would also be grateful if you did likewise.

Cheers


152. glennpow Jul 24, 2008 at 00:58

@mellifluidic et al.
So with the latest edge rails, there's a new feature for mass-assigning hash attributes, which uses an ":accessible => true" parameter appended to any has_one, has_many, etc in the model file.
http://github.com/rails/rails/commit/e0750d6a5c7f621e4ca12205137c0b135cab444a
I think this is similar to the "attr_accessible" method used before in the user.rb, but this also seems to allow these sort of forms to be created:

<% form_for @user do |f| %>
  <%= f.text_field :login %>
  <% fields_for :phone_numbers do |pn_f| %>
    <%= pn_f.text_field :area_code %>
    <%= pn_f.text_field :number %>
  <% end %>
  <%= submit_tag %>
<% end %>

So you don't have to mess around with "[new_phone_attributes]" or any custom attribute setter methods. This is GREAT!
I've only just learned about this, as it's very new, but I'll try it out and post any findings.

One thing that I already see as a possible snag is the use of AJAX-updated selects (for instance) in any of these nested resource form-segments.
EX. If I have a user with N number of addresses, where each address form has a country_id SELECT with a dynamically-updated region_id SELECT, then there will probably be conflicts when one country SELECT tries to update it's (and only it's) region_id SELECT.

hm, anyways. I'll be back.


153. Mark Richman Jul 28, 2008 at 12:21

Anyone know how to use text_field_with_auto_complete in place of f.text_field in the partial?

Using Ryan's example:

<%= text_field_with_auto_complete :task_attributes, :name, :object => task %>

Because it's a partial, the id and name attributes are duplicated, so the autocomplete only works on the first instance of the partial, not to mention it's invalid XHTML to have dupe id's.

Thanks,
Mark


154. mellifluidic Jul 29, 2008 at 11:03

@glennpow (et al.):

Awesome! Looks sweet. I was searching around on Rails Forums for solutions after just sort of winging it, and came across the following thread: http://railsforum.com/viewtopic.php?id=14905.

It looks like the attribute-fu plugin tidies things up similarly. When I get a chance I'll play around with both methods and see what I can see.

I should mention though that I've got an even more complicated situation - the kind of fields that I use for each subtasks depending on what kind of subtask you are dealing with. So imagine that either the task or the subtask has a select which when selected renders the appropriate fields for that selection. I'm gonna take this as it comes, but it's down the road for me so I'm keeping an eye on it.


155. victor Jul 31, 2008 at 10:05

Has anyone tested this in IE7? IE7 does not seem to like the prototype calls to remove the element from the form IF it was added via "Add a task" or other javascript call. Works fine in IE6 and FF (and Safari I'd imagine)

Any ideas?


156. Walker Hagius Aug 10, 2008 at 11:15

I'm wondering if I did something stupid (I did make some changes to fit it into my app), since nobody else has mentioned this problem, but my tests (luckily) turned up a nasty bug when I implemented this solution (which is fantastic otherwise, btw!)

The problem comes from existing_task_attributes=. Since that runs before the parent model (Project) is validated, existing children (Tasks) will be changed/deleted in the DB even if the parent model fails validation. I haven't actually tried Ryan's Project/Task code myself, but in my own app this would allow these sorts of scenarios:

1. Delete all children on edit page, but parent requires children (here if a Project required 1 or more tasks). Parent fails validation and goes back to edit page, but children have already been deleted. If you cancel back out of edit, children are gone from DB even though parent never updated.

2. Delete all existing children, and add new one that is invalid. (Say a task requires a name and you don't add one, so that child fails validation.) Parent will fail validation if it validates associated records and will go back to edit page, but existing records will have been deleted. Again, if you cancel back out of edit, children are gone from DB even though parent never updated.

3. Modify existing child(ren), and add new one that is invalid. Like above, parent validation fails, but child is still updated.

Is this just a quirk of my app, or a bug nobody has hit/noticed/commented on?

My fix was just to wrap a transaction around the update_attributes call (and make it update_attributes! so it would throw an exception on failure) and move the else clause to a rescue block. This way, if the parent model validation fails all the other changes to the children are rolled back as well. Tested and it works, though check out my blog post for a gotcha about testing Rails transactions: http://freevirusesandspyware.com/2008/08/testing-rails-transactions.html


157. Elaine Aug 13, 2008 at 09:33

Is there a way to count the number of times you have clicked on "Add a task" without using Javascript? I'm using a radio button in the partial and need a way to distinguish if one of the added partials contains the radio button that was the selected.


158. Jason Aug 13, 2008 at 20:18

the default date_select will break this quite spectacularly. I dont have a workaround yet but want to give a heads up.


159. Erik Aug 15, 2008 at 13:53

Yes, I'm having issues with this method and a date_select as well. When submitting the form it throws a:

Status: 500 Internal Server Error
  Conflicting types for parameter containers. Expected an instance of Array but found an instance of Hash. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were {"date1(1i)"=>"2008", "date1(2i)"=>"8", "date1(3i)"=>"15", "date2(1i)"=>"2008", "date2(2i)"=>"8", "date2(3i)"=>"15"}.)

Any ideas?


160. Tim Aug 24, 2008 at 21:51

In my application I would like to automatically number my tasks and display the number on the page. So far all of my attempts have resulted in the same number for all lines after the first, e.g. 1, 2, 2, 2.

In my task model:

  @@line_no = 1
  def initialize(attributes = nil)
    super
    unless line_no?
      self.line_no = @@line_no
      @@line_no += 1
    end
  end

Ideas?


161. Peter Aug 28, 2008 at 07:31

Similar problem like Tim: how to (auto-)number the tasks? I don't mind if on remove it isn't actually shown correct, but after commit the task should be written correctly (1, 2, 3, ...) to the database.


162. kain Aug 30, 2008 at 08:57

I would really like to see a screencast covering those methods but with a has_many :through, cleaning associated records and then rebuild them in a callback would be an example.


163. Bernd Aug 30, 2008 at 11:46

Thank you Ryan for your casts.

That was what i was looking for, where I have one problem to enhance it a little bit for some dynamic usage with radio buttons.

I use a different model, but to make it easier to understand it I'll use the task and project model to explain my problem.

I use select instead of a text_field in _task.erb, which works. But the database query should depend on a radio button where I ask which type should be used.
The reason for that is that a task database entry uses two fields to point to two other tables.

Here is the _task.erb code which I try to use:

<tr class="task">
 <% fields_for_task(task) do |task_form| %>
  <td id="radio">
   Task Low<%= radio_button_tag('type', 0, checked = true) %><br>
   Task High<%= radio_button_tag('type', 1, checked = false) %>
  </td>
  <td id="select">
   <%= task_form.select :todo_id, Todo.find(:all, :conditions => "tasklow_id is not NULL").collect {|p| [ "#{p.tasklow.title}", p.id ] }, { :include_blank => true } %>
  </td>
  <td><%= link_to_function "remove", "$(this).up('.slot').remove()" %></td>
 <% end %>
</tr>

The dynamic part is to use either (tasklow_id and p.tasklow.title) or (taskhigh_id and p.taskhigh.title). While defaulting to tasklow.
Btw, one of tasklow_id or taskhigh_id is NULL and the other one has a value.

Refactoring the select code to a partial could be a way to go and use onclick for the radio button, but I have no idea how to avoid getting many select fields and how to get the initial select box since the onclick works only onclick.


164. Rick Sep 05, 2008 at 11:06

Can someone post their working validation example? Actually, I have it all working, EXCEPT for the extra parent level messages of "Tasks is invalid" (in my case not Tasks, but you get the idea.) I don't want these messages since I have the correct task error messages following the main project messages.

I know some others earlier in this thread have mentioned the same thing, but I'm looking for what people have done about it. Does someone have a snippet of code that shows the best way to handle this? I would think it would just involve iterating over the errors and removing the messages I don't want? I'd like to see the best way to handle this, though.


165. yourstruly_vinay Sep 08, 2008 at 22:01

Ryan,
These three screencasts really pack a punch! thanks a lot for this series especially. There is one small glitch i am running into though.. hoping someone can point out the cause of the bug..
i have a complex form with fields from 3 models in the edit and new templates. 2 of them work just fine. in the fields from the 3rd model, there is a collection_select field where i get the following error
/!\ FAILSAFE /!\ Tue Sep 09 10:07:52 +0530 2008
  Status: 500 Internal Server Error
  Conflicting types for parameter containers. Expected an instance of Array but found an instance of Hash. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were {"1"=>{"days"=>"7"}}.)

i have set ':index => nil' in the code.. here is my code for that field

<p><label for="frequency">Frequency (in days)</label><br/>
<%= h.collection_select :days, Dates.find(:all), :no_of_days, :no_of_days, :index=>nil %></p>

every other field has ':index => nil' in place and the html code generates fine. any thoughts?


166. Tony V Sep 09, 2008 at 15:44

yourstruly_vinay: I think if you use :index => "" instead of nil, it'll work. A quirk in collection_select.

Now my problem:

I'm in the same boat as glennpow and others in that I'm befuddled by compound model creation: I can get two models to parent correctly as in the screencasts, but if I try to get to a third level, no soap.

My situation:
I have an Invite model that is a proxy for new (mass assignment) User creation:

(invite) has_many => :users
(user) belongs_to => :invite

 This works fine, as in Project/Task creation in the screencasts. However, I also want to have a new Group Membership created for each of the (new) Users created in the same transaction (all Invites are scoped to a Group):

(invite) has_many => :memberships
(user/group) has_many :groups/:users, :through => :memberships
(group) has_many :invites

 I can generate everything fine with two sets of virtual attributes (for the User and the Membership), EXCEPT the (new) User id for the Membership (If I try to hardwire something like :user_id => @invite.users.id, I get an id like '1546789'. I assume this is because I'm not nesting the Membership creation within the User creation within the Invite. Or maybe I'm just being a dopey newbie....

If anyone has successfully done a multi-level, multi-model form, I'd love to see the code.

Thanks for your ideas.


167. yourstruly_vinay Sep 09, 2008 at 23:59

@Tony V: thanks for the reply. Although, i found out that :index has to be passed with the html_options for collection_select and its family of form helpers.

Ref: http://dev.rubyonrails.org/ticket/589

I am getting another error that noone else seems to be getting, oddly enough. i am getting an "ActiveRecord::ReadOnlyRecord" error on the save_tasks method. It is raised because when we do tasks.each, the record is instantiated as a read only. (Ref: http://api.rubyonrails.com/classes/ActiveRecord/ReadOnlyRecord.html). The solution is here - http://learn.theworkinggroup.ca/2007/2/6/activerecord-readonlyrecord-error-what. However, im not sure if its great programming because of the many queries raised. Any work arounds? hope that was helpful for someone.


168. yourstruly_vinay Sep 11, 2008 at 04:37

I did this for an HABTM association. The join table records are not getting deleted on removing the child records. Everything else is fine though. So it is just the clutter in the join table that has to be removed. Just wondering if Pascal, pimpmaster or any other managed a solution to that..


169. Tom Cocca Sep 12, 2008 at 08:55

@chrisff (post 51) and @Kevin Triplett (post 114)

I was running into the following problem.

I had a page and the association was files. You could upload numerous files by clicking on the "Add file" link.

If I only had one file the validation code worked fine. If I had multiple it was running through the association and adding the errors to the base for the page model for every instance of the field.

For example. If I had a new page with 1 file (file required) and I didn't upload the file I would get 1 message saying:

File: no file uploaded

If I had two instances of the file added and I didn't upload the file I was getting 4 errors:

File: no file uploaded.

This is because 2 fields with the name :file were getting passed in so it was running the association validations twice so 2 errors each time yielded 4 errors for file.

I added the code to store and array of each field the the validation was run on and push the name of the field into the array after each. They use a statement to check if the name of the field is already in the array and not run the association validations if this is the case. This eliminated the extra validations on the association field. See the pastie:

http://pastie.org/271260

If anyone has a better way please respond.

chrisff your code was the basis and Kevin your error patch to remove the "'Association' is invalid" was a huge help.

I have used both of your pasties and modified them slightly for the error validation.

~ Tom


170. Walker Hagius Sep 12, 2008 at 21:01

Rick-

See my post here for info on how I solved the duplicate validation message problem:

http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/df8f450149b71b1/3f6aea404f962b30?lnk=gst&q=hagius#3f6aea404f962b30


171. rickcr Sep 21, 2008 at 18:42

Thanks Walker. I took a slightly modified approach of what you did. I replied in the google group (charlesrc) to your message:

http://groups.google.com/group/rubyonrails-talk/msg/163ada44b0efb1a3


172. jc Sep 23, 2008 at 09:32

I got to try out Michael's multimodel-forms plugin. It works quite well.

I also had to add radio buttons to my stuff and I used Sarah's solution.. I feel this is a much better solution to the problem than using javascript as it is more difficult to validate. Unfortunately right now I've only done this hacked into Michael's plugin and it's not very DRY:

in_attributes.each_with_index do |attributes, j|
# I just added these two lines
attributes["correct_answer"] = attributes.include?("correct_answer_id") ? 1 : 0
attributes.delete("correct_answer_id")

Good stuff guys, it's a big help.

As for validation - any thoughts on server-side validation to make sure there are a certain number of associated (belongs_to) records at all times? So for example you can only make a Book that has at least two Chapters. Or what about something like at least one Chapter has to be flagged (flag column) as "introduction"?


173. jc Sep 23, 2008 at 09:34

Also wrt my last comment - I used radio buttons, but mine don't require multiple radios per record, only one because they're selecting one of the records to flag. I got lucky in that what I wanted to do fit the broken situation.

Didn't want anyone to think it was a solution for radio buttons where you need to define a set for each record.


174. Mark Richman Sep 23, 2008 at 12:24

How does this work if you have a has_one relationship? Say, for example, Project has_one :billing_detail, where BillingDetail belongs_to :project?

I'd like to present the project and it's billing details in one form for new/edit.


175. Carl Fyffe Sep 26, 2008 at 06:08

I have created a plugin that implements this functionality. Please check it out here: http://github.com/sixty4bit/acts_as_virtual_attribute

I used the older should_destroy? method instead of creating a blank array in the container.

Why did I create this plugin? Because my current project has 50 of these on one model.


176. cs Oct 13, 2008 at 07:28

Ahoy,

First of all, thanks Ryan, you're still my #1 RubyHero :D

I've create a much simpler version of Ryan's example with a single select box and a list of checkboxes to store the associations.

Also uses client-side javascript and the code is less, without hacks.

Take a look at: http://clair.ro/rc/2008/10/13/has_many-with-select-box-and-checkboxes/


177. mauricio Oct 13, 2008 at 11:40

first, thanks ryan!

for those who get trouble with check_box adding more than rigth rows, due to unchecked value hidden field, my work arround was:
-at the task_attributes method setter loop (at project.rb model), I changed the "tasks.build(attributes)" line, to "tasks.build(attributes) unless attributes.values.length == 1", where attributes.values.length represents the quantity of fields for each task sent by the form.
I took the idea from http://railsforum.com/viewtopic.php?id=1063


178. Jimmy Oct 16, 2008 at 09:48

I have also having the problem where the params hash isn't properly created for attribues. I have the following example:
<input type='hidden' name='t[test][][1]' value='1'>
<input type='hidden' name='t[test][][2]' value='2'>
<input type='hidden' name='t[test][][3]' value='3'>
<input type='hidden' name='t[test][][1]' value='4'>
<input type='hidden' name='t[test][][2]' value='5'>
<input type='hidden' name='t[test][][3]' value='6'>
<input type='hidden' name='t[test][][1]' value='7'>
<input type='hidden' name='t[test][][2]' value='8'>
<input type='hidden' name='t[test][][3]' value='9'>

which creates the following params[:t][:test] => [{"1"=>"1"}, {"1"=>"4"}, {"1"=>"7", "2"=>"2"}, {"2"=>"5"}, {"2"=>"8", "3"=>"3"}, {"3"=>"6"}, {"3"=>"9"}]

As you can see the grouping isn't being properly assigned - i.e. params[:t][:test][0] should be { :1 => "1", :2 => "2", :3 => "3" }

Any advice at all on this is great appreciated.


179. Jose Oct 17, 2008 at 13:51

I'm having problems with the code in this line: "attributes = task_attributes[task.id.to_s]". I Keep getting the error "can't convert String into Integer" if I take out the to_s method it goes away, but the existing tasks get deleted. Anyone ideas why?


180. Jose Oct 17, 2008 at 18:03

Ok...found what was MY problem...

Note to self: Adapt the code to your liking!


181. Jimmy Oct 19, 2008 at 10:04

Uninstalling and reinstalling rails fixed my problem of the grouping of the dynamic files.


182. Lee Oct 19, 2008 at 23:22

I'm running into a weird problem I can't seem to figure out. I've got the code from the book working per se. However, no matter how many new attribute fields I add via the RJS link (in my case, it is "Add Another Child"), only the child entered into the original fields (i.e.: the first Child/Task field presented on load) is sent back to the controller. None of the rest are sent. If you click "Delete" and delete all the fields, then add one more back, the data in that new field isn't saved either, so it seems that for some reason only the original field is saved.

Strangely, after inspecting the generated source, it all looks fine and as if it has been compiled correctly. The HTML generated by the original view (with the initial child/task) is identical to the HTML generated when you delete the first child/task field and immediately add another one back, yet the latter doesn't send anything back to the controller.

Blows my mind, anyone else seen similar problems?


183. SH Oct 31, 2008 at 02:32

I am getting a
syntax error, unexpected '='
  at params[:project][:existing_task_attributes] ||= {}
Can you help me out?


184. SH Oct 31, 2008 at 02:55

Sorry the problem got fixed. It was a silly mistake. I had a space between || and =.


185. Jeroen van Dijk Nov 07, 2008 at 03:00

Great example Ryan!

Here is an elegant solution for the problems caused by array and hash index clashed (also works with date_select)

http://gist.github.com/22824


186. Jeroen Nov 07, 2008 at 03:56

My solution is only tested in rails 2.1..


187. Niklas Nov 30, 2008 at 12:40

Hi Ryan,

Excellent tutorial! I have a problem regarding has_many and the dependent => :destroy command. I have two models, one for an Item and another for Variation but when I delete my item the variations connected to it isn't deleted, even though I have the :dependent => :destroy attribute set.

A short preview can be found at http://pastie.org/327247.

I can get my variations when I view the item, I can update and everything. It's just when I want to delete the Item. Any suggestions?

Kind regards,
Niklas


188. Chris Dec 04, 2008 at 10:51

Hi Ryan,

Is there a way to monitor the task in the "new" event, and if it already exists select the existing id rather than creating a new one. That would be awesome, but I'm stumped, and I'm a noob.

Thanks,
Chris


189. Asbjørn Morell Dec 07, 2008 at 13:44

Ubercool! The new code from Advance Rails Recipes works fantastic. (Rails 2.2.2)


190. Iris Soto. Dec 22, 2008 at 10:37

Hi all,

I have a relationship many to many between two objects "factura" and "factura_producto". I've implemented this code to update many record into "factura_producto" table.

This send the following error:
Rendered orden/_factura_producto (0.00848)
Rendered orden/_factura_producto (0.00238)
Rendered orden/_factura_producto (0.00243)
Rendered orden/_factura_producto (0.00614)
Rendered orden/_factura_producto (0.00221)
Rendered orden/_factura_producto (0.00219)
Rendered orden/_factura_producto (0.00788)
Rendered orden/_factura_producto (0.00254)
Rendered orden/_factura_producto (0.00239)
Rendered orden/_factura_producto (0.00194)
Rendered orden/_fields (0.13827)
Completed in 0.21036 (4 reqs/sec) | Rendering: 0.14042 (66%) | DB: 0.03088 (14%) | 200 OK [http://localhost/orden/edit/1]
Conflicting types for parameter containers. Expected an instance of Hash, but found an instance of Array. This can be caused by passing Array and Hash based paramters qs[]=value&qs[key]=value.
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_controller/cgi_ext/cgi_methods.rb:204:in `type_conflict!'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_controller/cgi_ext/cgi_methods.rb:168:in `container'


191. Silas Baronda Dec 22, 2008 at 16:59

great information!
Is this still the proper way to do nested forms?
Does it matter how many levels your nested form is?


192. Josh Pencheon Dec 27, 2008 at 14:25

I've been using this method for a while, but I think I've found a problem with it.

If you delete all of the tasks in one fell swoop, by the looks of development.log the existing_task_attributes set of fields isn't serialised at all. This means that when update_attributes is called, the existing_task_attributes= method in the Project model isn't called, hence none of the tasks are removed.

This seems a little crazy, but the problem is very real for me!


193. Valk Jan 01, 2009 at 11:06

Things become really complicated when trying to add an autocomplete functionality to a dynamic form field. I started a thread here http://railsforum.com/viewtopic.php?pid=83963#p83963


194. Luvi Jan 12, 2009 at 23:59

I have a problem.
When I try to update the tasks, It's not updated but it's inserted in new row in database.
What's wrong?


195. Luvi Jan 13, 2009 at 00:12

I am getting a syntax error in task.rhtml: "undefined local variable or method `task' for #<ActionView::Base:0xb692bbf0>"
Why?


196. Styledev Jan 16, 2009 at 22:02

@Luvi c.198

Check to make sure the form for on your edit page is this:

- form_for :item, :url => item_path(@item), :html => { :method => 'put' } do |f|

Otherwise it will call the create action instead of the update action.


197. acode Feb 17, 2009 at 06:51

I have multiple models which I want to create. I have products and categories. The categories work like the tasks Ryan goes through in this episode, but I want products as well to be dynamically added and removed so in the partial for the category i added another partial for a product and I added helpers and extended the model.

However when I click the add category button then I get an error: missing } after property list
http://localhost:3000/shops/new#
Line 2

The buttons for removing and adding products works fine. If I remove the helpers for products then the category buttons works fine again.

Help!

Anyone who has had success using this with multiple models?


198. Michael Feb 19, 2009 at 05:33

As usual, great cast.

One question, is it possible to do something similar with a show form and 2 sub models. i.e show a projext and be able to add tasks as well as entries to a projectlog.

I tried simply adding a form to a show view to create the extra model but seem to have much success

any suggestions would be great

Michael


199. Rainer Feb 20, 2009 at 07:32

Great cast, and an even better update :). Thanks for that!

I'm currently struggeling with the following problem though:
If you delete a task but then have a failing validation on update (e.g. an empty project name) the task is deleted anyways.

Maybe not a big issue but inconsistant with the transaction model ("all-or-nothing").

The reason for this to happen is quite obvious if you watch the logs: the sql-DELETE is encapsulated by an individual BEGIN-COMMIT frame.

Is there any way to avoid that?

Thanks!
Rainer


200. Pascal Feb 22, 2009 at 13:00

Ryan, nice episode, many thanks!

I've added a checkbox to the tasks to mark them if they're done. Unfortunately, every time i mark a task as completed, it will be duplicated and the next task has the same name but becomes completed. If i hit save again, the procedure repeats again.

Any idea where the error could be? The whole code code is 1:1 the same as yours, except my addition

<%= tasks_form.check_box :is_completed, :index => nil %> is completed

Any suggestions would be great, thanks!

Pascal


201. Björn Grossmann Feb 25, 2009 at 03:32

Great episode! Thank you so much.

Has anybody used this in combination with cucumber and webrat for testing?

I would like to use wabrats fill_in method to fill in two or more tasks of the project (as in Ryan's example). However, as those fields are generated from the same partial they all have the same name and id.
Has anybody found a way to tell webrat to use the second or third field with a certain name/id?

Thanks for your inspirations!
Björn


202. Mark Turner Feb 25, 2009 at 18:12

Absolute wizardry :) Thank you!


203. David Southard Mar 03, 2009 at 20:35

Has anyone solved the collection_select issue. If so I'd love some help. I keep getting the errors described by @joe above.

if I enter a {} for the html options I get a nil object when evaluating nil.attributes=

w/o the {} in my collection_select I get a 500 error... Conflicting types for parameter containers. Expected an instance of Array but found an instance of Hash.


204. Brad Mar 11, 2009 at 19:21

I'd be interested to know how one might allow a user to create multiple projects AND multiple tasks on one page with jscript. I couldn't find a rails only method for keeping each new project separate from one another. Right now I'm using a jscript iterator for each new project created that creates a new 'row' in a multi-dimensional array. Works but seems like a hack method. I'd much prefer a method that doesn't require javascript for separating new projects from each other.


205. Hemant Mar 14, 2009 at 02:41

Hi,

Thanks! for the screencast, it was very helpful.
 
Recently i came across the same issue with date_select and time_select that @Erik, @Tim and many others are facing.

Status: 500 Internal Server Error
Conflicting types for parameter containers. Expected an instance of Hash but found an instance of Array. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value.

Here is the workaround for the issue:
http://agilerails.wordpress.com/2009/03/11/date_select-time_select-doesnt-work-with-auto_prefix-object/


206. Michael Mar 14, 2009 at 12:45

@Hemant Just used that workaround for date_select. Works perfectly on 2.2.2.


207. luciano Bonachela Mar 17, 2009 at 07:49

Hello Ryan,

Great screencast!

I creating a simple shopping cart, where you can add / remove itens, but since the field have the same name because :index => nil, the observe_field I have is not working.

Does anyone have some tip on using oberve_field here?

Thanks!

Luciano


208. Raphael Mar 26, 2009 at 08:41

Hello Ryan,

thx for your great work.

I try to follow your cast to use multiple model update in a single form.

But i have a problem and don't find any solution.

I have validation for the model let's say the primary. In your example in the project model.
When a fill up badly the form at the first time (i have the correct error message) and i correct it afterward, I have strange thing in the database.
Let second model (task in your example) has no reference to the first model. Project_id is NULL

I have look in more detail what 's happen. And when i have the validation error at the first time, the task object are created in the DB and at the second time, i have alreeady the object in the DB, thus this isn't new_mouton_attributes but existing_mouton_attributes that is used!

Can you help me, i can send you my code if you want

thank in advance

Raphael


209. Marcelo Ruiz Mar 27, 2009 at 11:29

Thank you! you helped me a lot!


210. Michael Mar 29, 2009 at 18:15

Hi Ryan.
First thanks for a great episode.

Is it possible to create a nested form building children model who themselves have a has_many relationship with their own children and successfully implemented this on the one form. Further to this, I am trying to implement this using ajax and wondered (again: if this is even possible?) how to pass the formbuilder object back to my partial.

One other quick one: I noticed that to date their seems to be only javascript workarounds when adding more than one child in the 2.3.2 accepts_nested_attributes_for. Are you aware of any work/changes pending to resolve this?

Appreciate all your work
Michael


211. Michael Mar 30, 2009 at 20:06

Found a way that appears to work which basically is a hybrid of the pre 2.3 way where I maintain the :index=>nil attribute and the 2.3 methodology. I also gave up on the form_builder and constructed the naming of the fields by hand circumventing the need to pass form builder objects either locally or by ajax.

Cheers
Michael


212. Marcelo Ruiz Mar 31, 2009 at 09:34

Hi, I wanted to share my solution for the checkboxes problem.

I simply use check_box_tag. For new:

<%= check_box_tag("project[task_attributes][][completed]", "1", false) %>

For edit:

<%= check_box_tag("project[task_attributes][][completed]", "1", task.completed == 1) %>

The "create" method does not change. If the checkbox is checked, then saves the "1" to the DB. In case the checkbox is not checked, I solve it with a default value of "0" in the DB.

In the case of the "update" method, what I do is iterate through the params[:task] array and check the value of the task_attribute:

params[:project][:task_attributes].each do |task|
  if task[:completed] != "1"
    task[:completed] = "0"
  end
end

I think it is the simplest and cleanest solution I have found.

Regards,
Marcelo


213. Sucellus Apr 29, 2009 at 06:46

That checkbox problem was a bear to get working for me. check_box_tag wouldn't work for updating and check_box wouldn't work for new tasks.

Here was my solution:

# in the partial
<% if @prefix == "new" %>
   <%= check_box_tag("project[# {@prefix}_task_attributes][][some_value]", "1") %>
<% else %>
   <%= sf_form.check_box 'some_value', {}, "1", "0" %>
<% end %>

# in the helper change prefix to @prefix


214. Tyler Gannon May 15, 2009 at 11:34

Thanks for the info Ryan! I am glad that in later railscasts the example code is available for download. For those who are trying to get this to work today, you can download example code from my google code repo at:

https://gone-cat-open-source-software.googlecode.com/svn/tags/herbs-test-railscast-75

A couple of differences:
-- changed the application concept a little to fit what I am trying to build. The structure is almost identical but I changed the names of the models.
-- replaced prototype with jQuery so the javascript is a little different from what you find in the cast.
-- this example code was build on rails 2.3.2.


215. Baran Khan May 25, 2009 at 03:26

Hi!!!
I want downloadable code of this episode.
Tyler Gannon your said URL is password protected


216. Matt Jun 03, 2009 at 06:07

Use http to see Tyler's code.

http://gone-cat-open-source-software.googlecode.com/svn/tags/herbs-test-railscast-75


217. Jay Jun 03, 2009 at 19:26

I am having some conflicts between jquery and setting defaults in javascript_include_tag. Does anyone know how Tyler converted the railscast prototype javascript to use jquery instead?


218. Tola Jun 05, 2009 at 11:14

Hi Ryan, This is a grt screencast. I tried it out and everything works besides one thing, which is adding new task. I can edit the task, i can delete the task but i can not add a new task. All the code is the same as shown in your screencast...any reason why?


219. Andrey Jun 11, 2009 at 05:19

This is great! Exactly the use case I was battling with. You saved my day definitely.


220. Horace Ho Jun 14, 2009 at 08:29

Hi Ryan,
Thanks for the great complex form railscasts! Do you have plan to update them for 2.3 with Nested Model Forms?


221. Nitin Jun 23, 2009 at 02:59

Thanks a lot Ryan for the highlighting the hidden secrets of Rails ActiveRecord and all others. Mass assignment made easy via params[:project][existing_task_attributes][]...

A tip for someone who might be hunting for Mass assignment in a belongs to many through a third connecting table Scenario -

Proposal can have many Sections via Proposalsections.

Decided by a checkbox to create/delete a new/existing Proposalsection record for a Proposal.
----
code for view - with checkboxes
for openingsection in @sections
<li class="topItem clear">
<%@pso=Proposalsection.find_by_proposal_id_and_section_id(@proposal.id, openingsection.id)%>
<% if @pso.nil? %>
<%= check_box_tag "proposal[proposalsection_attributes][][section_id]", openingsection.id %>
<% else %>
<%= check_box_tag "proposal[existing_proposalsection_attributes][#{@pso.id}][section_id]", openingsection.id, :checked=>true %>
<% end %>
<%= openingsection.name %></li>
end
-----
code for controller - saving Proposalsections with Proposal fields

params[:proposal][:existing_proposalsection_attributes] ||= {}
@proposal=Proposal.find(params[:id])
@proposal.update_attributes(params[:proposal])
  end

It worked for me after wasting full day tweaking over it.


222. Nitin Jun 23, 2009 at 03:03

code for my last post seperately -

for view -
<script src='http://pastie.org/521362.js'></script>

for controller -
<script src='http://pastie.org/521366.js'></script>


223. Mike A Jun 27, 2009 at 12:01

Nitin, can you clarify where the opensection.id is defined? Is it part of 'ProposalSections' or 'Proposals'?
Thanks.
This may be just what I need to solve a similar membership type challenge i'm having with my view.


224. Thomas Jul 26, 2009 at 14:32

Foreign ID Question:

I am having the hardest time figuring out why the foreign ID field is not being automatically populated in my site, which I've based my form off of Ryan's spectacular tutorial (thanks Ryan). From what I understand, the foreign id is put into the task when the build method is called in the line "@project.tasks.build". Then the foreign id is saved in the Project model under the "existing_task_attributes" method. Is this all correct or is the foreign id populated in some other way?

Thanks,
Thomas


225. Brent Lintner Aug 17, 2009 at 14:19

Its a big arm pump finally getting the hang of all this. Thanks for the cast, as well as the recipe entry about this!

Just to the poster before me (as I was doing it with selects), here is a good tutorial.

check out http://guides.rubyonrails.org/form_helpers.html#making-select-boxes-with-ease


226. frank patterson Aug 23, 2009 at 17:55

Ryan,
Thanks for all your screencasts! They are lifesavers!

I am really having trouble with the updating of attributes. The build of a new project (routinglist) works great...but I get the error "'can't convert String to Integer" in the Existing_Detail_Attributes= method. the creation of a new RoutingList with Details works fine, it is only when I try to update the RoutingList and save the attributes. If I change detail.id.to_s to just detail.id, I get no errors, but it deletes all my details.
Any ideas? BTW I am running this on Rails 2.0.2 (I upgraded to 2.3.3 but I never could get Mongrel to start, so I am still using 2.0.2)

Here are the 2 relevant methods in the model (detail.id=13):

def new_detail_attributes=(detail_attributes)
   detail_attributes.each do |attributes|
       routlist_details.build(attributes)
   end
 end

 def existing_detail_attributes=(detail_attributes)
   routlist_details.reject(&:new_record?).each do |detail|
     logger.info 'detail='+detail.to_yaml
     attributes = detail_attributes[detail.id.to_s]
     if attributes
         detail.attributes = attributes
     else
         routlist_details.delete(detail)
     end
   end
 end

Thanks - Frank


227. Gruff Aug 29, 2009 at 02:36

I'm struggling with multiple radio box groups. I don't want (ideally) to solve this problem with javascript if I can help it. Has anyone figured a way to get multiple radio button groups working without JS?

Thanks!


228. Temirlan Sep 03, 2009 at 06:37

how can I define dafault values for task, for example


229. docgecko Sep 16, 2009 at 04:43

Hi Ryan,

in terms of the addition of

params[:project][:existing_task_attributes] ||= {}

in the controller (update), this was causing a problem for me, so I removed it and all works fine.

However, by removing this, would you see any implications? I know you use if when checking for existing attributes (of course), but in my case I have a nested model where the child model is created when the parent model is created and can contain blank field. Therefore, it doesn't matter about checking for new or current attributes...I simply have attributes whatever.

As such, what is the purpose of this line of code?

Many thanks,

Doc


230. Gayathri Sep 25, 2009 at 04:58

@chrisff (post 51) and @Kevin Triplett (post 114)

I need to create custom validate message. I have a association model which has 2 associations .

For example i have list of companies as company_profile.rb
has_many:group_supported_school

In group_supported_school
i have two fields school_id and company_profile_id

associations are
belongs_to:company_profile
belongs_to:school

i need to validate the school_id here which is valid or not.

Its working fine but validation saying as default
Group supported school is invalid .

can u suggest me the way to do


231. Jonathan Oct 01, 2009 at 11:59

This is great! Just what I needed! One comment - this doesn't work so well with a datetime_select:

# projects_helper.rb
def fields_for_task(task, &block)
  prefix = task.new_record? ? 'new' : 'existing'
  fields_for("project[#{prefix}_task_attributes][]", task, &block)
end

Results in:

/!\ FAILSAFE /!\ Thu Oct 01 14:11:41 -0400 2009
  Status: 500 Internal Server Error
  expected Hash (got Array) for param `new_broadcast_attributes'

I had to hack it to look something like this:

def fields_for_task(task, &block)
    projectattributes = task.new_record? ? 'new_task_attributes][][' : 'existing_task_attributes]['
    fields_for("event[#{projectattributes}]", task, &block)
  end

Datetime_select on a new project would fill in the empty [] with its own data. This wasn't a problem on an edit since the [] contained a task ID.


232. Michael Oct 04, 2009 at 07:46

When I click the destroy link I get the following error:

TypeError: Result of expression '$(element).next('.should_destroy')' [undefined] is not an object.

Have double checked the code, and all other aspects work. Any thoughts?


233. Gayathri Oct 05, 2009 at 07:41

I am not able to get validation message on update. Kindly help me in this...


234. Kamil Nov 03, 2009 at 12:48

Hi, thanks for great tutorials ! I tried build complex form for habtm associtions, but I get this error: ActiveRecord::ReadOnlyRecord. Please can you help me and describe me how it works and how to fix it? Many thanks.


235. Kamil Nov 03, 2009 at 13:09

Solved :D. I overwrite readonly? method in controller so:

def readonly?
     false
end


236. Andre Nov 10, 2009 at 07:26

This tutorial is great and ive got it 90% working with my app. everything is happening except the Delete part.

The Javascript works up to where it hides the Task input box but it doesnt set the should_destroy value ... if i inspect the Params, its always null..

any ideas?


237. Achmad Gozali Nov 20, 2009 at 05:48

Hi Ryan,

I got this error

undefined method `reflect_on_association' for NilClass:Class

on my application helper, AFAIK reflect_on_association is Rails method, please help

thanks


238. Saurabh Dec 12, 2009 at 11:24

I used this approach with remote form. The hash that's created when the form is posted (non-Ajax) is different from the Ajax (Form.serialize). The ajax hash does not create the desired hash. Instead of creating
[{:a=>1, :b=>2}, {:a=>3, :b=>4}]
it creates
[{:a=>1}, {:b=>2}, {:a=>3}, {:b=>4}]

Any solutions here? Is this a known bug with rails?


239. testin Dec 16, 2009 at 23:03

testing comment


241. John Jan 11, 2010 at 07:46

Excellent railscast Ryan.

If I has several tasks, say 50 or more, I don't want to add a single text field but search for a particular task. Any ideas in which direction I should go?

Many thanks,
John


242. Abhishek Shukla Jan 29, 2010 at 22:51

Excellent Thansk ryan. You have a solution for everything :)

Made a bit changes as I am using jrails

With jRails
    <%= link_to_function "remove", "$(this).up('.task').remove()" %>

<%= link_to_function "remove", "$('#task').click(function() {$(this).remove();});" %>

 

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player
Give Back to Open Source