khagimoto
2009-Aug-01 01:09 UTC
Form problem with three layer deep "accepts_nested_attributes_for"
Hello,
I''m going to borrow this (http://gist.github.com/53464) example, since
my model is set up exactly the same way.
I''m trying to build a form that will add as many Parent to a
GrandParent and as many Child to a Parent as user wants in a nested
form. It work if I only have one parent and multiple children. As
soon as I add more than one parent, the problem starts.
I have partials for both parent and child, where child partial gets
included in the parent partial with "form.fields_for" call. In the
helper, I have methods to add/remove parent or child at will.
Something like this:
======================================= def add_parent_link(form_builder)
link_to_function ''Add More parent'' do |page|
form_builder.fields_for :parents, Parent.new, :child_index =>
''NEW_RECORD'' do |f|
html = render(:partial => ''parent'', :locals => {
:form => f })
page << "$(''parents'').insert({ bottom:
''#{escape_javascript
(html)}''.replace(/NEW_RECORD/g, new Date().getTime()) });"
end
end
end
def remove_trip_region_link(form_builder)
if form_builder.object.new_record?
link_to_function("Remove this Region", "$(this).up
(''.more_regions'').remove();");
else
form_builder.hidden_field(:_delete) +
link_to_function("Remove this Region", "$(this).up
(''.more_regions'').hide(); $(this).previous().value =
''1''")
end
end
=======================================
The problem is that the child form gets messed up since it''s inside
the parent form and (I think) "escape_javascript" is done twice on it.
Has anyone encountered this type of problem? If so, what was your
solution?
Thanks!
Josh White
2009-Aug-01 19:22 UTC
Re: Form problem with three layer deep "accepts_nested_attributes_for"
I thin to help you I would need to see the params that show up in the log from the post request and your controller logic which saves it. On Jul 31, 6:09 pm, khagimoto <kumi.hagim...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> Hello, > > I''m going to borrow this (http://gist.github.com/53464) example, since > my model is set up exactly the same way. > I''m trying to build a form that will add as many Parent to a > GrandParent and as many Child to a Parent as user wants in a nested > form. It work if I only have one parent and multiple children. As > soon as I add more than one parent, the problem starts. > > I have partials for both parent and child, where child partial gets > included in the parent partial with "form.fields_for" call. In the > helper, I have methods to add/remove parent or child at will. > Something like this: > > =======================================> def add_parent_link(form_builder) > link_to_function ''Add More parent'' do |page| > form_builder.fields_for :parents, Parent.new, :child_index => > ''NEW_RECORD'' do |f| > html = render(:partial => ''parent'', :locals => { :form => f }) > page << "$(''parents'').insert({ bottom: ''#{escape_javascript > (html)}''.replace(/NEW_RECORD/g, new Date().getTime()) });" > end > end > end > > def remove_trip_region_link(form_builder) > if form_builder.object.new_record? > link_to_function("Remove this Region", "$(this).up > (''.more_regions'').remove();"); > else > form_builder.hidden_field(:_delete) + > link_to_function("Remove this Region", "$(this).up > (''.more_regions'').hide(); $(this).previous().value = ''1''") > end > end > =======================================> > The problem is that the child form gets messed up since it''s inside > the parent form and (I think) "escape_javascript" is done twice on it. > > Has anyone encountered this type of problem? If so, what was your > solution? > Thanks!
khagimoto
2009-Aug-02 20:28 UTC
Re: Form problem with three layer deep "accepts_nested_attributes_for"
I actually never get to that point.... This is a problem that I see
on screen when I load the page before submitting the nested form.
The "Add More Parent" (and "Remove this Parent") links are
created by
the helper method below.
=======================================def add_parent_link(form_builder)
link_to_function ''Add More parent'' do |page|
form_builder.fields_for :parents, Parent.new, :child_index =>
''NEW_RECORD'' do |f|
html = render(:partial => ''parent'', :locals => {
:form => f })
page << "$(''parents'').insert({ bottom:
''#{escape_javascript
(html)}''.replace(/NEW_RECORD/g, new Date().getTime()) });"
end
end
end
def remove_parent_link(form_builder)
if form_builder.object.new_record?
link_to_function("Remove this Parent", "$(this).up
(''.more_parents'').remove();");
else
form_builder.hidden_field(:_delete) + link_to_function("Remove
this Parent", "$(this).up(''.more_parents'').hide();
$(this).previous
().value = ''1''")
end
end
=======================================
When I first load the page with the three-layer nested form,
everything looks fine with one parent and one child of it showing up.
I can even "add more child" to the initially visible parent form.
Problem occurs when I "Add more Parent". When the next parent is
added, I see one parent with mangled child form. And I think that is
because the child form goes through the helper method twice - one
method is the "add_parent_link" shown above and the other is
"add_child_link" (not shown, but does exactly the same as
"add_parent_link"). I hope I''m making sense here..
I''m hoping that
I don''t have to do this with two separate form-submits, but maybe
that''s what I have to do.. :-(
Thanks!
kikan
2009-Aug-13 16:13 UTC
Re: Form problem with three layer deep "accepts_nested_attributes_for"
Hi,> Problem occurs when I "Add more Parent". When the next parent is > added, I see one parent with mangled child form. And I think that is > because the child form goes through the helper method twice - one > method is the "add_parent_link" shown above and the other is > "add_child_link" (not shown, but does exactly the same as > "add_parent_link"). I hope I''m making sense here.I run into the same problem. I tried to pass an option "already_escaping" set to true if I add_parent_link is called from a partial which was already called add_parent_link. And then, I used the options like this : if options[:already_escaping]!=true html = escape_javascript(html) end I even rewrote the escape_javascript to become "unescape_javascript" like this : JS_UNESCAPE_MAP = { ''\\\\'' => ''\\'', ''<\/'' => ''</'', ''\n'' => "\r\n", ''\n'' => "\n" , ''\n'' => "\r" , ''\\"'' => ''"'' , "\\''" => "''" } def unescape_javascript(javascript) if javascript javascript.gsub(/(\\\\|<\\\/|\\\n|\\"|\\''|\n)/) { JS_UNESCAPE_MAP [$1] } else '''' end end But still without success. I think what''s important is to understand what''s beeing called and when. The children partial seems to be rendered while contructing the parent one. When it''s rendered, the Javascript is escaped. And then when the parent is rendered, the Javascript is escaped once again. Tell me if I''m wrong. It''s not a solution to the problem, but maybe seeing it from our two points of vue will help us find a solution :-) The methods in my helper are like this : def helper_remove_link(fields) out = '''' out << fields.hidden_field(:_delete) out << link_to_function("Supprimer", "$(this).up(''.# {fields.object.class.name.underscore}'').hide(); $(this).previous ().value = ''1''") out end def helper_add_record_link(form_builder, method, caption, options {}) options[:object] ||form_builder.object.class.reflect_on_association(method).klass.new options[:partial] ||= method.to_s.singularize options[:form_builder_local] ||= :f options[:insert] ||= method link_to_function(caption) do |page| form_builder.fields_for(method, options[:object], :child_index => ''NEW_RECORD'') do |f| html = render(:partial => options[:partial], :locals => { options[:form_builder_local] => f }) if options[:already_escaping]!=true html = escape_javascript(html) end page << %{ $(''#{options[:insert]}'').insert({ bottom: ''#{html}''.replace(/NEW_RECORD/g, new Date ().getTime()) }); } end end end And I call the helper like this, to add a children : <%= helper_add_record_link f, :contacts, ''Ajouter un contact...'', :insert => "#{prefix}contacts_#{f.object.id ? f.object.id : ''NEW_RECORD'' }", :already_escaping => true %> Best regards, Christian