Phoenix forms without changesets!

Typically, we back Phoenix forms with an Ecto changeset.

But sometimes, I prefer having more of a separation between the form, which is part of my UI, and the changeset that stores data in the database.

If we use the same changeset, it feels like our UI knows too much about our persistence logic.

One way to split that is to use Phoenix’s new to_form/2 helper (and protocol).

The protocol is already implemented for changesets, so you can continue using the changeset in a form. For example, it might look something like this:

changeset = Blog.new_post()
assign(socket, form: to_form(changeset))

But the to_form/2 protocol is also implemented for regular maps. So, we can back our forms with a simple map!

fields = %{"title" => "", "body" => ""}
assign(socket, form: to_form(fields))

Handling errors

But one of the cool things about the changeset integration with Phoenix forms is how they automatically handle errors.

Thankfully, the to_form/2 helper has a nice way of dealing with errors.

It takes a set of options, one of which is an errors key. If we pass a map of errors (field name to error), then it’ll work exactly like changesets work! 🥳

So, we could do something like this:

fields = %{"title" => "", "body" => ""}
errors = [title: "Can't be blank"]
assign(socket, form: to_form(fields, error: errors))

That’s hardcoded, but you can get those errors from your domain modules (and in the video, it’s actually the value returned by changeset.errors).

Pretty cool, right?

Want the latest Elixir Streams in your inbox?

    No spam. Unsubscribe any time.