Form Objects

Joe and Gabe review form objects, and explain why they can almost always replace accepts_nested_attributes_for. See an example Rails app written with nested attributes and how and why we recommend switching to form objects. Nested Attributes doc...
This is a companion discussion topic for the original entry at https://thoughtbot.com/upcase/videos/form_objects
1 Like

Nice episode! Would it be possible to see a gist of fully production-ready implementation (with the transactions @jferris mentioned)? Thanks!

3 Likes

If anyone is interested in a way to promote errors from component models to your Form model, here’s one way: thredded/topic_form.rb at main · thredded/thredded · GitHub

1 Like

I would like to see an example with tests as well.

How would I implement a multi-step form / wizard using form objects? How does Thoughtbot handle those features?

What are peoples thoughts on this presentation from rails conf 2014?

1 Like

Great episode. How would you handle has_many associations with form objects?

If the user had many companies for example.

Hey guys, thanks for sharing the ‘form object’ technique, very cool! You guys mentioned that the form object can cause problems when you need validations like ‘validates uniqueness of’ because the form object doesn’t use active record. However, it didn’t sound like you posed a solution for that issue, which I imagine would be a common problem. Do you mind positing a solution on the forum? Thanks!

I happen to maintain an open source app that basically has this exact scenario, we’re creating a User and an Account at the same time, and it was using accepts_nested_attributes_for. I’ve opened a PR on our app with a bit more full fledged example:

It includes (pretty basic) specs and shows how to promote child errors, allowing you to validate things like uniqueness etc.

Although I do kind of like this approach, it is quite a lot more code (~50 lines without tests)

1 Like

I’ve used the wicked gem in production for a post-sign up multi-step form process. Works well.

Nice episode.
But I am not quite sure how many code to put in the save part of the form object.
I have a form that takes a lot of configuration options, the availability of which depending on the permissions of the current user. Upon saving I need to update several models and add notifications to a queue to be picked up by a remote process. I currently use a service to do the updating, but that feels kind of awkward.

How would you go about the combination of the permissions part and the notifications part?

Thanks. Herman

I had a similar question a couple of months ago. If you like, you can add the reform gem, which allows for creating form objects with a DSL for validations, 1-n forms, etc. If you’re just interested for the sake of learning, take a look under the hood of reform to see how has_many is handled.

Hello, this was indeed a very nice episode! I liked it very much, please do more rails intermediate episodes like this one.

One thought: isn’t this approach of object form similar to the concept of View Model, which can be known for example in mobile development? I am a beginner in it, but it just seemed to me like a similar concept - to have a object which is only processing the view data and then sending those to the appropriate models to be saved.

One aspect of form objects than can trip people up not mentioned:

if you do create a resource for your form object, which is advised in the episode - switching from UsersController to SignupsController - and set it as singular resource ( resource :signup, only: [:new, :create] to keep the example provided), you do have to pass the url to the form.

Strictly speaking, this isn’t unique to form objects, but to any form for a singular resource in Rails. There is an open issue on the Rails repo regarding this.

1 Like

This is actually used as a form object in the Upcase app: https://github.com/thoughtbot/upcase/blob/master/app/models/checkout.rb

@benorenstein That link 404’s for me.

If you’re on our $29/month plan, head here for repo access: https://thoughtbot.com/upcase/repositories.

I wish this episode went into more details in implementation such as if user had many companies, or the user has a uniqueness validation on the email etc. This was a way too simple example

1 Like

I like your episodes, but is there any way you can show the code for longer and not switch to you two talking so quickly. Not that you’re not beautiful. : )

Great video, I will like to know how can I edit and update, any ideas or examples?

Sorry for replying much later than your original post. Not sure if you’ve already solved this but the way I see it you have a few approaches:

  1. Leave the validation in the models and populate the errors up into the form object as @aaronmcadam mentioned.

  2. Looking specifically at Thoughtbot’s post on uniquness validations you could have a unique index on the field which would raise an ActiveRecord::RecordNotUnique error which you could then rescue in the save method

    def save
    if valid?
    # Other code
    end
    rescue ActiveRecord::RecordNotUnique
    errors.add(:user_email, “is not unique”)
    end

The main issue with this approach being that it’ll only appear if everything else is valid.

  1. Have a custom validation method for it, something like

    validate :unique_user_email

    def unique_user_email
    if User.exists?(email: user_email)
    errors.add(:user_email, “is not unique”)
    end
    end

If you wanted to check it on editing you’d need to check if it’s persisted and include something to ensure you check any records where the email isn’t user_email and the id isn’t the user’s id

I think the best solution then would be either using 1 or a mixture of 2 and 3 really.

@christoomey Hi Chris! Quick question about this topic if you don’t mind. These Form Objects seem a lot like Presenters. Since the terminology on these is still a bit fuzzy to me, I was wondering if they really are one and the same in such a case?