Compare commits
No commits in common. '0578130fbe2cca0b018cc52036bb3973cab6cd94' and '05868386f3d0e90eac06c09bddd10dc845c4e511' have entirely different histories.
0578130fbe
...
05868386f3
@ -1,94 +0,0 @@
|
|||||||
Using Gleam in HTML with as little JavaScript knowledge as possible
|
|
||||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
||||||
|
|
||||||
:slug: using-gleam-in-html-low-js
|
|
||||||
:date: 2024-09-09 16:28
|
|
||||||
:tags: gleam, software, web
|
|
||||||
:category: programming
|
|
||||||
:keywords: javascript module, gleam, frontend
|
|
||||||
:lang: en
|
|
||||||
:translation: false
|
|
||||||
:status: published
|
|
||||||
|
|
||||||
I've been looking at the `Gleam language <https://gleam.run>`__ recently. Among other features, it can be compiled to JavaScript, and thus presumably used on web frontend. I wanted to try that. This is a short tutorial on how to do that with little idea how JavaScript is supposed to work.
|
|
||||||
|
|
||||||
My initial JS knowledge: ``alert(3)``, ``onclick`` and some basic selectors, i.e. the little stuff that is useful to add minor interactivity to HTML pages and implement trivial ViolentMonkey scripts. Namely: I have no knowledge about modules and whatnot, and this post is just a result of my trial-and-error attempt at embedding my Gleam. I succeeded, but still have no idea what I am doing :-)
|
|
||||||
|
|
||||||
My code
|
|
||||||
=======
|
|
||||||
Let's start with some trivial code, in ``src/lol.gleam`` (in a project initialised with ``gleam new lol`` and the ``repeatedly`` package added with ``gleam add repeatedly``)::
|
|
||||||
|
|
||||||
import gleam/io
|
|
||||||
import repeatedly
|
|
||||||
|
|
||||||
pub fn say_hello() {
|
|
||||||
repeatedly.call(2000, Nil, fn(_state, _call) {io.println(get_greeting())})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_greeting() {
|
|
||||||
"Hello World!"
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
io.println("3, 2, 1, go!")
|
|
||||||
}
|
|
||||||
|
|
||||||
This snippet has many of the basic stuff I might need in future: it does some io, it uses another package [#stdlib-package]_, it returns data I will want to show. At first I am mostly interested in multiplatform stuff (i.e. running also on the BEAM backend), so I don't want to use any DOM frameworks at first, though I will mention some of the ways later in the post.
|
|
||||||
|
|
||||||
So, build this for JavaScript: ``gleam build --target javascript``, and *stuff happens*. It is not very clear what to do now, but ``grep``ping through the ``build/`` directory shows that the built code lives in ``build/dev/javascript/lol/lol.mjs``.
|
|
||||||
|
|
||||||
Embedding the code
|
|
||||||
==================
|
|
||||||
|
|
||||||
Here comes the fun part: the code is in JavaScript *module*, not plain code. That comes with several surprises:
|
|
||||||
- It can only be imported from other modules, not by “plain” code in global scope
|
|
||||||
- Due to CORS, imports only work with network schemes, not with the ``file://`` one
|
|
||||||
- Due to scoping, I have not found a way of using the functions from devtools console
|
|
||||||
|
|
||||||
Big wins for the platform /s, but we have to live with that, so let's try to write the respective HTML for this. I put that in ``main.html`` in the project root, but it does not probably matter. The code::
|
|
||||||
|
|
||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset=utf-8> <!-- firefox complains otherwise -->
|
|
||||||
<title>Buh</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!-- some random elements to work with -->
|
|
||||||
<button id=butt>Klik mee</button>
|
|
||||||
<p id=uwu>
|
|
||||||
|
|
||||||
<!-- the “binding” to our Gleam code. This probably needs to be at the end of the page, since it needs to be able to use the selectors. -->
|
|
||||||
<script type=module> //It has to be a module to allow imports
|
|
||||||
import * as lol from "./build/dev/javascript/lol/lol.mjs";
|
|
||||||
|
|
||||||
// Set the text of one element to the computed stuff:
|
|
||||||
let par = document.getElementById('uwu');
|
|
||||||
par.innerText = lol.get_greeting();
|
|
||||||
|
|
||||||
// Let's have an interactive button (I love the come-from pattern, but using `onclick` would be even trickier…)
|
|
||||||
let butt = document.getElementById('butt');
|
|
||||||
butt.addEventListener('click', lol.say_hello);
|
|
||||||
|
|
||||||
// Apparently, the `main` function does not get run automatically, so call that explicitly.
|
|
||||||
lol.main();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
Shoutout to `the person who asked the same question on StackOverflow <https://stackoverflow.com/questions/53630310/use-functions-defined-in-es6-module-directly-in-html>`__. From this point on, we only need a HTTP server; luckily, Python has one in stdlib, so just calling ``python3 -m http.server 12312`` in the project root lets us load ``http://localhost:12312/main.html`` and see our page.
|
|
||||||
|
|
||||||
Next steps, alternatives et cetera
|
|
||||||
==================================
|
|
||||||
|
|
||||||
When developing web frontend in Gleam, the more common way is using a framework like `Lustre <https://hexdocs.pm/lustre/index.html>`__ or at least a DOM library (there are `several <https://packages.gleam.run/?search=dom>`__, I have no idea how mature those are). I did not go this route yet, because I am more interested in using backend-agnostic Gleam code from JavaScript and don't mind writing the trivial bindings in JavaScript. [#netzpevnik-gleam]_
|
|
||||||
|
|
||||||
Also, a common thing to do is using a minifier+bundler like `esgleam <https://hexdocs.pm/esgleam/index.html>`__ (it uses `esbuild <https://esbuild.github.io/>`__ under the hood), so that the whole project is in one file. I don't think I need that now (having the JS be readable is more important to me atm and I don't want to complicate things further), but there is at least `one tutorial <https://erikarow.land/notes/esgleam-embed>`__ on how to do that.
|
|
||||||
|
|
||||||
Also, during writing of this article I realised Gleam can run all the JS code from itself, so I could have a ``js_main`` function that would bind the HTML from Gleam itself. But this is probably more readable and separated anyway.
|
|
||||||
|
|
||||||
-------
|
|
||||||
|
|
||||||
.. [#stdlib-package] Well, as far as I understand it, Gleam's stdlib is actually just another package anyway.
|
|
||||||
|
|
||||||
.. [#netzpevnik-gleam] I will probably use some kind of framework eventually as a part of `netzpevnik <https://gitea.ledoian.cz/LEdoian/netzpevnik>`__, but I am exploring the technologies that will be involved, so I want to keep stuff simple.
|
|
Loading…
Reference in New Issue