New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rewrite has_many form structure #2679
Rewrite has_many form structure #2679
Conversation
is_being_wrapped = @already_in_an_inputs_block | ||
@already_in_an_inputs_block = false | ||
|
||
html = with_new_form_buffer do | ||
template.content_tag :div, class: "has_many #{assoc}" do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The has_many
method is already a monster; why not abstract this wrapping behavior into a separate method? Something like:
def without_wrapper
is_being_wrapped = @already_in_an_inputs_block
@already_in_an_inputs_block = false
html = with_new_form_buffer{ yield }
@already_in_an_inputs_block = is_being_wrapped
html
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it, I'll give it a shot.
@seanlinsley Any idea why the hidden |
Why do you have an |
|
Well that is weird. I just tried that locally and it works just fine. |
Sanity check: are you sure you saved the file with the Sorry, I just want to make sure we're not missing anything because everything works perfectly on my end. |
Hmm. Looks like the |
It doesn't look like the form block gets evaluated, but how could that be the case? |
I think it has to do with the fact that we're turning off an internal Formtastic variable dealing with |
What version of Formtastic are you using? I'm using 2.3.0.rc2 |
possible source of the hidden id input: |
I just tried adding an input field for an attribute that didn't exist on my model, and I get 'undefined method form do |f|
f.inputs 'Details' do
f.input :name
end
f.has_many :posts do |p|
p.input :date, as: :datepicker
end
f.actions
end I'm using |
Also, the specs @igorbernstein provided do apparently pass for this build. It appears we're still just failing on our devise password resets. |
@seanlinsley I just figured out how to actually reproduce your test case on my machine and it throws the error just fine. Cannot reproduce. :( Rails 4.0.1 |
I have no idea if the hidden input is anything I can do anything about. It doesn't hurt anything, it renders fine, it has all the right attributes, it's just not inside the related fieldset. It's only for associated models that already exist in the system (hence the |
options[:class] ||= "" | ||
options[:class] << "inputs has_many_fields" | ||
# remove options that should not render as attributes | ||
builder_options = {new_record: true}.merge! options.extract!(:new_record, :allow_destroy, :heading) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is broken:
{new_record: true}.merge!({}.extract!(:new_record, :allow_destroy, :heading))
# {:new_record=>nil, :allow_destroy=>nil, :heading=>nil}
you want to use slice instead
I'm still having the same problem as last night. Pulling in these changes, I get an empty div tag, and when debugging it doesn't seem like my You're using Rails 4.0.1? I'm using 3.2.15, though I'd be surprised if that was causing this. |
Hmm, I worked backwards and this problem existed since the very first commit, 12922df7fe929088f3874c2d9298ec67a751bc0a Maybe related to @igorbernstein's comment: #2679 (comment) |
I haven't had time to play with this yet. Can you reproduce the issue in a test case? On Sat, Nov 9, 2013 at 12:12 PM, Sean Linsley notifications@github.com
|
Also, can you reproduce this issue on https://github.com/igorbernstein/active_admin/tree/2659-nested-has-many ? On Sat, Nov 9, 2013 at 12:25 PM, Sean Linsley notifications@github.com
|
With these changes the form started showing up: diff --git a/lib/active_admin/form_builder.rb b/lib/active_admin/form_builder.rb
index 2840c9a..e917810 100644
--- a/lib/active_admin/form_builder.rb
+++ b/lib/active_admin/form_builder.rb
@@ -50,8 +50,8 @@ module ActiveAdmin
def has_many(assoc, options = {}, &block)
# remove options that should not render as attributes
- builder_options = {new_record: true}.merge! options.extract!(:new_record, :allow_destroy, :heading)
- options = {for: assoc}.merge options
+ builder_options = {new_record: true}.merge! options.slice! :new_record, :allow_destroy, :heading
+ options = {for: assoc }.merge! options
options[:class] = [options[:class], "inputs has_many_fields"].compact.join(' ') |
I think If |
Yeah, it didn't behave like I expected anyway. This would be a better solution: def has_many(assoc, options = {}, &block)
custom_settings = :new_record, :allow_destroy, :heading
builder_options = {new_record: true}.merge! options.slice custom_settings
options = {for: assoc }.merge! options.except custom_settings |
From phone so pardon the formatting... |
@shekibobo I don't understand your points. My goal is to make the structure as simple and consistent as possible, whether or not you're wrapping in an
What? The goal of this is to remove the currently-added Hmm, maybe you didn't catch that because I didn't explicitly add that code change yesterday. I'm suggesting that, in addition to the changes above, we remove wrapping template.content_tag :div, class: "has_many #{assoc}" do
# ... |
The full changes being: diff --git a/app/assets/javascripts/active_admin/components/has_many.js.coffee b/app/assets/javascripts/active_admin/components/has_many.js.coffee
index 7122c34..d32b439 100644
--- a/app/assets/javascripts/active_admin/components/has_many.js.coffee
+++ b/app/assets/javascripts/active_admin/components/has_many.js.coffee
@@ -7,7 +7,7 @@ $ ->
#
$(document).on 'click', 'a.button.has_many_remove', (e)->
e.preventDefault()
- parent = $(@).closest '.has_many'
+ parent = $(@).closest '.has_many_container'
to_remove = $(@).closest 'fieldset'
parent.trigger 'has_many_remove:before', [ to_remove ]
@@ -28,7 +28,7 @@ $ ->
$(document).on 'click', 'a.button.has_many_add', (e)->
e.preventDefault()
elem = $(@)
- parent = elem.closest '.has_many'
+ parent = elem.closest '.has_many_container'
parent.trigger before_add = $.Event 'has_many_add:before'
unless before_add.isDefaultPrevented()
diff --git a/lib/active_admin/form_builder.rb b/lib/active_admin/form_builder.rb
index 4fbc5e6..385dc90 100644
--- a/lib/active_admin/form_builder.rb
+++ b/lib/active_admin/form_builder.rb
@@ -72,20 +72,21 @@ module ActiveAdmin
end
html = without_wrapper do
- template.content_tag :div, class: "has_many #{assoc}" do
- unless builder_options.key?(:heading) && !builder_options[:heading]
- form_buffers.last << template.content_tag(:h3) do
- builder_options[:heading] || object.class.reflect_on_association(assoc).klass.model_name.human(count: 1.1)
- end
+ unless builder_options.key?(:heading) && !builder_options[:heading]
+ form_buffers.last << template.content_tag(:h3) do
+ builder_options[:heading] || object.class.reflect_on_association(assoc).klass.model_name.human(count: 1.1)
end
+ end
+ inputs options, &form_block
- inputs options, &form_block
+ form_buffers.last << js_for_has_many(assoc, form_block, template, builder_options[:new_record]) if builder_options[:new_record]
+ end
- form_buffers.last << js_for_has_many(assoc, form_block, template, builder_options[:new_record]) if builder_options[:new_record]
- end
+ form_buffers.last << if @already_in_an_inputs_block
+ template.content_tag :li, html, class: "has_many_container #{assoc}"
+ else
+ template.content_tag :div, html, class: "has_many_container #{assoc}"
end
- html = template.content_tag(:li, html, class: "has_many_container #{assoc}") if @already_in_an_inputs_block
- form_buffers.last << html
end
|
I'd prefer more complex markup to more complex behavior. Most of the complexity of this ticket had to do with formtastic changing behavior based on context. If it were a voting matter, I'd vote for minimizing context sensitive behavior rather than depending more on it. Any feedback on the additional commits on They are rooted at the tip of @shekibobo PR so should apply cleanly |
Hey, guys, sorry, I've been doing a weekend hackathon all weekend, so my coding brain is kinda fried right now. @seanlinsley Yeah, if we delete the @igorbernstein as I said, my brain is kind of fried. Can you summarize what your branch does differently or optimizes? I believe you can also submit a pull request directly to my branch if you want to do it that way. |
My commits are based on this PR and add the following:
I'll create another fork from your repo and repackage them as a PR |
For some reason your active_admin fork does not appear as an option in "Choose a Base Repository" so I can't send you a PR. You can pull in my changes using rebase git remote add igor https://github.com/igorbernstein/active_admin.git
git fetch igor
git checkout 2659-rewrite-has-many-form-structure
git rebase --onto 2659-rewrite-has-many-form-structure 2659-rewrite-has-many-form-structure igor/2659-rewrite-has-many-form-structure (I think that should work) |
@igorbernstein while 41ab134 makes sense, I don't seem to understand your other changes.
But you're putting it inside a
Doesn't the normal one still get built? |
The first 2 changes don't actually do anything other than shift around code to make the rest of the changes more apparent. The structure introduced in bed8f24 "wrap the fieldset in a ol" is 9aca4ae - "generate hidden inputs manually to keep markup consistent" if has_many_form.object.persisted? && has_many_form.options.fetch(:include_id, true) && !has_many_form.emitted_hidden_id?
has_many_form.input :id, as: :hidden
end edit: copy/pasted the check for hidden field |
side note: the logic for the hidden input check comes from the link I posted earlier in the discussion: |
👎 on adding an Though it makes the form code more complex, 👍 on the hidden ID change because it makes the HTML clearer. I said it earlier, but to have a compact list 👍 on 41ab134 as we should have already been doing that. |
@shekibobo if you can update the failing tests so the no longer depend on the CSS |
Should I remove the |
Oh, sure if you want. I was mostly just saying that as I assumed you were busy :] There are now quite a few commits in this PR that are just updating earlier changes. Would you mind squashing some of the more obvious ones? |
Yeah, no problem. |
Squashed. Hoping these tests pass. If they don't I'm going to take a look and fix them in the morning. |
Yeah, it looks like some of the unit tests aren't quite accurate. Anyway, til tomorrow! |
@@ -22,13 +22,13 @@ $ -> | |||
# e.preventDefault() | |||
# | |||
# # The after hook is a good place to initialize JS plugins and the like. | |||
# $(document).on 'has_many_add:after', '.has_many', (e, fieldset)-> | |||
# $(document).on 'has_many_add:after', '.has_many_container', (e, fieldset)-> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you missed the code example right above this one :)
[skip ci]
💚 💚 💚 💚 |
🐙 |
…ructure Rewrite has_many form structure
Yay, I can move on with my life! 😃 |
Potential solution for #2659.
I'm not sure if the important parts of this PR (cbd141d) are that much different from @igorbernstein's work in #2675, but it does manage to render a valid html structure.
On weird thing is, however, that the hidden
id
field forhas_many
get rendered as siblings to thefieldset
that contains the other fields related to it, and I have no idea why.Aside from this anomaly, this seems to work okay, solving both #2655 and #2659, although not exactly to the spec I proposed there. And apparently we can even support nesting
inputs > has_many
without generating invalid html. Again, not really sure why mine works and Igor's doesn't, but hey, there it is.