Last active
June 26, 2021 23:03
-
-
Save lucaspiller/615f09bb525a14163921fd56b4b8e611 to your computer and use it in GitHub Desktop.
Reform 2.2 nested form (with support for cocoon gem)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
= f.simple_fields_for :inputs do |input| | |
= render 'input_fields', f: input | |
.links | |
= link_to_add_association f, :inputs, partial: 'input_fields', force_non_association_create: true do | |
Add |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.nested-fields | |
= f.input :foo, as: :string | |
= f.input :bar, as: :string | |
= f.hidden_field :_destroy | |
= f.hidden_field :id | |
= link_to_remove_association f do | |
Delete |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class IndicatorForm < Reform::Form | |
InputPrepopulator = -> (options) { | |
if inputs.size == 0 | |
inputs << IndicatorInput.new | |
end | |
} | |
InputPopulator = -> (options) { | |
fragment, collection, index = options[:fragment], options[:model], options[:index] | |
if fragment["_destroy"] == "1" | |
# Marked for removal | |
collection.delete_at(index) | |
return skip! | |
else | |
(item = collection[index]) ? item : collection.insert(index, IndicatorInput.new). | |
end | |
} | |
collection :inputs, inherit: true, prepopulator: InputPrepopulator, populator: InputPopulator do | |
include NestedForm | |
property :foo | |
property :bar | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module NestedForm | |
extend ActiveSupport::Concern | |
included do | |
property :id, writeable: false | |
property :_destroy, virtual: true | |
end | |
def new_record? | |
model.new_record? | |
end | |
def marked_for_destruction? | |
model.marked_for_destruction? | |
end | |
end |
This is really helpful, link_to_add_association
raises undefined method
reflect_on_association' for NilClass:Class`
in order to solve it
class IndicatorForm < Reform::Form
...
def self.reflect_on_association(obj)
Indicator.reflect_on_association(obj)
end
end
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I found the above very useful.
It may help others to share an issue I came across (using Cocoon and Rails with fields_for) if you have more than one level of nesting. That is, if within the _input_fields partial you want another link_to_add_association for a further has_many association.
The problem (here: https://github.com/nathanvda/cocoon/blob/master/lib/cocoon/view_helpers.rb#L95) is that cocoon bases the nested fields for an associated object on a pure instance of the model, rather than that model wrapped in the Reform form object. (Not a criticism, just the way it's designed.) That's fine until you want to render fields for an association on that nested object, because the setup for the nested attributes is defined in your Reform form object, not your model. Essentially
f.object
in_input_fields
is the actual model, not the form object. Unless the model itself has accept_nested_attributes configured,fields_for
won't treat the attribute as anested_attributes_association
and the html markup won't name the fields in such a way that reform-rails can parse them into properly nested parameters.The solution for me was using
wrap_object
to ensure the record cocoon uses is the model wrapped in the relevant form object, like this:(There's probably a better way to get the form but this gives the idea).