Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

With multiple htmx.ajax requests only the first call is executed #2147

Open
RogierdeRuijter opened this issue Jan 2, 2024 · 14 comments
Open

Comments

@RogierdeRuijter
Copy link

When calling htmx.ajax multiple times only the first call is actually executed.
For example when executing the code below. Only my-page-1 is requested.

htmx.ajax("GET", `/my-page-1.html`);
htmx.ajax("GET", `/my-page-2.html`);

It seems like the my-page-2 call is cancelled.

Reproduction link is here

@Path-17
Copy link

Path-17 commented Jan 5, 2024

Im having a similar issue, except that only the second request is executed.

@RogierdeRuijter
Copy link
Author

My solution was to use fetch to retrieve data and then insert the result into the DOM using JavaScript. I stopped using htmx for this problem.

@Telroshan
Copy link
Collaborator

Hey, you might want to take a look at hx-sync

drop - drop (ignore) this request if an existing request is in flight (the default)

By default, if a second request is fired on an element that already has one in flight, that new request will be dropped thus cancelled

What may not be intuitive in your case here, is that calling htmx.ajax without the third parameter will default using the body as the source element of your requests. So with the default hx-sync strategy, any request happening while one is already in flight will be ignored

You could:

  • Define hx-sync="queue all" for example on your body element
  • Use different elements as the source of your htmx.ajax calls so they don't even collide at all

@freshstartagain
Copy link

Having a similar issue but for me the second request is not running.

  htmx.ajax('GET', "/cart/products", {target:"#cart_products", swap:"innerHTML"});
  htmx.ajax('GET', "/cart/summary", {target:"#cart_summary", swap:"innerHTML"});
  htmx.ajax('GET', "/cart/products_count", {target:"#cart_product_count", swap:"innerHTML"});

@amrojjeh
Copy link
Contributor

this:queue all does not seem to be working on the body tag. I'll try to investigate this further @Telroshan

@amrojjeh
Copy link
Contributor

I'm having trouble understanding when is the queuedRequest supposed to be cleared in the issueAjaxRequest function. It seems to only occur when a new request is sent, yet while debugging I've found that the htmx internal data sometimes resets, causing the queue to reset (no idea why or what's causing it)? And even then, shouldn't the queue sending requests as the element becomes free rather than waiting for a new request? Or maybe I'm just misunderstanding the source code. For further clarity on the problem, my goal is to make the below work:

<!DOCTYPE html>
<html>
  <head>
    <script src="htmx.js"></script>
  </head>
  <body hx-sync="this:queue all">
    <p id="questionid123">first</p>
    <p id="questionid124">second</p>
    <p id="questionid125">third</p>
    <p id="questionid0">fourth</p>
    <script>
      setTimeout(() => htmx.ajax('GET', '/question/partial?fieldId=0', { source: "body", target: '#questionid0'}), 2000);

      var array= ['123','124','125'];
      for(i=0;i<array.length;i++) { 
          htmx.ajax('GET', '/question/partial?fieldId='+array[i], { source: "body",
          target: '#questionid'+array[i] });
      }
    </script>
  </body>
</html>

@patreeceeo
Copy link

patreeceeo commented Jul 19, 2024

This issue arises when ajax is called while other requests sent via ajax are still in fight, even when the targets are different elements. The root cause has to do with the XHR being stored on the internal data for the target elements and the fact that, for some reason, ajax is using the internal data for the body element even when a target parameter is provided. Thus, it seems to get confused and think there's already a request in flight for the body element when if it were doing the right thing it wouldn't necessarily think there are any requests in flight for the body.

I fixed the issue for myself with the following change:

   /**
   * Issues an htmx-style AJAX request
   *
   * @see https://htmx.org/api/#ajax
   *
   * @param {HttpVerb} verb
   * @param {string} path the URL path to make the AJAX
   * @param {Element|string|HtmxAjaxHelperContext} context the element to target (defaults to the **body**) | a selector for the target | a context object that contains any of the following
   * @return {Promise<void>} Promise that resolves immediately if no request is sent, or when the request is complete
   */
  function ajaxHelper(verb, path, context) {
    verb = (/** @type HttpVerb */(verb.toLowerCase()))
    if (context) {
      if (context instanceof Element || typeof context === 'string') {
-       return issueAjaxRequest(verb, path, null, null, {
+       return issueAjaxRequest(verb, path, resolveTarget(context), null, {
          targetOverride: resolveTarget(context),
          returnPromise: true
        })
      } else {
        return issueAjaxRequest(verb, path, resolveTarget(context.source), context.event,
          {
            handler: context.handler,
            headers: context.headers,
            values: context.values,
            targetOverride: resolveTarget(context.target),
            swapOverride: context.swap,
            select: context.select,
            returnPromise: true
          })
      }
    } else {
      return issueAjaxRequest(verb, path, null, null, {
        returnPromise: true
      })
    }
  }

I don't understand why it was passing null for the elt parameter to issueAjaxRequest, so maybe this will have some unintended consequences, but it fixed this issue for me.

HTH and happy to collab more on this.

@patreeceeo
Copy link

I'll roll with this change for now and if it doesn't cause me any trouble I'll issue a PR (if I don't forget ;))

patreeceeo added a commit to patreeceeo/htmx that referenced this issue Jul 28, 2024
@patreeceeo
Copy link

patreeceeo commented Jul 29, 2024

fwiw, the above solution is still working fine for me, but I see that the maintainers don't want PRs without explicitly asking for them, so there's nothing for me to do for now. Their automated tests are also cool with this change.

@amrojjeh
Copy link
Contributor

Sorry I meant to test this as well but I've not gotten to it yet. I'll be sure to test this on my end as well. But thank you for your work!

@Telroshan
Copy link
Collaborator

I see that the maintainers don't want PRs without explicitly asking for them

Only for new features @patreeceeo ! Cf the contribution guidelines

  1. Please do not PR new features unless you have already made an issue proposing the feature, and had it accepted by a core maintainer.
  2. Correspondingly, it is fine to directly PR bugfixes for behavior that htmx already guarantees

You're ofc welcome to submit a bugfix PR. We'll then investigate on whether this is the best fix or not, make sure it doesn't break anything etc., but you can open that FR if you feel like it

@patreeceeo
Copy link

Oh, I totally misread... I'll open that bugfix PR.

patreeceeo added a commit to patreeceeo/htmx that referenced this issue Jul 29, 2024
@Darkproduct
Copy link

I solved this by adding the source attribute to the ajax options:

htmx
      .ajax("GET", "./stuff.html", {
        source: "#stuff",
        target: "#stuff",
        swap: "innerHTML",
      })

From what I read, htmx remember what requests are already in flight for each source to avoid requesting the same data multiple times and to cancel in flight requests when the data changed and a new request should be made. If the source attribute is not provided, htmx will use the <body> element for all requests, so only one inflight request at the time possible.

@gabrielacos
Copy link

I solved this by adding the source attribute to the ajax options:

htmx
      .ajax("GET", "./stuff.html", {
        source: "#stuff",
        target: "#stuff",
        swap: "innerHTML",
      })

From what I read, htmx remember what requests are already in flight for each source to avoid requesting the same data multiple times and to cancel in flight requests when the data changed and a new request should be made. If the source attribute is not provided, htmx will use the <body> element for all requests, so only one inflight request at the time possible.

This worked for me Thank you !!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants