bigskysoftware / htmx

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

multiple requests triggered sequentialy using hx-sync #2385

Open bensos000 opened 7 months ago

bensos000 commented 7 months ago

i have this todo app which is a fullstack built on hono and htmx, i have a header with a backend data to be displayed on the target element in the frontend. but it must wait for the root request to be triggered first to display data on the header.

here is the structure of the layout => html -> body (with hx request) -> div (with hx request) -> ul (with hx request)

the problem is ul hx-sync watches body request and display data correctly (wait for data) but the middle div request is not triggered after body request end (hx-sync).

the url of the request is : ${process.env.BaseUrl}/api/auth/user

here is the code => https://github.com/bensos000/hono-htmx-monorepo

andryyy commented 7 months ago

Are you sure you need to sync here? Wouldn’t the load trigger alone be sufficient?

I haven't used sync a lot but afair it is inherited. So putting it on body means funny times.

I think it could work using "queue all" as policy for sync as workaround.

You could also use hx-on::after-swap or something on body and trigger some events there.

bensos000 commented 7 months ago

yes i used before hx-sync load only but i have a status 403 forbidden when the requests loads sequentially because i inject the event listener htmx:configRequest on hx-get request from the backend to inject the bearer token in the authorization header of every request. the problem here is the script finished executing after all other htmx requests finished so 403. what is the solution to this problem ? can i wait for the script to finish executing before calling hx requests ?

andryyy commented 7 months ago

Can this perhaps be helpful?

https://htmx.org/examples/async-auth/

bensos000 commented 7 months ago

yes i tried this code snippet and my listener to token authrization header injection is working correctly which is why todos authenticated route is respondng with 200 ok status code.

here is the screenshot and code to the problem image this is the layout component code that contains the body :

<html>
      <head>
        <script src="https://unpkg.com/htmx.org@1.9.10"></script>
        <script src="https://unpkg.com/htmx.org/dist/ext/json-enc.js"></script>
        <script src="https://cdn.tailwindcss.com"></script>
      </head>
      <body
        className="bg-gray-800" 
        hx-get={`${process.env.BaseUrl}/api/token`}
        hx-trigger="load"
        id="token-api"
        hx-target="#token"
        hx-swap="innerHtml"
      >
        {children}
      </body>
      <script id="token"></script>
    </html>

this is the inner div inside children of Layout

<Layout>
      <div className="mt-8 max-w-sm mx-auto">
        <div
          hx-get={`${process.env.BaseUrl}/api/auth/user`}
          hx-trigger="load"
          hx-target="this"
          className="flex items-baseline justify-between"
          hx-sync="closest #token-api:loadend"
        ></div>
        <NewTodo />
        <TodoList />
        <script id="logout"></script>
      </div>
</Layout>

and this is the unordered list in the TodoList component:

<ul
    hx-get={`${process.env.BaseUrl}/api/auth/todos`}
    hx-trigger="load, todo-delete from:body"
    hx-target="#todo-list"
    id="todo-list"
    role="list"
    className="divide-y divide-gray-200 dark:divide-gray-700"
    hx-sync="closest  #token-api:loadend"
  >
    <div className="flex items-center justify-center">
      <img
        className="htmx-indicator"
        src="data:image/svg+xml;base64,PD94bW...."
      />
    </div>
</ul>

as you can see in the screenshot these are the sequential htmx requests but only the todos request wait for the script of the authentication token bearer to complete and the other is 401 without token header. this is the problem. how can i configure htmx for that failing request i tried also (hx-trigger:"load delay:100ms") it works but it is not a solution since the requests can varies in response time which is unpredictible.

andryyy commented 7 months ago

Instead of using queue all in the sync parameter, which could still be a quick fix, I suggest you trigger an event like tokenAcquired on body and trigger your depending elements on this event (tokenAcquired from:body).

You could add "hx-on::after-swap='htmx.trigger("body", "tokenAcquired")'" to your body element.

The cleanest option is a separate script to intercept all requests. Added to the head.

bensos000 commented 7 months ago

hello @andryyy thanks for the solution the cleanest option worked out pretty well.

<html>
      <head>
        <script src="https://unpkg.com/htmx.org@1.9.10"></script>
        <script src="https://unpkg.com/htmx.org/dist/ext/json-enc.js"></script>
        <script src="https://cdn.tailwindcss.com"></script>
      </head>
      <body className="bg-gray-800"> {/*hx-on--after-swap='htmx.trigger("body", "tokenAcquired")'*/} 
        {children}
      </body>
      <script
        id="token"
        dangerouslySetInnerHTML={{
          __html: `
        document.body.addEventListener("htmx:configRequest", (e) => { const cookie =
          document.cookie; if (cookie === "" && window.location.pathname !== "/")
          window.location.href = "/"; e.detail.headers["Authorization"] = "Bearer " +
          cookie.split("=")[1]; });
        `,
        }}
      ></script>
    </html>

and i tried the trigger option it partially worked well because the /user and /todo htmx request are in infinite loop can i know why ? because it's sometimes handy to have manually triggering custom events. please help.

image

andryyy commented 7 months ago

Oh, you probably need a "once" modifier for the trigger. 😄

But it’s dirty and difficult to handle imo. I think the script is the best method. :)

andryyy commented 7 months ago

I wonder if the ~Aftershave~ (lol) afterSwap events from within body bubble up and trigger it multiple times. Perhaps it’s filterable by checking the event.target for body.

bensos000 commented 7 months ago

i did console.log here:

document.body.addEventListener('htmx:after-swap', function(evt) {
          console.log(evt.detail.target);
});

and it shows this:

image image

when i added the once modifier it works pretty well thanks for that @andryyy !!!!

image

andryyy commented 7 months ago

Cool! :)