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.

Specifications

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

Structure

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:

data-list="new-list-container", data-list="list-index-container" or data-todo="todos-container"

Those containers will be soon used to render dynamic lists and todos.

JavaScript

The JS side will take the role of Views and Templates.

web/static/js/
app.js
sockets.js
views/
list.js
todo.js
actions/
list_actions.js
todo_actions.js
templates/
list/ (hbs templates)
todo/ (hbs templates)

app.js imports List from the views and initialize it with the socket List.init(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:

./web/static/js/views/list.js

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:

./web/static/js/actions/list_actions.js

The newListTemplate is actually imported hbs template:
import newListTemplate from "../templates/list/new_list.hbs"
The CRUD actions are organized in Push / Receive pairs.

Channels

Channels use pattern matching to map the events sent by the front-end. This reflects the same CRUD actions as the JS above.

./web/channels/list_channel.ex

A Phoenix view renders the list JSON:

./web/views/list_view.ex

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.

Conclusion

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.

What’s next?

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.

--

--

--

elixir dev | dorian.iacobescu@gmail.com | @iac0bs0n

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

10 Basic Concepts of React

How to convert less to styled components

JavaScript: What is Hoisting

What’s the difference between a “Thenable” and a “Promise”?

Easy Pagination with Vapor 4

Learning AMP: Responsive AMP Carousel — AMPire.city

JavaScript Destructuring

How to Pass the Data From the Table to the Modal (VueJs)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
iacobson

iacobson

elixir dev | dorian.iacobescu@gmail.com | @iac0bs0n

More from Medium

How Node.js performance depends on available CPU count

Part #2: Memory Management in WASM

WebSocket development with Typescript from scratch

Repository pattern in Nest.js