In this guide I present how to quickly make an HTML resource come alive using abstraction offered by Phoenix.LiveController.
Recently I've finally managed to give a serious spin to the Phoenix.LiveView library. As a born pragmatist I fell in love with its concept, so I've been following its course all the way since the announcement, through the exciting Phoenix Frenzy contest and ending with recent changes that may indicate that the library starts approaching maturity. On the other side, the ecosystem around it is only starting to form with a lot of open questions concerning even the most basic & most obvious topics, e.g. authentication.
So I've started experimenting. And as back in the old days I was interested in building web apps that are fully reactive across client & server (such as those built with Meteor framework which didn't buy me after all), I took the all-or-nothing approach in which entire application, including navigation, is run on live views. Why? Well, for a couple of reasons:
Of course, that last concern - the overhead - highly depends on maturity of the library as well as on maturity of the entire ecosystem - including libraries, resolved issues and learning materials. And it's this concern that I'm trying to support with this article.
What I've discovered is that this kind of all-in approach, although already well supported by the library itself, is barely present in the ecosystem. I was looking for a clear path for converting my HTTP controllers, such as those coming from mix phx.gen.html
, into live views, but most of the available guides focused on more specific cases for live views. Still, along with well-written LiveView docs, they were enough to teach me how to get there myself.
Then, looking at my code, I've noticed a lot of redundancy and unnecessary scattering of my live view equivalents of good ol' controllers. I felt like I'm lacking an abstraction for this kind of application, so I've created Phoenix.LiveController. Please take a look at its docs for a thorough explanation and examples before carrying on in order to grasp the idea behind steps below.
Using LiveController allowed me to make my live views more compact, easier to approach and just faster to code. Perhaps this abstraction will fill the gap for organizing the multi-action live view code, bringing more developers onboard this exciting ship.
So, without further delays, let's prepare the app and give LiveController a spin.
If you're starting from scratch or haven't configured Phoenix.LiveView in your project yet, then this section will get you started. But rather than repeating official guides, I'll point you at appropriate places in docs and briefly list the required steps.
Update: With a recent release of Phoenix 1.5.0-rc.0, you can now setup a new Phoenix project including LiveView and live layout by passing the --live option to mix phx.new, making this procedure unnecessary for a fresh project. It's still viable for existing 1.4 or 1.5 projects.
All steps are backed by commits & diffs from the example project based on following libs:
We'll create an app using mix phx.new task (commit):
mix phx.new my_app
cd my_app
mix ecto.create
mix phx.server
We'll generate an HTML resource using mix phx.gen.html task (commit).
mix phx.gen.html Blog Article articles title
mix ecto.migrate
MyAppWeb.Router
(diff).We'll configure Phoenix.LiveView as described in Phoenix.LiveView installation guide (commit).
We'll set up layout to support both live views and regular HTML controllers as described in Layouts section of Phoenix.LiveView installation guide (commit).
MyAppWeb
(diff).MyAppWeb.Router
(diff).Phew, now that we're done with preparations we have following prerequisites in place:
Let's see what it takes to convert that HTML resource to multi-action live view (commit).
Remove old resources call and add live equivalents of previous GET routes (diff).
Update: In phoenix_live_controller v0.3.0,
@
action_mount
annotation is renamed to@action_handler
. It still behaves the same for the exercise below.
Live controller code will be very similar to HTTP controller, so it makes sense to start by recycling it. Following Linux/Mac shell commands will put the file in a right place:
mkdir lib/my_app_web/live mv lib/my_app_web/controllers/article_controller.ex \ lib/my_app_web/live/article_live.ex
Then, make following changes in the live controller code:
@action_mount true
(new).@event_handler true
(new).Start off by changing extensions of all templates in lib/my_app_web/templates/article from .eex to .leex. Following Linux/Mac shell commands will do the trick:
cd lib/my_app_web/templates/article for f in *.eex; do mv -- "$f" "${f%.eex}.leex"; done
Then, make following changes in all templates:
I haven't covered that part yet. At the time of writing this article, phoenix_live_view v0.12 is still under development with planned overhaul of the testing module, so you're best off referencing current docs for Phoenix.LiveViewTest for up-to-date testing instructions.
Now that you're done, the entire resource will run navigation without any requests while still being crawlable and ready for any JS-free live additions. If you carry on, you may end up converting all routes into live views, making the result complete.
It may still seem like a lot of work, but I hope that this step-by-step diff-backed approach towards implementing live controllers for resources will be just as useful to you as it was for me. That it'll paint a clear logical picture of similarities and differences between the old & the new. And that once you dare to clear that path, you'll efficiently carry on with more and more of live view code, contributing into making the LiveView ecosystem rich & packed.
P.S. I'm currently in the process of adopting the new authentication solution by José Valim for this approach with a great progress so far, so stay tuned for the results.