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

设置remote: true的Simple Form未触发AJAX,提交后显示空白页

问题

使用ActionCable开发聊天功能时,消息广播正常、接收端能即时渲染消息,但发送端提交消息创建表单后会跳转到空白页(类似常规POST请求的表现)。消息创建方法已返回200状态码,期望停留在当前页面并通过Stimulus插入HTML。怀疑remote: true未触发,表单走了本地提交流程,不确定是否和使用Vite而非Webpacker有关。同时想了解Redis在此场景中的作用。


相关代码

消息创建表单(app/views/chats/show.html.erb)

<%= simple_form_for [@chat, @message], remote: true,
    html: {
      class: 'chat__form',
      data: {
        action: "turbo:submit-end->chat-subscription#resetForm"
      }
    } do |f| %>
      <%= f.input :content,
                  label: false,
                  placeholder: "Message ##{@name}",
                  input_html: { class: 'chat__form-input' } %>
      <%= button_tag(class: 'chat__form-button') do %>
        <%= image_tag('icons/send.svg') %>
      <% end %>
  <% end %>

消息控制器create方法(app/controllers/messages_controller.rb)

def create
    @chat = Chat.find(params[:chat_id])
    @message = Message.new(message_params)
    @message.chat = @chat
    @message.user = current_user
    if @message.save
      ChatChannel.broadcast_to(
        @chat,
        render_to_string(partial: "messages/message", locals: {message: @message})
      )
      head :ok
    end
  end

Stimulus控制器(app/javascript/controllers/chat_subscription_controller.js)

import { Controller } from "@hotwired/stimulus"
import { createConsumer } from "@rails/actioncable"

export default class extends Controller {
  static values = { chatId: Number }
  static targets = ['messages', 'form']

  connect() {
    this.channel = createConsumer().subscriptions.create(
      { channel: 'ChatChannel', id: this.chatIdValue },
      { received: data => this.#insertMessageAndScrollDown(data) }
    )
    console.log(`Subscribed to the chat with the id ${this.chatIdValue}.`)
    console.log(this.messagesTarget)
  }

  #insertMessageAndScrollDown(data) {
    this.messagesTarget.insertAdjacentHTML('beforeend', data)
    this.messagesTarget.scrollTo(0, this.messagesTarget.scrollHeight)
  }

  resetForm(event) {
    event.preventDefault()
    event.target.reset()
  }

  disconnect() {
    console.log('Unsubscribed from the chat')
    this.channel.unsubscribe()
  }
}

解决方案

  1. 修正remote: true的使用问题
    Rails 7+默认用Turbo替代UJS,remote: true已不再生效。Turbo会自动处理表单提交,但需要确保提交行为不会触发页面跳转:

    • 移除表单上的remote: true属性;
    • 将表单的data-turbo设为false,改用手动AJAX提交逻辑,确保提交后不刷新页面;
    • 或者监听turbo:submit-start事件并阻止默认行为,在Stimulus中手动发送请求。
  2. 调整Stimulus的表单重置逻辑
    当前resetForm方法绑定的turbo:submit-end事件无法阻止页面跳转,需要提前拦截提交行为:

    // 在Stimulus控制器中添加拦截提交的方法
    submitForm(event) {
      event.preventDefault()
      // 手动发送AJAX请求提交表单数据
      fetch(event.target.action, {
        method: 'POST',
        headers: { 'Accept': 'text/plain', 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content },
        body: new FormData(event.target)
      }).then(() => {
        event.target.reset()
      })
    }
    

    然后在表单的data-action中改为submit->chat-subscription#submitForm。

  3. Vite配置检查
    确保Vite正确加载Turbo和Stimulus,在entrypoints/application.js中包含:

    import "@hotwired/turbo-rails"
    import "controllers"
    

    确认Vite Rails插件已正确配置,保证相关JS文件被打包加载。


Redis在ActionCable中的作用

  • 跨服务器消息共享:单服务器部署时ActionCable可用内存存储广播消息,但多服务器场景下,内存无法跨节点共享。Redis作为分布式消息中间件,能让所有服务器节点同步广播内容,确保连接到不同服务器的客户端都能收到消息。
  • 订阅状态管理:Redis帮助ActionCable跟踪所有活跃的频道订阅关系,保证消息能精准推送给所有订阅对应频道的客户端。
  • 提升可靠性:相比内存存储,Redis可配置持久化,避免服务器重启时丢失未发送的消息,增强系统稳定性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 23:27:44