Skip to main content

Subscribe to GoRails to get access to this episode and all other pro episodes, and new awesome content every month.

Subscribe Now
Only $19/month

Unlimited access. Cancel anytime.

31 Form Objects Design Pattern

Episode 179 · March 28, 2017

See how you can use a form object to improve the way you implement custom and complex forms in Rails

Design Patterns


Transcripts

Subscribe or login to view the transcript for this episode.

Discussion


Gravatar
Steven Hansen on

Nice episode, I've been using forms like this all over the place, especially when an AR object can be saved via many different use cases with many different "valid" states. One quibble, seem more clear to do something like:

if @contact_form.valid?
ContactMailer.message(params).deliver_later
redirect_to ...
end

especially since the contact form is not really saving anything.


Gravatar
vcavallo on

as far as ideas for further examples: in the realm of interesting forms I'd be curious to see your approach to a multi-step, "Wizard"-style experience where multiple forms are filled out for different records (with or without branching paths depending on earlier data :) )

Gravatar
Nick on

Yep definitely relevant, the old wizard where you need to progressively build up multiple associated models across a few steps.

I know this can be done "big-bang" style using Javascript, but then risks the user losing some of the data if it isn't written back/progressively validated at each logical step.

An example for the work we do, in our case, we deal with the provision of a lot of information based products, which require the user to enter a lot of their information in, and then they finally submit the "form" at the end, but it might have been 5 - 6 steps/pages updating/creating multiple models.

Currently, we've used Wicked Wizard gem to hide a lot of the mess away from non-RESTfully updating a model (or multiple).

Would be great to get some more insights as to how a Form Object could be applied in the context of a wizard. Currently, we're using a liberal application of nested_attributes_for, but it's quite brittle, and particularly, requires a lot of condition validation for each step that saves back to the model (because they are all pretty much only ever partially valid at each step until the final step is saved)..

An example: say our primary model is an CarInsuranceQuote, and associated to this CarInsuranceQuote model would Addresses, Names, Cars.

Form Step 1: Car Details & Name of Insured (creates a new CarInsuranceQuote, and build associated Name model on the quote, eg CarInsuranceQuote.cars.new(car_attrs), CarInsuranceQuote.names.create(names_attrs).

Form Step 2: Address Details for Quote,
ie: CarInsuranceQuote.addresses.create(address_attrs). And maybe save a few other attributes on the CarInsuranceQuote.update_attributes(ciq_attrs).

Form Step 3: Finalise Quote by Creating a User for the quote
ie: CarInsuranceQuote.users.create(user_attrs).

As you can see, each step would build different associations, but might also partially update the parent model in the Wizard.

Gravatar
vcavallo on

In a project I'm currently working on I have 4 or 5 full page loads that each hold a form (some of which have a single group of nested attributes). I'm currently just handling each separately, building the models, validating, etc. before prompting the user to move on to the next page/form. It works fine tech-wise, but the user experience (especially "nowadays") is pretty crappy. as a result I'm currently changing it over to javascript and ajax. I think having it set up as separate views and controllers now will help that transition since the only real difference is making the form posts asynchronously and collapsing the forms into a javascript/css navigation.
it's a bit of a pain in the ass (as a developer who tries to avoid javascript for his own dumb reasons) but I think the user-facing result will be well worth it. plus the change over to ajax is a good headstart to building an API that will eventually support a native mobile client.

Gravatar
Dan Mitchell (120 XP) on

I was also going to suggest this - https://github.com/nerdcave...

Gravatar
vcavallo on

Am I right in understanding that that works only for wizards dealing with _single models_, broken into steps?

Gravatar
Dan Mitchell (120 XP) on

This repo is an updated version of Ryan Bates screencast for Rails 5. I've used it on a project and it's great but the model had no associations so I'm not sure if it would work for nested resources

Gravatar
Alexander Popov (230 XP) on

Wow, I am struggling with multi-steps forms as well. I definitely would support the creation of an episode for multi-step forms!

Gravatar
vcavallo on

I ended up doing the whole thing in jquery/ajax and i'm not thrilled about it :|

Gravatar
Alex Popov on

Well, that doesn't solve the problem, unless you are saving the values from all steps in the client and making a single request. If you are making several requests, you are faced with the same issues, even with Ajax


Gravatar
Sam Van Damme (1,230 XP) on

Awesome episode! Building something like this in an existing project as we speak (or as I type) :)


Gravatar
victor hazbun (330 XP) on

how do you handle Active Record Callbacks in form objects design patterns?


Gravatar
Владислав Коваленко on

Hey, Chris!) 10Q for awesome episode=) What do you think about "Interactor Pattern"? It could be very interesting and useful for community to build it from scratch and use 'interactor' gem for organizers!) Something like this:
https://mkdev.me/en/posts/a...
Thank You for attention)
Piece=)


Login or create an account to join the conversation.