bigskysoftware / htmx

</> htmx - high power tools for HTML
https://htmx.org
Other
38.06k stars 1.29k forks source link

Trigger other elements #362

Closed srkunze closed 3 years ago

srkunze commented 3 years ago

Hi everybody, a little bit of a background from my side: I worked at a company where we build a similar framework like htmx for internal usage. So I was excited to see the very same idea in the wild last year. Then I decided to use htmx for a small side-project of mine.

Looking at https://htmx.org/examples/ and sifting through the reference however it became clear that there was one important use-case not obviously being covered (or I'm didn't find it in the docs). Think of the following situation in pseudo-html:

<html>
    <body>
        <stuff>
        <list of things>
        <other stuff>
        <create-form>
        <yet other stuff>

When the create-form successfully created a new object, the list of things should be updated as well as the create-form being reset for yet another post. And the rest of the stuff, body or html should not be touched or triggered.

This brings me to the proposal:

Reloading multiple others hx-elements by triggering them.

Or alternatively:

Let's add an example for a create-form with updating other elements after successful form submission (if that's already possible).

BoPeng commented 3 years ago

Will hx-swap="beforeend" work for you?

Even if you would like to update the form itself, you could do

hx-target="myform"
hx-post="post_form"

and then you could return the form with the post swapped out of band using something like

<my form>
<newpost id="mylistofposts" hx-swap-oob="beforeend">
1cg commented 3 years ago

The most straightforward way to handle this in HTML would be to use out of band swaps:

https://htmx.org/docs/#oob_swaps

And send down the new list of things with an id and the hx-swap-oob attribute set to true.

Alternatively, there is an extension, path-deps that could address the situation using path-based dependencies:

https://htmx.org/extensions/path-deps/

this can be set up to trigger a new request from the list of things when the form posts to it

I know you said it isn't an option to replace and but htmx does a pretty good job of making swaps transparent and it might make things simpler to replace everything. There are two related features I should mention.

hx-preserve will preserve an element between swaps:

https://htmx.org/attributes/hx-preserve/

which can be used for things like videos or highly-state-dependent nodes.

Also, there is a morphdom extension that uses morphdoms extremely sophisticated DOM syncing algorithm:

https://htmx.org/extensions/morphdom-swap/

Out of band swaps is probably the most straight forward, but there are many ways to skin this cat. :)

1cg commented 3 years ago

@srkunze does the above answer your use case?

srkunze commented 3 years ago

@1cg Still thinking about it. I didn't have the oob-option on my radar so far; thus I'm still evaluating it. My gut feeling (and that's why it have been taking so long to answer) is ambiguous, i.e. feels interesting but not quite right.

srkunze commented 3 years ago

In order to approach my issue, I proceeded by trying to work-around the limitations so far and get a feeling about HTMX as well as thinking through more conceptual issues here. For this purpose, I opened #375 and #376 for these separate ones.

From an eagle's perspective (and I remember the discussion with colleagues), this is a data-dependency issue. I'm going to explain what I mean by that:

This is the example setup:

  1. Imagine the list element L displays several items drawing data from sources A and B (let's say PostgreSQL tables).
  2. There's a create from C adding data to source A
  3. There's a status element S displaying statistics from source A
  4. There's a modifier button M in L to modify an item, thus changing data in source B

Our requirements:

  1. We want L, M and S to represent the current state of the data sources A and B
  2. separation of concerns (should C know about M? rather not)
  3. avoid implementing data dependencies twice, in the back-end and front-end, keep it simple and maintainable

As a consequence:

  1. When C is submitted and succeeds, we need to update L and S (M implicitly by L)
  2. When M is submitted and succeeds, we need to update L

Ideas so far

In a discussion with a friend of mine, we discovered that websockets should solve that issue more gracefully for the following reasons:

  1. resolving the data dependency graph can be done easily on the server side, simply pushing the new versions to all required elements (using a publish-subscribe pattern)
  2. keeping HTML free of data dependencies management and maintenance (elements only know themselves; can be freely added or removed)
  3. HTMX supports websockets already (experimental I gather but I'll let you know how we fared and provide feedback)

Combining this with the morphdom extension would also be really cool. I'll keep you posted.

srkunze commented 3 years ago

One comment about out-of-band swaps: here we have the ability to replace data-related elements by an request to URL U. The main issue happens if somebody introduces changes.

Code-wise, all OOB-providing URLs (including U) must be discovered and updated. That holds for new elements, existing elements or elements to be removed from the source. So, managing this process can become quite hard.

srkunze commented 3 years ago

One comment about path-deps: my gut-feeling is quite good here. I would backtrace to use it when the websockets idea fails.

path-deps fully solves requirement 2, partially requirement 1 (only for the current user - not for all). Still it has the re-implementation issue.

srkunze commented 3 years ago

@BoPeng @1cg thanks for your feedback so far. 🙂️

srkunze commented 3 years ago

@BoPeng @1cg

My tests using hx-ws were quite successful and also code-wise highly maintainable. I opened pull requests #381 #382 since I noticed some missing features (helpful for development and production).

I know that the original issue has not been solved fully without websockets but I have the feeling that in the environment of current technologies, we should not fight the progress and embrace it.

So, I would rather close this issue here. Except, you want to leave it open for later adding this functionaliy.

1cg commented 3 years ago

If websockets are working for you, then let's go with that.

Still waiting on a review of your change and then we'll merge it. Thanks!

ribeiropaulor commented 3 years ago

Hello, @1cg and @srkunze !

I found myself today with the very same problem.

Would something like the following code make sense?

First, we use the functionality that already exists, attaching a listener to the form.

<ul
      hx-get="/from/l"
      hx-trigger="event-after-form-submit from:form.just-submited"
      hx-target="#l_container"
  ></ul>

We could do the same to another list.

<ul
      hx-get="/from/another"
      hx-trigger="event-after-form-submit from:form.just-submited"
      hx-target="#another_container"
  ></ul>

Finally, we tell HTMX to dispatch a special custom event after swap (could also choose another events that make sense).

<form hx-post="/my-post" hx-dispatch-after-swap="event-after-form-submit">
</form>

I tried the path-deps extension. It works, but, depending on the page, you would 'intercept' paths that you do not want. It was my case.

If something like I described as a possible approach existed, it would be cleaner and less likely to interfere with other stuff on the page.

UPDATE

I missed the hx-ext="ignore:path-deps" possibility at first. I used it now to get rid of interference between contexts.

srkunze commented 3 years ago

@ribeiropaulor Custom events sound really cool. I don't know if hx-target is really necessary but hx-dispatch-after-swap seem to do the trick here.

@1cg What is your opinion on this?

1cg commented 3 years ago

The preferred way of doing this would be to use the htmx events, either with vanilla javascript or hyperscript.

Here's what the hyperscript would look like:

<form hx-post="/my-post" 
          _="on htmx:afterSwap trigger event-after-form-submit">
   ...
</form>
dwasyl commented 3 years ago

The preferred way of doing this would be to use the htmx events, either with vanilla javascript or hyperscript.

Here's what the hyperscript would look like:

<form hx-post="/my-post" 
          _="on htmx:afterSwap trigger event-after-form-submit">
   ...
</form>

Not to be obtuse, but this is something I've been trying to sort out as I adopt htmx for a project. How would this be used to clear a format after a submit? would the event-after-form-submit be a function that cleared the form?