Reactive views from server and 0 JavaScript?
Tags: realtime, elixir, phoenix
Published: 2020-03-25 19:34:29

## Elixir

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

Phoenix is to Elixir what Rails is to Ruby: an amazingly fast web framework for building web applications without compromising scalability or maintainability.

By 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.

### Let's stop talking and let's write some code!

Having in mind that you already installed NodeJS in your computer, we first want to have Elixir installed 
``` sh
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
``` sh
mix archive.install hex phx_new 1.4.0
```


Now we are all set to create our new Phoenix app
``` sh
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
``` sh
cd reactivity
mix ecto.create
mix phx.server
```

And know in our browser, we can go to `localhost:4000`

![phoenix-running](https://i.imgur.com/nvzSSyY.png)

### Now we are ready to set up `LiveView`

First, let's open our `mix.exs` file and add LiveView as a dependency and then run `mix deps.get`

``` elixir
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`

``` elixir
config :reactivity, ReactivityWeb.Endpoint,
   ...
   live_view: [
     signing_salt: "YOUR_SALT_GOES_HERE"
   ]
```

Now we need to configure our browser pipeline:

```elixir
# lib/my_app_web/router.ex
import Phoenix.LiveView.Router

pipeline :browser do
  ...
  plug :fetch_session
- plug :fetch_flash
+ plug :fetch_live_flash
end
```

Then, add the following imports to our web file in `lib/reactivity_web.ex`:

```elixir
# lib/reactivity_web.ex

def controller do
  quote do
    ...
    import Phoenix.LiveView.Controller
  end
end

def view do
  quote do
    ...
    import Phoenix.LiveView.Helpers
  end
end

def router do
  quote do
    ...
    import Phoenix.LiveView.Router
  end
end
```

Then, we will configure our endpoint:

```elixir
# lib/reactivity_web/endpoint.ex

defmodule ReactivityWeb.Endpoint do
  use Phoenix.Endpoint

  # ...

  socket "/live", Phoenix.LiveView.Socket,
    websocket: [connect_info: [session: @session_options]]

  # ...
end
```

Now, let's add LiveView JavaScript dependency:

```json
{
  "dependencies": {
    "phoenix": "file:../deps/phoenix",
    "phoenix_html": "file:../deps/phoenix_html",
    "phoenix_live_view": "file:../deps/phoenix_live_view"
  }
}
```

And let's install it:

```sh
npm install --prefix assets
```

Now, let's enable the socket communication:
```js
// assets/js/app.js
import {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:
```css
/* assets/css/app.css */
@import "../../deps/phoenix_live_view/assets/css/live_view.css";
```

Perfect! We can now restart our server and check if everything is OK!

![phoenix-running](https://i.imgur.com/nvzSSyY.png)

### Controller

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:

```elixir
defmodule ReactivityWeb.ToDoController do
  use ReactivityWeb, :controller

  def index(conn, _params) do
    live_render(conn, ReactivityWeb.ToDoView)
  end
end
```

Then, add the route to `lib/reactivity_web/router.ex`

```elixir
  scope "/", ReactivityWeb do
    pipe_through :browser

    get "/", PageController, :index
    
    get "/todo", ToDoController, :index
  end
```

### LiveView

Add a new folder index `/lib/reactivity_web/` called `live` and a file inside it called `to_do_view.ex` with the next content:

```elixir
defmodule ReactivityWeb.ToDoView do
  use Phoenix.LiveView

  # Method that returns the HTML code that will be rendered
  def 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 loaded
  def mount(_session, _, socket) do
    # Check if the socket is correctly connect 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 event
  def handle_info(:tick, socket) do
    # So we send another tick after 1 second
    Process.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 Hour
  def format_date(date) do
    {_, {h, m, s}} = date
    "#{h}:#{m}:#{s}"
  end

end
```

And now, if we go to `localhost:4000/todo`, we'll see
![phoenix-running](https://media.giphy.com/media/iEw091aPYELtAS8hqX/giphy.gif)

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`:

```elixir
  def mount(_session, _, socket) do
    if connected?(socket), do: Process.send_after(self(), :tick, 1000)
    {:ok, assign(socket, time: :calendar.local_time(), todos: [])}
  end
```

Add a form and a loop for rendering the tasks in our To-Do list. Here's 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.

```elixir
  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:

```elixir
  #This function will handle the save event on submit form 
  def 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:

![phoenix-running](https://media.giphy.com/media/Y4WP18alCt9OvgNIls/giphy.gif)

### And that's it!

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 with each other. Elixir has been growing along with its community, so I think it's the best time to start learning it and build amazing stuff with it.

[And here is the full code](https://github.com/gamezcua1/reactivity)