In 2011 a new programming language appeared with a simple focus in mind, Making a programming language concurrent, elegant, easy, blasting fast, and powered by one of the monsters ever built, Erlang.
Elixir is a functional language built on top of Erlang, providing a delightful syntax pretty similar to Ruby, it is designed to scale tons of thousands of threads concurrently and enabling communication between them via messages. Elixir and erlang also promote a really nice expression things will go wrong
and this is because it is designed to fail, offering a lot of mechanisms such as supervisors restarting particular parts or threads of the system instead of a complete explosion.
Phoenix is for Elixir what Rails for Ruby. An amazingly fast web framework for building web applications without compromising scalability or maintainability.
Taking advantage of Phoenix speed, channels, and HTTP 2.0 implementation Phoenix's builders came out with a cool library for Phoenix called LiveView
which provides a really cool way to make reactive views with 0 Javascript and just Elixir.
Having in mind that you already installed NodeJS in your computer, we first want to have Elixir installed
brew install elixir
Mix
is a build tool that ships with Elixir that provides tasks for creating, compiling, testing your application, managing its dependencies and much more.
Now that we have it installed let's install Phoenix
mix archive.install hex phx_new 1.4.0
Now we are all set to create our new Phoenix app
mix phx.new reactivity
A lot of things are going to start happening so when mix asks us to Fetch and install dependencies?
just type y
.
Now let's set up our database and start our server
cd reactivitymix ecto.createmix phx.server
And know in our browser we can go to localhost:4000
LiveView
First, let's open our mix.exs
file and add LiveView as a dependency and then run mix deps.get
def deps do[...{:phoenix_live_view, "~> 0.10.0"},{:floki, ">= 0.0.0", only: :test}]end
After getting all the dependencies, let's restart our server by pressing ctr+c
twice and then mix phx.server
Now let's open config/confix.exs
and add out endpoint configuration, you can generate your own salt by running mix phx.gen.secret 32
config :reactivity, ReactivityWeb.Endpoint,kj...live_view: [signing_salt: "YOUR_SALT_GOES_HERE"]
Now we need to configure our browser pipeline
# lib/my_app_web/router.eximport Phoenix.LiveView.Routerpipeline :browser do...plug :fetch_session- plug :fetch_flash+ plug :fetch_live_flashend
Then add the following imports to our web file in lib/reactivity_web.ex:
# lib/reactivity_web.exdef controller doquote do...import Phoenix.LiveView.Controllerendenddef view doquote do...import Phoenix.LiveView.Helpersendenddef router doquote do...import Phoenix.LiveView.Routerendend
Then we will configure our endpoint
# lib/reactivity_web/endpoint.exdefmodule ReactivityWeb.Endpoint douse Phoenix.Endpoint# ...socket "/live", Phoenix.LiveView.Socket,websocket: [connect_info: [session: @session_options]]# ...end
Now let's add LiveView JavaScript dependency
{"dependencies": {"phoenix": "file:../deps/phoenix","phoenix_html": "file:../deps/phoenix_html","phoenix_live_view": "file:../deps/phoenix_live_view"}}
And let's install it
npm install --prefix assets
Not let's enable the socket communication
// assets/js/app.jsimport {Socket} from "phoenix"import LiveSocket from "phoenix_live_view"let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content");let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}});liveSocket.connect()
And finally and optionally you can add the default css
/* assets/css/app.css */@import "../../deps/phoenix_live_view/assets/css/live_view.css";
Perfect! We now can restart our server a check if everything is ok!
Now that we have everything set up, we can start by writing our first Controller, which will have the specific action for our route and will render our LiveView
!
Let's go ahead and create a new file under /lib/reactivity_web/controllers/
called to_do_controller.ex
and add the following
defmodule ReactivityWeb.ToDoController douse ReactivityWeb, :controllerdef index(conn, _params) dolive_render(conn, ReactivityWeb.ToDoView)endend
Then add the route at lib/reactivity_web/router.ex
scope "/", ReactivityWeb dopipe_through :browserget "/", PageController, :indexget "/todo", ToDoController, :indexend
Now add a new folder undex /lib/reactivity_web/
called live
and a file inside it called to_do_view.ex
with the next content
defmodule ReactivityWeb.ToDoView douse Phoenix.LiveView# Method that returns the HTML code that will be rendereddef render(assigns) do~L"""<div><span> <%= format_date(@time) %></span></div>"""end# This is the first function that gets called once the live view is loadeddef mount(_session, _, socket) do# Check if the socket is correctly conntect we send a tick in the server and falls in handle_info(:tick, process)if connected?(socket), do: Process.send_after(self(), :tick, 1000)# Here we assign all the variables needed for our view{:ok, assign(socket, time: :calendar.local_time())}end# This function receives the tick event and the socket that triggered the eventdef handle_info(:tick, socket) do# So we send another tick after 1 secondProcess.send_after(self(), :tick, 1000)# And reply to our view with the local time, which ends up in rendering the view again{:noreply, assign(socket, time: :calendar.local_time())}end# Helper to format the date into a readable Hourdef format_date(date) do{_, {h, m, s}} = date"#{h}:#{m}:#{s}"endend
And now, if we go to localhost:4000/todo
we'll see
And as you can see, the view is being rendered without making any kind of event from the browser, the server knows that every second a :tick
event is being triggered and the handle_info(:tick, socket)
function handles this event and updates the state
or assigns
making the view render again.
Now let's add some more code in order to build a simple To-Do list
First, we'll add a new variable called todos
def mount(_session, _, socket) doif connected?(socket), do: Process.send_after(self(), :tick, 1000){:ok, assign(socket, time: :calendar.local_time(), todos: [])}end
Now add a form and a loop for rendering the tasks in our To-Do list. Here the form will an option called phx_submit
and a value of :save
this means that whenever the form is submitted LiveView
will trigger an event called :save
with the data in the form.
def render(assigns) do~L"""<div><span> <%= format_date(@time) %></span><%= f = form_for :todo, "#", [phx_submit: :save] %><%= label f, :task %><%= text_input f, :task %><%= submit "Save" %></form><ul><%= for task <- @todos do %><li><%= task %></li><% end %></ul></div>"""end
Now to handle that event let's add a new handle_event
function
#This function will handle the save event on submit formdef handle_event("save", %{"todo" => %{"task" => task}}, socket) do# And we will add the new task to out tasks array{:noreply, update(socket, :todos, &([task |&1]))}end
And now in the browser, you will see
No JavaScript needed, and a whole world of opportunities around Elixir, Phoenix and this amazing library LiveView, There's a ton of things you can build with this and you don't need a lot of languages and stuff trying to interact each other. Elixir has been growing along with its community, so I think it's the best time to start learning it a building amazing stuff with it.