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.

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.

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 made it extremely easy to implement nesting in reform).

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
#=> <0x999#Artist name: "Paul Gilbert" gender: "m">

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.

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.

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

From Here.

The new nesting feature was requested by many users and we’re really happy to release this version of reform. There will surely be issues with certain use cases and we can’t wait for your feedback!

One Response to “Reform 0.2 Released – With has_one And has_many Support!”

  1. 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 :)

Leave a Reply