When we first hear and use Phoenix Views, it's primary use case is to render HTML or JSON views.
But that's not all Phoenix Views can do!
JavaScript!
HTLM and JSON have their uses in web apps and APIs, for sure. But what if we are an API or web application providing a service not to a user or system, but to a browser?
A web browser can deal with different assets. But if you want it to do something, you have to speak JavaScript. There are some notable exceptions (WebAssembly?) but this is the de-facto way of getting code to a user's browser.
So, are we able to create a Phoenix View that renders JavaScript? Let's give it a shot.
In order to pull this off, we would need:
1) A Phoenix.View that is going to render our JavaScript file
2) A template, probably with an extension like .js.eex
3) A controller that sets up the correct Content-Type
and renders this .js.eex
template.
Let's try!
The View
defmodule Myapp.JSView do
use MyappWeb, :view
end
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}
.
But we are not HTML.
The Template
# myapp_web/templates/js/something.js.eex
(function() {
console.log('oh my, from Elixir? This is <%= @adjective %>!');
})()
As you can see it's a normal template. But there's a key difference: we're actually a .js.eex
extension. This is just to signal that we're building a .js template, it is not dealt with differently by EEx.
And we have access to any assigns, as you can see when we read the @adjective
. No surprises here!
So, how do we render it?
The Controller
defmodule MyappWeb.JSController do
use MyappWeb, :controller
def index(conn, _params) do
render(conn, "something.js", adjective: "awesome")
end
end
something.js
gets mapped to the corresponding something.js.eex
template we defined above. Not unlike building a "normal" HTML Phoenix View. Super clean!
There's only one last thing we need to do in order to correctly serve this file as valid JavaScript: the 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 "/custom_js", MyappWeb do
pipe_through :js
get "/myjsfile.js", JSController, :index
end
end
So there you have it. Phoenix Views are great and really flexible!
DANGER
Be Careful with how you deal with user input when pasting it inside JS code. You could be opening yourself up to a XSS attack! You can read more about XSS here.
Phoenix has facilities to deal with (some) JavaScript data escaping. Refer to javascript_escape.
In short: make sure that data you put on your file is not able to run arbitrary code. This is not meant to be a security guide, so viewer discretion is advised.
Conclusion
Granted, this is not your common use case. Usually, JS files are static and served from a CDN. But sometimes we need to step out of the ordinary, and it's amazing that it's so simple to do in Phoenix!
Top comments (0)