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

求助:如何用Rails 5 + AJAX实现按地点筛选课程的三级联动下拉菜单

Hey Bradley, let's get that dependent dropdown workflow working smoothly! Since you're using Rails, we can combine its backend tools with some jQuery AJAX to make the dropdowns update dynamically based on user selections. Here's a step-by-step breakdown tailored to your setup:


1. Update Your View (Add IDs & Initial States)

First, we need to modify your ERB view to add unique IDs to each dropdown (so we can target them with JS) and set sensible initial states (disabled until a parent selection is made):

<div class="row no-gutters wow slideInUp" data-wow-duration="1s">
  <div class="col-md-12 home-form">
    <form class="form-inline">
      <!-- Location dropdown: Add ID to trigger AJAX on change -->
      <select id="location-select" class="custom-select mb-0 mr-sm-0 mb-sm-0">
        <option selected>Location</option>
        <% @locations.each do |location| %>
          <option value="<%= location.id %>"><%= location.header %></option>
        <% end %>
      </select>

      <!-- Course Type dropdown: Start disabled, empty except default -->
      <select id="course-type-select" class="custom-select mb-0 mr-sm-0 mb-sm-0" disabled>
        <option selected>Course Type</option>
      </select>

      <!-- Course dropdown: Start disabled, empty except default -->
      <select id="course-select" class="custom-select mb-0 mr-sm-0 mb-sm-0" disabled>
        <option selected>Course</option>
      </select>

      <button type="submit" class="btn btn-primary">Submit</button>
    </form>
  </div>
</div>

2. Add Backend AJAX Endpoints

Next, we'll add two controller actions to return filtered data as JSON, plus the routes to map to them.

Controller Actions (Add to your CoursesController)

These actions will fetch the relevant data based on the user's selection:

# app/controllers/courses_controller.rb
# Get unique course types for a given location
def course_types_by_location
  location = Location.find(params[:location_id])
  # Pluck unique course types and sort them for better UX
  course_types = location.courses.pluck(:course_type).uniq.sort
  render json: { course_types: course_types }
end

# Get courses for a given location + course type
def courses_by_type_and_location
  location = Location.find(params[:location_id])
  courses = location.courses.where(course_type: params[:course_type])
  # Format courses into a simple ID/title structure for the dropdown
  course_options = courses.map { |course| { id: course.id, title: course.title } }
  render json: { courses: course_options }
end

Routes (Add to config/routes.rb)

Map the actions to accessible URLs:

get 'courses/course_types_by_location', to: 'courses#course_types_by_location'
get 'courses/courses_by_type_and_location', to: 'courses#courses_by_type_and_location'

3. Add the AJAX JavaScript (Using jQuery)

Create a new file at app/assets/javascripts/course_dropdowns.js (or add this to your existing JS file) to handle the dynamic updates:

$(document).ready(function() {
  // Handle location selection change
  $('#location-select').on('change', function() {
    const locationId = $(this).val();

    // Reset dropdowns if user picks the default "Location" option
    if (!locationId) {
      $('#course-type-select').empty().append('<option selected>Course Type</option>').prop('disabled', true);
      $('#course-select').empty().append('<option selected>Course</option>').prop('disabled', true);
      return;
    }

    // Fetch course types for the selected location
    $.ajax({
      url: '/courses/course_types_by_location',
      type: 'GET',
      data: { location_id: locationId },
      beforeSend: function() {
        // Show loading state while fetching
        $('#course-type-select').prop('disabled', true).empty().append('<option selected>Loading...</option>');
      },
      success: function(response) {
        // Enable and populate the course type dropdown
        $('#course-type-select').prop('disabled', false).empty().append('<option selected>Course Type</option>');
        response.course_types.forEach(function(type) {
          $('#course-type-select').append(`<option value="${type}">${type}</option>`);
        });
        // Reset course dropdown since location changed
        $('#course-select').empty().append('<option selected>Course</option>').prop('disabled', true);
      },
      error: function() {
        alert('Oops, failed to load course types. Please try again.');
        // Reset to default state on error
        $('#course-type-select').empty().append('<option selected>Course Type</option>').prop('disabled', true);
      }
    });
  });

  // Handle course type selection change
  $('#course-type-select').on('change', function() {
    const locationId = $('#location-select').val();
    const courseType = $(this).val();

    // Reset course dropdown if user picks default
    if (!courseType) {
      $('#course-select').empty().append('<option selected>Course</option>').prop('disabled', true);
      return;
    }

    // Fetch courses for the selected location + type
    $.ajax({
      url: '/courses/courses_by_type_and_location',
      type: 'GET',
      data: { location_id: locationId, course_type: courseType },
      beforeSend: function() {
        $('#course-select').prop('disabled', true).empty().append('<option selected>Loading...</option>');
      },
      success: function(response) {
        // Enable and populate the course dropdown
        $('#course-select').prop('disabled', false).empty().append('<option selected>Course</option>');
        response.courses.forEach(function(course) {
          $('#course-select').append(`<option value="${course.id}">${course.title}</option>`);
        });
      },
      error: function() {
        alert('Oops, failed to load courses. Please try again.');
        $('#course-select').empty().append('<option selected>Course</option>').prop('disabled', true);
      }
    });
  });

  // Handle form submission to navigate to the course page
  $('form').on('submit', function(e) {
    e.preventDefault(); // Stop default form reload
    const courseId = $('#course-select').val();
    if (courseId) {
      // Redirect to the course's show page (adjust the path if your routes use a different structure)
      window.location.href = `/courses/${courseId}`;
    } else {
      alert('Please select a course first!');
    }
  });
});

What This JS Does:

  • Listens for changes to the location dropdown, fetches matching course types, and populates the second dropdown.
  • Listens for changes to the course type dropdown, fetches matching courses, and populates the third dropdown.
  • Adds loading states to let users know data is being fetched.
  • Handles form submission by redirecting to the selected course's page instead of reloading the whole page.

4. Quick Notes
  • Make sure jQuery is included in your Rails app (it's included by default in older Rails versions; for Rails 7+, you may need to add it via importmap-rails or a CDN).
  • If your Course model doesn't have a course_type attribute, adjust the controller actions to match your actual course categorization logic.
  • You can style the loading states further (like adding a spinner icon) to improve UX even more.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:35:09