{{{
“Reform”:https://github.com/apotonick/reform gives you a new abstraction layer for handling forms without hard-wiring them to your database. It just went 0.2 bringing you nesting to easily create forms for @has_one@ and @has_many@ relationships.
h3. Composition Forms.
In earlier versions, reform could automatically build a composition object to handle forms for multiple, unrelated objects.
class SongForm < Reform::Form include Reform::Form::Composition property :title, on: :song property :written_by, on: :artist end
This still works, however, the @DSL@ module got replaced by @Composition@, which you must include to make reform understand what this @on:@ option is about.
SongForm.new( song: Song.find(1), artist: Artist.find(2))
When creating a composite form you still need to pass in the separate objects using a hash.
h3. One-To-One Relationships.
Technically, every kind of model relations could be handled with this @Composition@ trick. Nevertheless, the new reform makes it super easy to compose forms of multiple associated models.
Say we had the following database configuration.
class Song < ActiveRecord::Base has_one :artist end
A classic 1-to-1 association! Yay!
Although I’m using @ActiveRecord@ to demonstrate reform’s new goodies, it is important to understand that this gem *doesn’t speak a single word of @ActiveRecord@* – it uses public readers and writers, only.
To create a form to handle fields for both @Song@ and @Artist@ you can now define _nested_ forms.
class SongWithArtistForm < Reform::Form property :title property :artist do property :name property :gender validates :name, presence: true end end
See how you can now pass a block to @property@ and simply create another form class inline? Awesome, isn’t it?
(Tech note: the new inline representer feature in “representable 1.6”:https://github.com/apotonick/representable/#inline-representers made it extremely easy to implement nesting in reform).
h3. Render The Association Form.
Now, check out how this form is instantiated.
@form = SongWithArtistForm.new(Song.find(1))
As you’re _not_ using the composition feature, all you do is pass in a single model.
song = Song.find(1) song.artist #=>
Since you have a nested setup, this model is required to respond to @#artist@, which in turn must expose readers for @name@ and @gender@.
That should save you some work when creating the form.
Even cooler: rendering the form using Rails’ (nested) form helpers now works out-of-the-box – without inheriting all the flaws from @accepts_nested_attributes@ code.
= form_for @form do |f| = f.text_field :title = fields_for :artist do |a| = a.text_field :name = a.text_field :gender
This just works, so you don’t have to worry about rendering the proper markup – the most annoying part when writing forms in my opinion.
h3. Validating And Processing.
All you need to do now is passing the submitted data to reform.
@form.validate(params[:song])
This will run all validations from the form, even the nested one from the artist form.
Error messages – in case of bull data – can be rendered using the common steps.
- @form.errors.full_messages.each do |msg| %li = msg
Using the block-less @#save@ will push submitted and validated data to all objects automatically.
@form.save #=> @form.song.title = "Beachparty" # @form.song.artist.name = "No Fun At All"
As before, you can do the saving manually: @#save@ will yield the nested input.
@form.save do |data, hash| data.artist.name #=> "No Fun At All" hash[:artist][:name] #=> "No Fun At All" Artist.create(hash[:artist]) end
Here, it’s up to you how to process the nested data. Reform just makes sure things are correctly nested.
h3. One-To-Many Relationships.
You thought that’s it? No way, we also got support for nested collections.
class Album < ActiveRecord::Base has_many :songs end
Mapping this association in your form is pretty straight-forward.
class AlbumForm < Reform::Form property :name collection :songs do property :title end end
Creating the form works just like the @has_one@ example.
@form = AlbumForm.new(Album.new( songs: [Song.new, Song.new] )
Here, it is important the @Album#songs@ returns a _collection_ of objects.
Rendering, validating and displaying errors works likewise.
You can use @fields_for@ and Rails will render the form collection. You could also go manually through the collection.
= @form.songs.each_with_index do |f, i| = text_field_tag "title_#{i}" ..
Currently, it’s your job to keep the number of forms visible on the page in sync with the forms created internally. That is why I pass in two new @Song@ instances to @Album@, as this will make two nested song forms appear after rendering.
If this feels inconvenient, we’re open for suggestions.
When saving sane data, you get a collection of data for the song forms.
@form.save do |data, hash| data.songs[0].title #=> "Sanity" hash[:artist][:songs][0][:title] #=> "Sanity" end
h3. From Here.
The new nesting feature was “requested by”:https://github.com/apotonick/reform/issues/6 “many users”:https://github.com/apotonick/reform/issues/26 and we’re really happy to release “this version of reform”:https://github.com/apotonick/reform. There will surely be issues with certain use cases and we can’t wait for your feedback!
}}}
Hey Nick, thanks A LOT for working on it, it’s awesome to see this in place. If you ever come to Brazil again let me know and I’ll buy you a beer 🙂
LikeLike