bigskysoftware/htmx

Combine hx-include and hx-vals ?

didibus opened this issue · 4 comments

I've been trying to find a way to use HTMX to create components that:

  1. Components should have their own routes and render functions.
  2. Components should be easily droppable into any page.
  3. Components should be able to react to changes in other components without being tightly coupled.

One common interaction between such self-contained components is that one component must grab input that is present in another component.

In order to do that, the render function of the component that needs to grab input from elsewhere takes the attribute id where to grab the value.

The problem is that, the name of the key for that value on the request is based on the name attribute, and that is managed by the other component. The goal here is too decouple, so the other components should not have to know what name they are meant to use for this component to work. Ideally I just render the component, say, grab this extra value from here: #some-other-input and it should work.

What would be needed to achieve this is something like:

hx-vals="{"foo":"#some-other-input"}"

Currently if one uses hx-include, you can grave the value using a CSS selector, but you can't specify the name you want for it. With hx-vals, you can specify the name, but you can't use a CSS selector.

Maybe we need: hx-include-vals or something like that.

Hey, I never use hx-vals (here goes the disclaimer), but looking at its doc, you should be able to pass in a custom JS function to then do a querySelector if you want to grab the value you need, which would let you associate any value with any name.

The goal here is too decouple, so the other components should not have to know what name they are meant to use for this component to work

Now, on a more conceptual aspect, and as a personal opinion, I must admit I have hard time understanding the decoupling goal presented here, while retrieving specific values from specific elements of other components.
Wouldn't your first component then be tied to these other components and not be independent anymore at all, kinda failing the initial goal? As after all, if you want to include the value in your request, is likely because your first component's backend endpoint expects that value to be here, thus expects the other component to be present along the first one.
With the idea you explained above, you still have to know what ID to look for, so is it really a win compared to know what name you should look for ? In the end you're still looking for an other-component-specific data, I don't really see how this would solve the decoupling issue to be completely honest (but feel free to roast me here).

On another personal opinion, I'd argue it'd probably be better to keep the names consistent if say component A or B include the value of component C, it'd feel weird to me that this very value is named differently in different including components when it still represents the same data. What would be the use of having a different name for it in this case? (though maybe I just lack imagination here)

Other than that, I just wanted to let you know that if you want to add a feature to htmx, you can do so by defining an extension ; you'll want to take a look at the extensions repo for the setup instructions and examples with the existing ones. You can first define an extension from any JS script, then if you'd like to suggest it to be added to htmx, feel free to make a repo for it, and open a PR to the extensions repo to add your extension to the community extensions section.

Thanks for the comment!

I've been using hx-vals with js: and querySelector as a workaround, but it requires letting HX eval code, which some environments lock down. The extension is a good idea, if I've got the time I might look into it.

As for the conceptual aspect, I appreciate the discussion. I'm still trying to figure out how to best organize my code with HTMX and in general, so I'm happy to think through that problem.

What I mean is that say I have a component that's a "Visit Counter". And I want it to show:

"Welcome <visitor-name>, you are visitor number 345"

Now the <visitor-name> is like an input variable to this component.

Now if I hard-code it to hx-include: "#visitor-name". And in my handler for the component render I look for a variable visitor-name.

This means that if say there's another component called Bio, and it has an input field where the user is meant to type in their name. And it saves the bio to the DB.

Now ideally both these components don't know about each other and are fully independent.

Now I'm including them both on a page. And in that page I'm thinking, I want to use the name from the Bio component as the visitor-name you show on the visit counter component.

In the hard-coded case, I have to go modify the code for the Bio component and make sure the name input has id="visitor-name" and name="visitor-name".

This couples Bio with the visit counter.

Instead, say the name on Bio currently has id="bio-name". What I do is that the visit counter doesn't hard-code looking for id="visitor-name", instead the page gives it as argument when it's rendered to look for the value at id="bio-name". And visit-counter will grab the value from there and put in on the visitor-name param.

Now it means I can use Bio anywhere, in other pages, in other apps. And it's just always a Bio component. And I can also use VisitCounter in other pages or apps, and even if Bio isn't on that page, I can tell visit counter to get the visitor-name from some other id that might be on another component or some hidden field on this page etc.

While I understand the idea, isn't that "too much genericity" to aim for? Since it's a "visitor name" component anyway, you probably always intend to pass it some value that represents the name of the person, so it won't be some <input name="address" or anything similar that you'd pass through it.
My suggestion would then be, why not make a simple convention in your code that all inputs representing that same data (no matter the source input here, it'd represent the user's name right?) have the same name so it's a standardized property?
Unless you're making a generic lib ofc, but if that's just for the scope of your own project, I would probably lean more towards this than trying to be as generic as possible for the sake of genericity.
Again, just a personal opinion!

Anyway, regarding the feature itself, you should indeed totally be able to implement it as an extension. You can look at the source code of the include-vals extension, which has a similar behavior

Thanks for the link to an example!

I'm kind of using a contrived example here, but also you might be right.

I come from backend development, and desktop GUI development. And one thing about the web I've always found strange is how separate and global everything is. If I need a script only for one widget, somehow I have to put it in the head section. So I'm always trying to find ways to make things more self-contained and modular.