Rails [求助] 用 form_with 在发送没有 model 关联的 ajax 请求时遇到问题

MrTentacle · 2020年01月10日 · 最后由 MrTentacle 回复于 2020年01月10日 · 3048 次阅读

我是个新手 我查阅了很多帖子 但依旧无法解决这个问题

我有一些数据需要提交到后端 这些数据不属于任何 model
我在尝试使用 form_with 向控制器发送没有 model 关联的 ajax 请求

index.html.erb

<%= form_with url: check_mobile_h5_meet_videos_url, method: "post", remote: true, id: "form" do
   %>
  <div class="form-group">
    <%= hidden_field_tag :userId, "#{rand(999999999)}",name: "userId", id: "userId", class: "form-control" %>
    <label id='mobile_title'>手机号:</label>
    <%= number_field_tag :mobile, "", class: "form-control" %>
  </div>
  <div class="form-group bmd-form-group">
    <label id='room_title'>房间号:</label>
    <%= number_field_tag :roomId, "",id: "roomId", name: "roomId", required: 'required', class: "form-control" %>
   </div>
  <div id='local_img'>
    <%=image_tag 'h5/meet/index.jpg' %>
  </div>
  <div class="form-group bmd-form-group">
    <%= button_tag "加入房间", id: "join", class: "btn btn-raised btn-primary" %>
  </div>
<% end %>

h5/meet/videos_controller.rb

def check_mobile

    @mobile = params[:mobile]
    @room_id = params[:roomId]
    # 页面生成的随机用户ID
    @user_id = params[:userId]
    logger.debug "#{@mobile} | #{@room_id} | #{@user_id}"

    @is_admin = false
    if @mobile.present?
      if @mobile.in?(ADMIN_MOBILES)
        @is_admin = true
      end
    else
      # 提示点啥呢?
    end

end

check_mobile.js.erb

console.log("已进入 check_mobile.js.erb");
console.log("手机号:<%= @mobile %>");
console.log("房间号:<%= @room_id %>");
console.log("随机用户ID:<%= @user_id %>");
console.log("是否有权开房:<%= @is_admin %>");

我的数据被成功发送 控制器中可以拿到数据 然而 页面中却提示我:

xxx ..... is missing a template for this request format and variant. request.formats: ["text/html"] 
request.variant: []

此时 check_mobile.js.erb 文件中的内容并没有被执行到

这里我无法理解 以我学习的知识看来 rails 在发送 ajax 请求时候
只需要对应 action 的 check_mobile.js.erb 文件就行了
然而这里却向我索要页面模版 我尝试创建了一个对应的 check_mobile.html.erb
可是在请求成功后会被直接跳转过去了

我期望 页面不跳转 且在 js.erb 中继续进行处理 然后由我来控制页面的局部刷新或跳转
我卡在这里三天了查阅了很多资料 答案都不是我想要的 我不知道该如何进行下去

我不能确定我对 rails 的知识理解是否有误 求各位前辈指点迷津 感激不尽 OTZ


此问题已解决,总结如下 ↓↓↓ 希望能帮助遇到同样困境的朋友

多谢 一楼 yfscret 老哥的提醒 确实是请求类型的问题

因为我在 form_with 上浪费了两天的时间 所以我参考了 二楼 stargwq 老哥的方案 直接在 JS 中写

var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function(){
  if(ajax.readyState == 4 && ajax.status == 200){
    var msg = ajax.responseText;
    console.log(msg);
  }
}
var url = "videos/check_mobile";
ajax.open('POST', url, true);
ajax.setRequestHeader("content-type","application/x-www-form-urlencoded");
var params = "mobile=" + mobile + "&userId=" + userId + "&roomId=" + roomId;
ajax.send(params);

然而我发现这种↑↑↑原生写法请求是成功了 但是查看了相关帖子说 不好解决浏览器兼容问题
于是我使用了 JQuery 的写法↓↓↓来避免未知的浏览器兼容问题

$.ajax({
  url: "/h5/meet/videos/check_mobile",
  type: "POST",
  data: {
    check_mobile_params: {
      mobile: roomId,
      roomId: roomId,
      userId: userId
    }
  },
  success: function(resp){  }
});

观察请求类型:

且浏览器控制台中可以正确打印结果


那么 form_with remote:true 未识别为 js 格式 而是 html 格式 是什么原因呢?

  1. rails 会根据请求 headers 的 accept type 来选择模板
  2. form submit 默认 accept 是 html
  3. 加上 reomte:true 的 form 实际只给 form 加了一个 data-remote=true 的属性,并没有改变表单提交的行为
  4. 改变表单提交行为的是 rails ujs 侦测到 data-remote=true 的 form 会重写提交
    改成发一个 ajax 请求 并且带 accept type 等于 javascript 的 headers

感谢各位前辈的指点 OTZ

可能我的解决方式不能适用于所有情况
但愿能为遇到同样遭遇的同行提供一点参考价值

你的理解是对的,ajax 请求只要有对应的.js.erb 文件就行。

我怀疑是你浏览器发起的请求不是 ajax 请求。rails_ujs 的 ajax 请求的 request.formats 应该 text/javascript, 而你的是 text/html

你可以浏览器检查元素,查看网络那块,点击表单后请求的类型是不是 XHR。如果不是 XHR,那很可能是 rails_ujs 的问题。

直接在 index.html.erb 写 ajax,先暴力解决了

你可以在后台 log 里看到他 render 的到底是 html 还是 js 另外你这个 form _with 换成 form_tag 试试,如果不是 model 建议用 form_tag

yfscret 回复

多谢老哥提醒 我最早用的就是 form_tag... remote: true 还留在上面忘了删 因为 form_with 是自带 remote: true 效果的 我就没管它

stargwq 回复

已经实操了~

zhuoerri 回复

多谢老哥的提醒 确实是请求类型的问题 已经修改了请求方式 更新在帖子正文中了~ 感谢 OTZ

MrTentacle 关闭了讨论。 01月10日 18:41
MrTentacle 重新开启了讨论。 01月12日 22:42
MrTentacle 关闭了讨论。 01月12日 22:42
需要 登录 后方可回复, 如果你还没有账号请 注册新账号