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

基于Rails构建无刷新链式问答应用的技术实现咨询

实现Rails单页链式问答功能方案

嘿,我来帮你搞定这个链式问答的功能!首先你的现有数据模型还需要补充一些核心字段来支撑“答案决定下一个问题”的逻辑,然后结合Rails生态里的Turbo和Stimulus,就能轻松实现单页无刷新的交互效果。

1. 完善数据模型

首先得给GameBlock补充关键字段,用来存储问题内容、答案选项以及关联的下一个问题:

生成迁移文件

rails generate migration AddQuestionFieldsToGameBlocks question:text options:json next_block_id:integer user_answer:string
rails db:migrate

更新模型代码

修正Game模型的关联(注意复数形式),同时给GameBlock添加上必要的关联和验证:

# app/models/game.rb
class Game < ApplicationRecord
  belongs_to :user
  has_many :game_blocks, dependent: :destroy
  accepts_nested_attributes_for :game_blocks, allow_destroy: true

  # 快速获取第一个问题
  def first_block
    game_blocks.order(:created_at).first
  end
end
# app/models/game_block.rb
class GameBlock < ApplicationRecord
  belongs_to :game
  belongs_to :next_block, class_name: "GameBlock", optional: true

  # 验证核心字段必填
  validates :question, presence: true
  validates :options, presence: true
end

2. 后端API端点

我们需要两个核心接口:一个用来加载游戏的初始问题,另一个用来接收用户答案并返回下一个问题:

# app/controllers/games_controller.rb
class GamesController < ApplicationController
  before_action :set_game, only: [:show, :update_answer]

  # 加载游戏页面,渲染第一个问题
  def show
    @current_block = @game.first_block
  end

  # 处理用户提交的答案,返回下一个问题
  def update_answer
    current_block = @game.game_blocks.find(params[:block_id])
    current_block.update(user_answer: params[:answer])

    @next_block = current_block.next_block

    respond_to do |format|
      format.turbo_stream do
        if @next_block
          render turbo_stream: turbo_stream.replace("question-container", partial: "game_blocks/block", locals: { block: @next_block })
        else
          render turbo_stream: turbo_stream.replace("question-container", partial: "game_blocks/end")
        end
      end
    end
  end

  private
  def set_game
    @game = Game.find(params[:id])
  end
end

别忘了在config/routes.rb里添加路由:

resources :games do
  member do
    post :update_answer
  end
end

3. 前端无刷新交互(Stimulus + Turbo)

用Stimulus来监听用户的答案选择事件,结合Turbo Stream实现无刷新替换问题:

创建Stimulus控制器

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

export default class extends Controller {
  static values = { gameId: Number, blockId: Number }

  submitAnswer(event) {
    event.preventDefault()
    const answer = event.target.value

    fetch(`/games/${this.gameIdValue}/update_answer`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-CSRF-Token": document.querySelector('meta[name="csrf-token"]').content
      },
      body: JSON.stringify({ block_id: this.blockIdValue, answer: answer })
    })
  }
}

4. 视图层实现

游戏主页面(app/views/games/show.html.erb)

<div>
  <%= turbo_frame_tag "question-container" do %>
    <%= render partial: "game_blocks/block", locals: { block: @current_block, game_id: @game.id } %>
  <% end %>
</div>

问题局部视图(app/views/game_blocks/_block.html.erb)

<% if block %>
  <div 
    data-controller="question"
    data-question-game-id-value="<%= game_id %>"
    data-question-block-id-value="<%= block.id %>"
  >
    <h3><%= block.question %></h3>
    <% block.options.each do |option| %>
      <button 
        type="button"
        data-action="question#submitAnswer"
        value="<%= option %>"
        class="px-4 py-2 m-2 bg-blue-500 text-white rounded"
      >
        <%= option %>
      </button>
    <% end %>
  </div>
<% end %>

问答结束局部视图(app/views/game_blocks/_end.html.erb)

<div>
  <h3 class="text-xl font-bold">问答结束!感谢参与</h3>
  <p>你的所有答案已保存,可在个人中心查看记录</p>
</div>

核心逻辑说明

  • 打开游戏页面时,自动加载当前游戏的第一个GameBlock
  • 用户点击答案按钮后,Stimulus控制器发送POST请求到后端接口
  • 后端更新用户答案后,通过Turbo Stream无刷新替换页面上的问题区域
  • 当没有下一个问题时,自动切换到问答结束的提示页面

这样就完全实现了单页无刷新、答案驱动下一个问题的链式问答功能啦!

内容的提问来源于stack exchange,提问作者Val-Lee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 04:22:08