"Their JavaScript API is clunky, esoteric, and hard to understand for devs" I loved her opinion, I've always shared that sentiment but I was afraid of saying it. I also wish there was a way to develop such abstractions (WebComponents) with/in HTMX.
I actually recently contributed Shadow DOM support to the HTMX cobebase for 2.0! Once that drops, linking Facet and HTMX will only take a simple mixin. htmx.process(root)
Web Components Aren't Framework Components
31–40 of 42 posts
Re: Web Components Aren't Framework Components
#32Earlier quoted context omitted.
I like to think that I didn't reinvent it (just like react, et al didn't reinvent it), I only refined the concept into the simplest form that works :-) After all, a custom element that takes a front-end newcomer 30m to write is absolutely preferable to a large infrastructure-based standard that, after 22 years, is still not included in the spec. Look at the options for including HTML fragments in web pages: 1. Comple…
Technically you could also just write a bit of JS within the HTML to do without needing any of the first 3. You could querySelectorAll and check the src attribute etc, then fetch the content put it in the innerHTML. document.querySelectorAll("remote-fragment").forEach(async (el) => { const src = el.getAttribute("src"); const result = await fetch(src).then((res) => res.text()); el.innerHTML = result; }); Or if you wan…
For example it won't work recursively when a fetched fragment has fragments of its own.
Or when the js snippet loads before some fragment element is loaded (say, from a DOM update).
The nice thing about custom elements is that you get to treat them exactly like normal elements.
Re: Web Components Aren't Framework Components
#33I've found much use out of custom elements. On nice use, that took me maybe 30m to code up, is a ` ` element. simply retrieves the specified file and adds it to the DOM where the `remote-fragment` is declared. No need for server-side scripting to include common parts of the HTML page. There's a bunch of similar use-cases currently satisfied by fat client frameworks or server-side languages that simply go away when yo…
Sounds like htmx. https://htmx.org/essays/right-click-view-source/
Re: Web Components Aren't Framework Components
#34I've found much use out of custom elements. On nice use, that took me maybe 30m to code up, is a ` ` element. simply retrieves the specified file and adds it to the DOM where the `remote-fragment` is declared. No need for server-side scripting to include common parts of the HTML page. There's a bunch of similar use-cases currently satisfied by fat client frameworks or server-side languages that simply go away when yo…
Can you elaborate a bit more on the example in your footnote about propagating client-side state? Does that provide functionality similar to Alpine.js's data objects/stores?
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:
Example
Submit
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:
Example
Submit
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.
Re: Web Components Aren't Framework Components
#35Are you new or something?
Web components are so 2012
Re: Web Components Aren't Framework Components
#36Earlier quoted context omitted.
It seems like you reinvented Edge Site Includes ( https://en.m.wikipedia.org/wiki/Edge_Side_Includes ).
I like to think that I didn't reinvent it (just like react, et al didn't reinvent it), I only refined the concept into the simplest form that works :-) After all, a custom element that takes a front-end newcomer 30m to write is absolutely preferable to a large infrastructure-based standard that, after 22 years, is still not included in the spec. Look at the options for including HTML fragments in web pages: 1. Comple…
Re: Web Components Aren't Framework Components
#37Author of such amazing discoveries like, the sun doesn't always shine and the sky isn't always blue. Are you new or something? Web components are so 2012
Re: Web Components Aren't Framework Components
#38Earlier quoted context omitted.
Technically you could also just write a bit of JS within the HTML to do without needing any of the first 3. You could querySelectorAll and check the src attribute etc, then fetch the content put it in the innerHTML. document.querySelectorAll("remote-fragment").forEach(async (el) => { const src = el.getAttribute("src"); const result = await fetch(src).then((res) => res.text()); el.innerHTML = result; }); Or if you wan…
That breaks on edge cases. For example it won't work recursively when a fetched fragment has fragments of its own. Or when the js snippet loads before some fragment element is loaded (say, from a DOM update). The nice thing about custom elements is that you get to treat them exactly like normal elements.
const onRemoteFragmentMount = async (el) => {
const src = el.getAttribute("src");
const result = await fetch(src).then((res) => res.text());
el.innerHTML = result;
};
const callback = (mutationsList, observer) => {
for (let mutation of mutationsList) {
if (mutation.type === "childList") {
mutation.addedNodes.forEach((node) => {
if (node.tagName === "REMOTE-FRAGMENT") {
onRemoteFragmentMount(node);
}
});
}
}
};
const observer = new MutationObserver(callback);
const bodyElement = document.querySelector("body");
observer.observe(bodyElement, {
childList: true,
subtree: true,
});
Re: Web Components Aren't Framework Components
#39Earlier quoted context omitted.
That breaks on edge cases. For example it won't work recursively when a fetched fragment has fragments of its own. Or when the js snippet loads before some fragment element is loaded (say, from a DOM update). The nice thing about custom elements is that you get to treat them exactly like normal elements.
In this case you could use MutationObserver - although we are doing manually the logic web components provide, but my point is that you can do it also without using web components. My code doesn't include attribute changes, but this should be possible to also do with MutationObserver. const onRemoteFragmentMount = async (el) => { const src = el.getAttribute("src"); const result = await fetch(src).then((res) => res.te…
Re: Web Components Aren't Framework Components
#40Earlier quoted context omitted.
Can you elaborate a bit more on the example in your footnote about propagating client-side state? Does that provide functionality similar to Alpine.js's data objects/stores?
Sure, but bear in mind it's only an experiment, so I do not expect this to be long-lived, or if it is, I do not expect it to look the same way as it does now, and even if either of the above remain true, I still think that alternatives such as htmx are preferably for multiple reasons, bar one (explained in the last paragraph of this post). IOW, I expect this to be an experiment that I eventually throw away after havi…
Looking at your example with the psjs elements, it looks like the psjs-bind elements define what fields get captured and used in the published payload, as well as setting the input elements' values from a subscription event; is that correct? That does seem useful for keeping state synchronized across multiple local components.
It seems like you'd still need some JS if there's any client-side logic that needs to be run, though I'm not sure if there's a way to avoid that. I'm thinking of something like displaying different parts of the page based on multiple input fields. Maybe you could handle a lot of use cases like that with plain HTML and CSS, though.