1
0
Fork 0
You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
blog/output/using-gleam-in-html-low-js....

165 lines
9.4 KiB
HTML

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!doctype html>
<html>
<head>
<meta charset=utf-8>
<meta name=generator content="Pelican 4.9.1">
<meta name=author content="LEdoian">
<meta name=description content="My personal webpage">
<meta name=referrer content=no-referrer>
<link rel=stylesheet href="./theme/css/theme.css">
<title>Using Gleam in HTML with as little JavaScript knowledge as possible LEdoian's Blog</title>
</head>
<body>
<header>
<h1>LEdoian's Blog</h1>
</header>
<div id=main>
<nav>
<div>
<!-- Main navigation -->
<!-- TODO! -->
</div>
<div>
<h2>Categories</h2>
<ul>
<li><a href="./category/networking.html">networking</a></li>
<li><a href="./category/programming.html">programming</a></li>
<li><a href="./category/queer.html">queer</a></li>
<li><a href="./category/talks.html">talks</a></li>
<li><a href="./category/technology.html">technology</a></li>
<li><a href="./category/til.html">til</a></li>
</ul>
<h2>Tags</h2>
<ul>
<li><a href="./tag/gleam.html">gleam</a></li>
<li><a href="./tag/software.html">software</a></li>
<li><a href="./tag/web.html">web</a></li>
<li><a href="./tag/gender.html">gender</a></li>
<li><a href="./tag/identity.html">identity</a></li>
<li><a href="./tag/linux.html">linux</a></li>
<li><a href="./tag/lifehack.html">lifehack</a></li>
<li><a href="./tag/relationships.html">relationships</a></li>
<li><a href="./tag/print.html">print</a></li>
<li><a href="./tag/comics.html">comics</a></li>
<li><a href="./tag/ipv6-only.html">ipv6-only</a></li>
<li><a href="./tag/dns.html">dns</a></li>
<li><a href="./tag/meta.html">meta</a></li>
<li><a href="./tag/infrastructure.html">infrastructure</a></li>
<li><a href="./tag/smrst.html">smršť</a></li>
<li><a href="./tag/trains.html">trains</a></li>
<li><a href="./tag/software-engineering.html">software-engineering</a></li>
</ul>
</div>
<div>
<h2>Stalk me also at</h2>
TODO!
<h2>I stalk</h2>
TODO!
</nav>
<main>
<div>
<div class="details"><time datetime="2024-09-09T16:28:00+02:00">2024-09-09 16:28</time></div>
<h1>Using Gleam in HTML with as little JavaScript knowledge as possible</h1>
<p>I've been looking at the <a class="reference external" href="https://gleam.run">Gleam language</a> 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.</p>
<p>My initial JS knowledge: <tt class="docutils literal">alert(3)</tt>, <tt class="docutils literal">onclick</tt> 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 :-)</p>
<div class="section" id="my-code">
<h2>My code</h2>
<p>Let's start with some trivial code, in <tt class="docutils literal">src/lol.gleam</tt> (in a project initialised with <tt class="docutils literal">gleam new lol</tt> and the <tt class="docutils literal">repeatedly</tt> package added with <tt class="docutils literal">gleam add repeatedly</tt>):</p>
<pre class="literal-block">
import gleam/io
import repeatedly
pub fn say_hello() {
repeatedly.call(2000, Nil, fn(_state, _call) {io.println(get_greeting())})
}
pub fn get_greeting() {
&quot;Hello World!&quot;
}
pub fn main() {
io.println(&quot;3, 2, 1, go!&quot;)
}
</pre>
<p>This snippet has many of the basic stuff I might need in future: it does some io, it uses another package <a class="footnote-reference" href="#stdlib-package" id="footnote-reference-1">[1]</a>, 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.</p>
<p>So, build this for JavaScript: <tt class="docutils literal">gleam build <span class="pre">--target</span> javascript</tt>, and <em>stuff happens</em>. It is not very clear what to do now, but <tt class="docutils literal"><span class="pre">grep``ping</span> through the ``build/</tt> directory shows that the built code lives in <tt class="docutils literal">build/dev/javascript/lol/lol.mjs</tt>.</p>
</div>
<div class="section" id="embedding-the-code">
<h2>Embedding the code</h2>
<p>Here comes the fun part: the code is in JavaScript <em>module</em>, 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 <tt class="docutils literal"><span class="pre">file://</span></tt> one
- Due to scoping, I have not found a way of using the functions from devtools console</p>
<p>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 <tt class="docutils literal">main.html</tt> in the project root, but it does not probably matter. The code:</p>
<pre class="literal-block">
&lt;!doctype html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=utf-8&gt; &lt;!-- firefox complains otherwise --&gt;
&lt;title&gt;Buh&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;!-- some random elements to work with --&gt;
&lt;button id=butt&gt;Klik mee&lt;/button&gt;
&lt;p id=uwu&gt;
&lt;!-- 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. --&gt;
&lt;script type=module&gt; //It has to be a module to allow imports
import * as lol from &quot;./build/dev/javascript/lol/lol.mjs&quot;;
// 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();
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Shoutout to <a class="reference external" href="https://stackoverflow.com/questions/53630310/use-functions-defined-in-es6-module-directly-in-html">the person who asked the same question on StackOverflow</a>. From this point on, we only need a HTTP server; luckily, Python has one in stdlib, so just calling <tt class="docutils literal">python3 <span class="pre">-m</span> http.server 12312</tt> in the project root lets us load <tt class="docutils literal"><span class="pre">http://localhost:12312/main.html</span></tt> and see our page.</p>
</div>
<div class="section" id="next-steps-alternatives-et-cetera">
<h2>Next steps, alternatives et cetera</h2>
<p>When developing web frontend in Gleam, the more common way is using a framework like <a class="reference external" href="https://hexdocs.pm/lustre/index.html">Lustre</a> or at least a DOM library (there are <a class="reference external" href="https://packages.gleam.run/?search=dom">several</a>, 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. <a class="footnote-reference" href="#netzpevnik-gleam" id="footnote-reference-2">[2]</a></p>
<p>Also, a common thing to do is using a minifier+bundler like <a class="reference external" href="https://hexdocs.pm/esgleam/index.html">esgleam</a> (it uses <a class="reference external" href="https://esbuild.github.io/">esbuild</a> 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 <a class="reference external" href="https://erikarow.land/notes/esgleam-embed">one tutorial</a> on how to do that.</p>
<p>Also, during writing of this article I realised Gleam can run all the JS code from itself, so I could have a <tt class="docutils literal">js_main</tt> function that would bind the HTML from Gleam itself. But this is probably more readable and separated anyway.</p>
<hr class="docutils" />
<table class="docutils footnote" frame="void" id="stdlib-package" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-1">[1]</a></td><td>Well, as far as I understand it, Gleam's stdlib is actually just another package anyway.</td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="netzpevnik-gleam" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-2">[2]</a></td><td>I will probably use some kind of framework eventually as a part of <a class="reference external" href="https://gitea.ledoian.cz/LEdoian/netzpevnik">netzpevnik</a>, but I am exploring the technologies that will be involved, so I want to keep stuff simple.</td></tr>
</tbody>
</table>
</div>
</div>
</main>
</div> <!-- #main -->
<footer>
<hr>
Written using Pelican 4.9.1 by LEdoian.
</footer>
</body>
</html>