How to (Part 1): Swap Registration Flow to a Live View With phx_gen_auth

Mark
The Startup
Published in
5 min readDec 5, 2020
Metamorphic logo © Moss Piglet. All rights reserved.

Background

I’ve been working on a new web app for connecting with people socially using Elixir and Phoenix, and adding a live view flow to the registration experience for people was something high on my to do list.

**UPDATE — 1/14/2021 **

Nicole and Mike over at Pragmatic Bookshelf have released their early access Phoenix Live View course, the paid Pro course covers authentication (also with phx_gen_auth). I highly recommend you check it out for a more in-depth understanding and tutorial.

** END UPDATE — 1/14/2021 **

So, I spent some time looking at Chris McCord’s phoenix_live_view_example repo and managed to successfully swap over phx_gen_auth’s standard registration page for a live view one.

Prerequisites

For this quick tutorial, we’ll assume the following:

  • You have a working Elixir/Phoenix app setup with the live generator (or add the appropriate code — see the docs).
  • You have installed and setup phx_gen_auth (version 0.6.0) using mix phx.gen.auth Accounts Person people (you can substitute other names, I just prefer to treat people of our upcoming web app as people).
  • Optional: You have setup email confirmation with Bamboo.

Let’s Begin

Let’s dive right in.

Step 1

Update your person_registration template to a live view one. So, change new.html.eex to new.html.leex and then update the following parts (I’ve left off details for brevity’s sake, you’ll have to compare with your code and adjust accordingly):

Note: You will also want to update any conn code to socket in the template as the live view template uses the socket in place of the connection used in regular templates.

# your_app_web/templates/person_registration/new.html.leex<%= f = form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, phx_hook: "SavedForm"] %>  <%= email_input f, :email, required: true, aria_label: "Email address", type: "email", phx_debounce: "blur" %>  <%= password_input f, :password, value: input_value(f, :password), required: true, aria_label: "Password", type: "password", phx_debounce: "blur" %>  <%= password_input f, :password_confirmation, value: input_value(f, :password_confirmation), required: true, aria_label: "Password confirmation", type: "password", phx_debounce: "blur" %>  <%= submit "Register", phx_disable_with: "Registering...", %></form>

Phoenix provides really wonderful live view bindings, and I definitely recommend you explore them further in the bindings documentation. But, for us, the key parts to takeaway:

  • <%= f = form_for @changeset, “#”, [phx_change: :validate, phx_submit: :save, phx_hook: “SavedForm”] %>

For this example, you don’t need the phx_hook: “SavedForm”. But, that shows an example of how you would start to go about implementing custom client-side JavaScript. You can learn more by reading the JavaScript interoperability documentation.

  • phx_debounce: “blur”

This is a rate-limiter on the client end (along with phx_throttle). In terms of the user experience (UX), this will make the live validation not populate until the field is blurred by the person, i.e. clicks off the input field. Remove this if you want the immediate feedback and are not worried about rate-limiting (or, for example from the documentation, set it to “2000” to have it validate, at most, every 2 seconds). Here is the documentation for further reading.

  • value: input_value(f, :password)
  • value: input_value(f, “password_confirmation)

**UPDATE — 3/17/2021 **

Here’s the password input documentation. I’ve finally found clarity on this (after reading it differently each read-through): by default, password fields are not reused when rendering a password input tag (that part was clear). However, that doesn’t leave you with a great experience when performing live validations in a Live View. So, the solution is to explicitly set the input_value/2 from Phoenix.HTML on any password fields in your Live Views.

** END UPDATE — 3/17/2021**

  • phx_disable_with: “Registering…”

This handles latent events. In terms of UX, this will have your submit button render a nice, informative message for the client (you can have the phx_disable_with say whatever you like). You can learn more by reading the form events documentation.

  • </form>

Since we’ve changed the HTML form code in our template, we need to remember to update the closing HTML tag from <% end %> to </form>.

Step 2

** UPDATE — 1/15/2021 **

Using the wonderful credo static analysis tool, I’ve refactored the with statement to a case statement in the GitHub Gist below (the last handle_event/3 function).

** END UPDATE — 1/15/2021**

Next, add a folder titled person_registration_live under your live directory. Within person_registration_live folder create a new file titled new.ex .

Note: There are so many ways to do things, this is just one example.

# your_app_web/live/person_registration_live/new.ex## note: your file's full path will be the following if it is a regular Elixir/Phoenix app:your_app/lib/your_app_web/live/person_registration_live/new.exAnd if you're working in an umbrella app, then it will be something like:apps/your_app_web/lib/your_app_web/live/person_registration_live/new.ex

Optional parts to consider:

  • The last def handle_event/3 function is altered for sending a confirmation email using Bamboo.
  • I highly recommend comparing with the phx_live_view_example and this Medium post if you’re struggling to implement without email confirmation.

Step 3

Then, update your routes in your router (you’ll want to comment out your existing registration routes — you can delete them later once you’re certain everything is proper and good to go for production):

# your_app_web/router.ex. . .## Authentication routesscope "/", YourAppWeb do  pipe_through [:browser, :redirect_if_person_is_authenticated]  . . .  live "/people/register", PersonRegistrationLive.New, :new  # get "/people/register", PersonRegistrationController, :new  # post "/people/register", PersonRegistrationController, :create  . . .end

Step 4

Finally, update your links throughout your application code. If you’re using VS Code on Windows/WSL, you can hit ctrl + shift + f to power up a “find all” within your project’s files and do a fast “find and replace” (see Microsoft’s Mac OS keyboard shortcuts).

# your old links will be something like:person_registration_path ## Update to your newly created routing pathsperson_registration_new_path

Key parts to takeaway:

  • Your new routing path will be person_registration_new_path.
  • Use your code editor’s find & replace tools to make the update painless.

Let’s go just a little bit further to remove the need to update existing links in our application and clean up our now-defunct your_app_web/controllers/person_registration_controller.ex.

Just a little bit further — proceed with caution

Before proceeding, you will want to double check that your registration flow works as intended. Then, I suggest renaming your person_registration_controller.ex and testing again.

Once satisfied that everything is working as it should, you can go ahead and delete your old your_app_web/controllers/person_registration_controller.ex as we’ve effectively switched over the controller functions and actions into our your_app_web/live/person_registration_live/new.ex file.

If you’d like your links to the registration page to remain as person_registration_path, then we need to update our live route in our router.ex:

# your_app_web/router.exlive "/people/register", PersonRegistrationLive.New, :new, as: :person_registration

The as: :person_registration will enable Phoenix to “sync” the person_registration_path with our new Live View file.

Now we don’t need to update any of our existing person_registration_path links in our application.

Conclusion

That’s it! You can apply the pattern from Step 1 to add additional fields. For instance, you may have a name field added to your registration_changeset.

Hope this helps you out, and please let me know if I missed something (writing this during a nap break) or if I could improve something somewhere.

At our public benefit studio, we are super excited to be working with Elixir and Phoenix to implement our upcoming web app, Metamorphic. Feel free to check us out, listen to our podcast, and support us on Patreon to learn more about our journey.

Check out Part 2 to see how we transition this into a multi-step form.

Happy holidays!

--

--

Mark
The Startup

Creator @ Metamorphic | Co-founder @ Moss Piglet