DEV Community

Bruno Luis Panuto Silva
Bruno Luis Panuto Silva

Posted on

Phoenix Views are not only for HTML

When we first hear and use Phoenix Views, our primary wiring about it is that it serves as a way to render HTML. A fast way, to be honest.

But that's not all it can do.

Rendering JSON

If you want to render JSON from a Phoenix View, it is pretty simple

defmodule Myapp.MyView do
  use MyappWeb, :view

  def render("resource.json", %{resource: resource}) do
    %{name: resource.name, value: resource.value}
  end
end
Enter fullscreen mode Exit fullscreen mode

From your controller:

defmodule Myapp.MyController do
  def show(conn, %{"id" => id}) do
    resource = MyContext.get_resource!(id)
    render(conn, "resource.json", resource: resource)
  end
end
Enter fullscreen mode Exit fullscreen mode

You'll get a JSON back from your controller. Pretty cool, right?

But Phoenix Views are not only for HTML and JSON. Oh no. They are for EVERYTHING!

Getting crazy

I mean, we can serve HTML and JSON, right? But what if we are providing a service not to a user or system, but to a browser?

A web browser only speaks JavaScript. We can fool ourselves all we want, but JS is the de facto language for the browser (WebAssembly pls come now ty). So the question: can our Phoenix Views spit out... JavaScript?

And the surprising answer is: yes! It can!

defmodule Myapp.CrazyView do
  use MyappWeb, :view
end
Enter fullscreen mode Exit fullscreen mode

We're stating that the template is available in the directory configured by the :root property. By default, that would be myapp_web/templates/#{myresource}/#{mycontroller.html.eex}.
Oh, but we're not HTML.

# myapp_web/templates/js/something.js.eex
(function() {
  console.log('oh my, from Elixir? This is <%= @adjective %>!');
})()
Enter fullscreen mode Exit fullscreen mode

Welp, it's a template. We're even reading the :adjective from the assigns there. How do we render it?

defmodule MyappWeb.JSController do
  use MyappWeb, :controller

  def index(conn, _params) do
    render(conn, "something.js", adjective: "awesome")
  end
end
Enter fullscreen mode Exit fullscreen mode

There's only one last thing we need to do in order to actually write JavaScript from this: the correct Content-Type.

# lib/myapp_web/router.ex
defmodule MyappWeb.Router do
  # lots of stuff above...

  pipeline :js do
    plug :put_js_content_type
  end

  defp put_js_content_type(conn, _params) do
    put_content_type(conn, "text/javascript")
  end

  scope "/crazy", MyappWeb do
    pipe_through :js
    get "/wat.js", JSController, :index
  end
end
Enter fullscreen mode Exit fullscreen mode

There you have it. This is not your general use-case, to be honest, but has it's niche and works great!

Discussion (0)