使用 .js.erb 和 Rails 实例变量响应 AJAX Rails 请求

Responding to AJAX Rails request with .js.erb and Rails instance variable

我正在为 Rails 中的 JS AJAX 请求而苦苦挣扎。有一个官方指南 here,但我在将它与 ES6 JS 匹配时遇到了一些困难。提出请求后,我无法将事情传回我的前端。

我有一个 JS window.onload 调用,因为我试图找到用户的屏幕尺寸(除其他外)并将其传递回 Rails:

    let xhttp = new XMLHttpRequest();
    const url = "/users";
    xhttp.open("POST", url);
    // Some other things added to it...
    xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 201) {
            console.log(this.responseText);
       }
    };
    xhttp.send(JSON.stringify({user_info: userInfo}));

它正在向 /users 发布有关会话的一些信息。这一切都很好。请注意跟踪响应的 console.log,我们将在稍后讨论。

在我的 Rails 控制器中:

def create
  user_info = params[:user_info].permit!
  user_info = user_info.to_s
  @fingerprint_user =  User.find_or_create_by(fingerprint: user_info)
  respond_to do |format|
    # NOTE: I have tried a few things here
    # format.html { redirect_to @fingerprint_user, notice: "Successfully identified user by fingerprint." }
    # format.js
    format.json { render json: @fingerprint_user, status: :created, head: :ok }
  end
end

JSON 发送器工作正常。上面JS中的console.log正确console.logs接收到JSON。请求以 201 和 JSON 形式的 @fingerprint_user 实例变量响应。

我的问题是返回带有实例变量的 ERB JS 。如指南所示,我尝试添加 format.js。然后,请求returns一个200,以及我的views/users/create.js.erb文件的内容:

console.log("hello");

然而,它实际上并不是记录到控制台。

最后,我尝试了所有格式字段(js、html 和json)。这是我的 show.html.erb:

<p>Got user: <%= @fingerprint_user.to_s %> </p>

这是一个更好的 views/users/create.js.erb 文件,其中 fingerprint 是我 index.html.erb 中的 div:

console.log("hello");
$("<%= escape_javascript(render @fingerprint_user) %>").appendTo("#fingerprint");

再一次,响应是 200 和适当的 html,但这并没有呈现在页面上。

请求 AJAX 请求 JavaScript 与请求 JSON 不同。您实际上不是请求一些数据并解析它,而是加载数据,然后通过各种技巧将其评估到当前页面上下文中,例如将脚本标签附加到文档中。这是实际的 Rails UJS implementation:

processResponse = (response, type) ->
  if typeof response is 'string' and typeof type is 'string'
    if type.match(/\bjson\b/)
      try response = JSON.parse(response)
    else if type.match(/\b(?:java|ecma)script\b/)
      script = document.createElement('script')
      script.setAttribute('nonce', cspNonce())
      script.text = response
      document.head.appendChild(script).parentNode.removeChild(script)
    else if type.match(/\b(xml|html|svg)\b/)
      parser = new DOMParser()
      type = type.replace(/;.+/, '') # remove something like ';charset=utf-8'
      try response = parser.parseFromString(response, type)
  response

这基本上就是十年前我们使用 AJAX 跨域调用 JSONP 来绕过当时浏览器的限制的方式。

您可以在 "raw ajax request" 中模拟同样的事情:

let xhttp = new XMLHttpRequest();
const url = "/users";
xhttp.open("POST", url);
// Some other things added to it...
xhttp.onreadystatechange = function() {
  if (this.readyState == 4 && this.status == 201) {
    let script = document.createElement('script');
    script.innerHTML = data;
    document.querySelector('head').appendChild(script);
  }
};

但坦率地说 js.erb 是个糟糕的主意。它使服务器端和客户端的责任变得一团糟,并使您的代码很难理解和推理,并且它将 JS 从 assets/webpack 管道中移出,并进入了一些过程垃圾脚本视图中。使用它的唯一可能原因是你可以多么懒惰地使用 Rails UJS,并且仍然向你的应用程序添加一些 ajax。

如果你正在编写一个 ajax 处理程序,那么只需 return 一大块 html(在 json 对象中或作为 html)和而是将其附加到 DOM。