You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@rails/ujs中等效于jQuery检测所有AJAX请求完成方法的实现咨询

Equivalent @rails/ujs Solution for Detecting Completed AJAX Requests

Great question! Let's walk through how to replace that jQuery-based AJAX completion check with a @rails/ujs-compatible approach—perfect for your modal remote form + StimulusJS setup, whether you're working in production code or writing Capybara tests.

First, Understand the Original jQuery Logic

Your existing Ruby code checks jQuery.active, which tracks the number of in-flight AJAX requests managed by jQuery. When it hits 0, all requests are done. Since @rails/ujs (especially modern versions) doesn't rely on jQuery, we need a way to track requests using native browser APIs and @rails/ujs events.


Solution 1: For Capybara Tests (Matching Your Original Ruby Code)

If you're looking to replicate the test helper method, here's how to do it without jQuery:

Step 1: Add a Global Request Tracker to Your Frontend

First, add code to track all AJAX requests (including those from @rails/ujs and Stimulus) in your application's JavaScript:

// app/javascript/packs/application.js
// Initialize a global counter for active AJAX requests
window.activeAjaxRequests = 0;

// Track @rails/ujs AJAX events
document.addEventListener('ajax:beforeSend', () => {
  window.activeAjaxRequests++;
});
document.addEventListener('ajax:complete', () => {
  window.activeAjaxRequests--;
});

// Track fetch requests (common in Stimulus)
const originalFetch = window.fetch;
window.fetch = (...args) => {
  window.activeAjaxRequests++;
  return originalFetch(...args).finally(() => {
    window.activeAjaxRequests--;
  });
};

// Track raw XMLHttpRequest requests (if your Stimulus code uses this)
const originalXHROpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(...args) {
  window.activeAjaxRequests++;
  this.addEventListener('loadend', () => {
    window.activeAjaxRequests--;
  });
  originalXHROpen.apply(this, args);
};

Step 2: Update Your Test Helper

Replace your existing finished_all_ajax_requests? method with this version that reads our global counter:

def finished_all_ajax_requests?
  # Wait for the counter to exist (in case it hasn't initialized yet)
  return false if page.evaluate_script('typeof window.activeAjaxRequests === "undefined"')
  page.evaluate_script('window.activeAjaxRequests').zero?
end

This works exactly like your original jQuery check, but now it tracks all AJAX requests handled by @rails/ujs, Stimulus, and native browser APIs.


Solution 2: For Production/Stimulus Controller Logic

If you need to check for completed requests directly in your Stimulus controller (e.g., to close the modal after all AJAX finishes), here's a clean, encapsulated approach:

Create an AJAX Tracker Stimulus Controller

// app/javascript/controllers/ajax_tracker_controller.js
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  connect() {
    this.activeRequests = 0;

    // Track @rails/ujs events
    document.addEventListener('ajax:beforeSend', () => this.activeRequests++);
    document.addEventListener('ajax:complete', () => this.activeRequests--);

    // Wrap fetch to track Stimulus-initiated requests
    this.wrapFetch();
    // Wrap XMLHttpRequest if needed
    this.wrapXHR();
  }

  wrapFetch() {
    const originalFetch = window.fetch;
    window.fetch = (...args) => {
      this.activeRequests++;
      return originalFetch(...args).finally(() => this.activeRequests--);
    };
  }

  wrapXHR() {
    const originalXHROpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function(...args) {
      this.activeRequests++;
      this.addEventListener('loadend', () => this.activeRequests--);
      originalXHROpen.apply(this, args);
    };
  }

  // Public method to check if all requests are done
  isAllFinished() {
    return this.activeRequests === 0;
  }
}

Use the Tracker in Your Modal Form Controller

// app/javascript/controllers/modal_form_controller.js
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["form"];

  connect() {
    // Get a reference to the AJAX tracker
    this.ajaxTracker = this.application.getControllerForElementAndIdentifier(
      document.body,
      "ajax-tracker"
    );
  }

  // Example: Triggered when the remote form submits
  handleFormSubmit() {
    // Wait for all AJAX to finish before closing the modal
    this.waitForAllAjaxThenClose();
  }

  waitForAllAjaxThenClose() {
    if (this.ajaxTracker.isAllFinished()) {
      this.closeModal();
    } else {
      // Check again after a short delay (adjust timing as needed)
      setTimeout(() => this.waitForAllAjaxThenClose(), 100);
    }
  }

  closeModal() {
    this.element.classList.add('hidden');
    // Add any other modal cleanup logic here
  }
}

Don't forget to register the ajax-tracker controller in your HTML (e.g., on the <body> tag):

<body data-controller="ajax-tracker">
  <!-- Your modal and form here -->
</body>

Key Notes

  • This approach covers all AJAX sources: @rails/ujs remote forms, Stimulus fetch/XHR calls, and even third-party scripts using native APIs.
  • For tests, the global counter is straightforward and mirrors the simplicity of jQuery.active.
  • For production code, using a Stimulus controller keeps your logic encapsulated and aligned with Hotwire best practices.

内容的提问来源于stack exchange,提问作者Artur79

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 09:57:48