IOW, I expect this to be an experiment that I eventually throw away after having gained some insight. The `ps` in `psjs` below means "publish/subscribe".
The big draw for me about htmx is that common usage does not require knowledge of Javascript! This means that htmx (and my set of custom elements) does not require the creator of the front-end to know that npm even exists, much less how to use it. The same goes for Virtual DOMs, Hooks, tree-shaking/web-packing, promises and async, functions, and the whole container-ship full of arbitrary things that common front-end stacks need you to know.
My custom elements require javascript (for now), and require that the user know what is meant by 'publish a message onto a queue' and 'subscribe to a queue for messages of a particular subject'.
-------------------------------------
I have at least four custom elements: psjs-tree, psjs-bind, psjs-subscribe and psjs-publish.
I also have two JS functions, `publish` and `subscribe`: `publish` publishes an arbitrary payload (a JS object) to a channel (string) with a subject (also of string type). `subscribe` registers a callback function for a channel (string) with a pattern for the subject.
These are all global. A piece of code could do:
subscribe("ERROR", "*", (sender, subject, payload) => {
dialog.innerHTML = `${sender.id}: Error ${subject} ${payload.errMessage}`;
dialog.showModal();
});
and then any code, anywhere else can do `publish(this, "ERROR", "Rpc Request", { errMsg: "Rxed 404"});`My web components psjs-publish and psjs-subscribe execute those two functions (for publish, there is an `onevent` attribute). The psjs-publish component uses the closest psjs-tree ancestor to determine which fields go into the payload. The fields are set by psjs-bind. The psjs-subscribe does the opposite of psjs-publish, in that it uses the closest psjs-tree ancestor to determine which fields of the payload should be used to update the contents or values of the psjs-tree descendents.
For example, a form that can have the values reset by some other code (say, fetching them from a server) and can have the values submitted:
<psjs-tree>
<psjs-subscribe channel="MYFORM" subject="update-fields"> </psjs-subscribe>
<psjs-bind for="team-name"> </psjs-bind>
<psjs-bind for="notifications"> </psjs-bind>
<psjs-bind for="user-name"> </psjs-bind>
<psjs-bind for="user-email"> </psjs-bind>
<form>
<div id="title"> Example</div>
<div id="team-name"> </div>
<div id="notifications"> </div>
<input id="user-name"> </input>
<input id="user-email"> </input>
<psjs-publish onevent="click" channel=MYFORM subject=submit-fields>
<button>Submit</button>
</psjs-publish>
</form>
</psjs-tree>
Of course, this means that I have a line of js somewhere that looks like this: subscribe("MYFORM", "submit-*", (sender, subject, payload) => { /* send payload to server */ });
And another that looks like this: const rsp = successfulFetchFromServer();
publish(this, "MYFORM", "update-fields", { "team-name": rsp.teamName, ... });
This is still very much a WIP, and I'm playing with having custom elements for performing RPC without using the publish/subscribe functions; this is so that, during usage of these components, there will be zero JS to write. Right now there is still the publish/subscribe calls that intercept messages and tx/rx messages from the server to local components.I'd like it to look like this eventually:
<rpc-tree>
<rpc-rx subject="Some Subject">
<rpc-bind for="team-name"> </rpc-bind>
<rpc-bind for="notifications"> </rpc-bind>
<rpc-bind for="user-name"> </rpc-bind>
<rpc-bind for="user-email"> </rpc-bind>
<form>
<div id="title"> Example</div>
<div id="team-name"> </div>
<div id="notifications"> </div>
<input id="user-name"> </input>
<input id="user-email"> </input>
<rpc-tx onevent="click" href="/some/path/to/submit" subject="Some Subject">
<button>Submit</button>
</rpc-tx>
</form>
</rpc-tree>
The psjs-subscribe/publish elements are still useful to propagate value changes locally within the client, but for non-local state an RPC set of elements seems preferable.All in all (and I realise that I wrote an exceptionally long post, so sorry!), I feel that htmx is better right now, but ... it forces the creation of a backend-for-frontend layer, so you have the layers of front-end -> backend-for-front-end -> API. With RPC calls only, I don't need to write the middle layer and can simply use the API directly from the client application, especially when using the custom element for specifying fragments.