Model View Channel ?! with Phoenix
A while ago, contributing to the Formerer project, I had my first contact with Phoenix Channels. I enjoyed it so much and thought why not to build an app that uses only Channels instead of Controllers?
This is how ChanDoThis experiment was born. Yes, just another todo list application… but a live updating one :) .The app is live on Heroku, and available here.
Just to clarify, this post is not a tutorial or step by step guide on how to build a controller-less application in Phoenix. I will focus more on the code organization and the relation between front-end and back-end of the app.
Before starting to code I defined the minimal structure and functionalities:
- a simple Todo list, with 2 models: List and Todo
- make use of Phoenix Channels instead of Controllers
- no front-end frameworks and no AJAX calls
- one-page app
- everything live updates for all users present on the app
- follow as close as possible the CRUD actions from a “normal” Controller
Phoenix Controller and Template
Let’s start with the only controller that exists in the app. It’s the
PageController with a single
index action. This generates the default Phoenix welcome page.
The page template includes also the fixed structure of lists and todos. It also has some data attributes such as:
Those containers will be soon used to render dynamic lists and todos.
The JS side will take the role of Views and Templates.
list/ (hbs templates)
todo/ (hbs templates)
app.js imports List from the views and initialize it with the socket
The view connects to the socket and creates the channel. Views have 2 roles:
- receive channel messages or user input
- send it to the action JS file
For example, this is how you create a list:
The JS actions files together with the Channels will stand in place of the classic Controller. To avoid writing HTML in the JS, I use handlebars templates (
.hbs). The role of the JS actions files is:
- push the event to the channel
- render errors if any
- render handlebar template for the specific action
Coming back to the above example, this is how the action JS file handles the creation of a new list:
newListTemplate is actually imported hbs template:
import newListTemplate from "../templates/list/new_list.hbs"
The CRUD actions are organized in Push / Receive pairs.
Channels use pattern matching to map the events sent by the front-end. This reflects the same CRUD actions as the JS above.
A Phoenix view renders the list JSON:
There are other components we did not review, such as models or lib modules, but those are not relevant in the context of this case study.
However, the full code is available on Github:
https://github.com/iacobson/chan_do_this/blob/lists-todos-final if you want to take a closer look.
To finish with the structure, below is a diagram of the system components and the interactions between them:
Test-drive the app
As mentioned in the beginning of this post, the app is hosted on Heroku: https://chandothis.herokuapp.com/
You will need 2 browser windows opened in split screen. Or, even better, open the app at the same time on 2 different devices.
Create a new list. The list should instantly appear in the other browser/device.
Create new todos, edit their names, delete or complete them. Everything should reflect at the same time in all the instances of the app.
This is a simple, straightforward project, but I think it achieved its goal. There are ways to (almost) avoid controllers in Phoenix. Channels can take their place while keeping a CRUD-like structure.
This is not something you would want to do for all your projects. But whenever you need many live updates on the page, it may be an option.
While it was so fun to work on this app, it wasn’t entirely what I expected when I started it. I expected a cool Elixir / Channels learning project and ended up mostly with a JS / jQuery one. Hunting Lists and Todos IDs around in the view proved at times to be frustrating and repetitive.
With all the good and bad parts, this can be a very good learning project. There are many things that you can build and test on top of it such as:
- add more clarity to the JS side and remove duplications
- add some form of Router
- implement a controler-less user authentication system
- use Phoenix Presence to show online users
and so much more.
Just clone the project and have fun!
Originally published at iacobson.net — October 15, 2016.