Phoenix uses Elixir, but when dealing with LiveView Hooks it requires JavaScript. But how about using another functional language in that area?.
Presenting Gleam
The power of a type system, the expressiveness of functional programming, with a familiar and modern syntax.
Gleam comes with compiler, build tool, formatter, editor integrations, and package manager all built in, so creating a Gleam project is just running gleam new
.
In my humble opinion, Gleam is the perfect alternative to Typescript! if you want all the goodies of a functional language and a type system for your Phoenix Hooks.
Be sure the gleam
binary is in your $PATH. You can refer to the Installation Guide for more details.
$ gleam --version
gleam 0.26.1
This will be an small and simple project (Phoenix 1.6) with no ecto. Just a LiveView with a simple hook for demostration.
First let's start with a project named gleamphx
with no database requirement (just to be slim).
$ mix phx.new . --app gleamphx --no-ecto
Let's go to assets/
directory and create a new Gleam Project
named hooks
.
$ cd assets
$ gleam new hooks
$ cd hooks
Now we edit gleam.toml
so we can setup the Javascript
target.
name = "hooks"
version = "0.1.0"
description = "A Gleam project"
# ...
target = "javascript"
[javascript]
# Generate TypeScript .d.ts files
typescript_declarations = true
# Which JavaScript runtime to use with `gleam run`, `gleam test` etc.
runtime = "node" # or "deno"
This will be the main Javascript file that will export all of our hooks. This file will be used in app.js
later.
$ touch assets/hooks/index.js
import * as Hello from "./build/dev/javascript/hooks/hello.mjs";
export default { Hello };
Inside the src/
directory we will create our hooks.
Lets create hello.gleam
hook.
import gleam/io
pub fn mounted() {
io.println("Hello from Gleam!")
}
Ok we are ready with our hook. Lets configure Phoenix!.
First lets edit assets/app.js
to import our hooks.
// ...
import Hooks from "../hooks"
// ...
let liveSocket = new LiveSocket("/live", Socket, {hooks: Hooks, params: {_csrf_token: csrfToken}})
Let's add a new build step in assets.deploy
task.
"gleam.build": [
"cmd cd assets/hooks && rm -rf build && gleam build"
]
This task only builds the gleam code to the target javascript files.
defp aliases do
[
"gleam.build": [
"cmd cd assets/hooks && rm -rf build && gleam build"
],
setup: ["deps.get"],
"assets.deploy": [
"gleam.build",
"esbuild default --minify",
"phx.digest"]
]
end
We add the task to the assets.deploy
pipeline.
Ok now we just have to test. Let's create a simple live view with a div that is Hooked to the Hello
function in Gleam.
First we configure our router
scope "/", GleamphxWeb do
pipe_through :browser
live "/", Live.Example, :index
end
And then create our module
defmodule GleamphxWeb.Live.Example do
use GleamphxWeb, :live_view
@impl true
def render(assigns) do
~H"""
<div id="ExampleGleamHook" phx-hook="Hello">Example Hooked Component</div>
"""
end
end
If everything went OK then after mix phx.server
you will see in the browser console a message similar to this.
- "/": Example Gleam code using console.log
- "/video": Example Gleam code interacting with the
MediaStream
api andthis.el
;
TODO: Maybe configure autoreload on change of gleam files.
You can check out the example project here https://github.com/ElixirCL/gleamphx