【问题标题】:Responding to AJAX Rails request with .js.erb and Rails instance variable使用 .js.erb 和 Rails 实例变量响应 AJAX Rails 请求
【发布时间】:2020-05-02 16:28:42
【问题描述】:

我在 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。然后,请求返回一个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,但它没有呈现在页面上。

【问题讨论】:

    标签: ruby-on-rails ajax xmlhttprequest erb ruby-on-rails-6


    【解决方案1】:

    对 JavaScript 的 AJAX 请求执行请求与请求 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
    

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

    您可以在“原始 ajax 请求”中模拟相同的内容:

    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 从资产/webpack 管道中移出并进入了少量的过程垃圾脚本视图。使用它的唯一可能原因是你可以多么懒惰地使用 Rails UJS 并且仍然向你的应用程序添加一些 ajax。

    如果您正在编写 ajax 处理程序,则只需返回一段 html(在 json 对象或 html 中)并将其附加到 DOM。

    【讨论】:

    • 这成功了!非常感谢!问题在于我如何使用 JS 处理响应,这是有道理的。根据你的建议,我也放弃了js.erb。在我的onreadystatechangedocument.querySelector('#fingerprint').innerHTML = this.response;。在我的控制器中,我只使用了 format.html 和 json,并将 html 更改为 render 而不是重定向。我将我的 html 文件名更改为 _user.html.erb
    • w.r.t. “坦率地说,js.erb 是一个可怕的想法”——我认为这对我的用例来说是有意义的。我有外部JS,不需要两个地方都有JS。我得到的印象是 js.erb 应该仅在您 1) 没有其他 JS 2) 仅用于特定页面时使用,所以它看起来有点狭窄。再次感谢!
    猜你喜欢
    • 2013-12-03
    • 2018-01-21
    • 2010-10-31
    • 1970-01-01
    • 2011-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-01
    相关资源
    最近更新 更多