有没有办法在 fetch 调用后呈现 js.erb ?

Is there a way to render a js.erb after a fetch call?

在我的 Rails 应用程序中,我想在 fetch 调用后呈现一个 js.erb 部分,但由于某些我不明白的原因,它不起作用。如果你能帮助我,那就太好了。

在一个视图中,一个事件触发了这个基本上执行获取请求的函数:

function updateState(id, state){
      const csrfToken = document.querySelector('meta[name="csrf-token"]').attributes
          .content.value;
      fetch(window.location.origin + "/update_state", {
          method: "POST",
          headers: {
            'Accept': "JS",
            "Content-Type": "application/json",
            "X-CSRF-Token": csrfToken
          },
          body: JSON.stringify({'id': id, 'state': state}),
          credentials: "same-origin"
        })
}

然后在我的控制器中:

  def update_state
    @model = Model.find(params[:id])
    authorize @model
    @model.update(state: params[:state])

    respond_to do |format|
      format.html { redirect_to xxx }
      format.js
    end
  end

在我的 js.erb 文件中:

console.log('hello');

但是,在这种情况下,我从服务器收到错误消息: ActionController::UnknownFormat 在行 'respond_to 做 |format| 我感觉服务器不理解获取的 header: 'Accept': "JS"

当我查看服务器的日志时:

Started POST "/update_state" for 127.0.0.1 at 2019-04-04 11:22:49 +0200
Processing by ModelsController#update_state as JS

但是我觉得Rails不认识。我该怎么办?

我也在控制器中尝试过这个:

  def update_state
    @model = Model.find(params[:id])
    authorize @model
    @model.update(state: params[:state])
    render 'update_state.js', layout: false
  end

这不会触发错误。我在客户端收到了 js.erb。但是,它没有执行(console.log不执行)。

你有什么想法吗?非常感谢您的帮助。

编辑:查看编辑以获取有关手动触发 .js.erb 文件的完整解决方案。

有同样的问题,这里实际上有两个问题。你是对的,你的 headers 不是。这些 headers 触发 .js.erb 对我有用并且看起来很正确:

'dataType': 'json',
'contentType': 'application/json; charset=utf-8',
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': - your CSRF method to get the token -

但其次要触发 js 响应,您的 respond_to 块 :

respond_to do |format|
  format.html { redirect_to xxx }
  format.js
end

必须先有 format.js。由于 rails 对 HTML 有响应,它将首先响应 HTML 并且似乎甚至不寻找下一个格式块。

respond_to do |format|
  format.js
  format.html { redirect_to xxx }
end

希望对您有所帮助:-)

编辑 2018 年 11 月 13 日

大家好,好久不见!同样的问题又发生在我身上,我之前的回复并没有解决问题。与您一样,呈现了 .js.erb 文件但没有任何反应,并且未显示 console.log。所以我创建了一个 link_toremote: true 触发了我的 .js.erb 文件并比较了请求。一切都一样,除了一件事:请求的类型。一个是抓取,另一个是 xhr.js.erb 文件似乎仅由 xhr 请求触发。所以解决方案是构建一个旧的 xhr 请求而不是获取,如下所示:

    const url = 'your_custom_path' // Build the url to fetch
    const csrf = document.querySelector('meta[name="csrf-token"]').getAttribute('content')

    let xhr = new XMLHttpRequest();

    // Will be a GET request here
    xhr.open('GET', url, false);
​    
    // Setting the headers
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.setRequestHeader('X-CSRF-Token', csrf);
    xhr.setRequestHeader('dataType', 'script');
​
    // What to do with the response
    xhr.onload = function() {
      let responseObj = xhr.response
      eval(responseObj) // You need to eval() manually the response for it to be executed
    }
​
    // Sending the request
    xhr.send()

希望对您有所帮助,即使是在几个月后! ;)

我反过来管理。我从未成功 js.erb.

因此,我使用带有文本响应的提取调用:

function updateState(id, state){
  const csrfToken = document.querySelector('meta[name="csrf-token"]').attributes
      .content.value;
  fetch(window.location.origin + "/update_state", {
      method: "POST",
      headers: {
        'Accept': "JS",
        "Content-Type": "application/json",
        "X-CSRF-Token": csrfToken
      },
      body: JSON.stringify({'id': id, 'state': state}),
      credentials: "same-origin"
    }).then(response => response.text())
      .then(data => {
       div.inserAdjacentHTML('XXX', data);
    });
}

然后在 controller/action:

  def update_state
    @model = Model.find(params[:id])
    authorize @model
    @model.update(state: params[:state])
    render 'update_state.js', layout: false, content_type: "text/plain"
  end

这样就可以了。希望对你有帮助。

我 运行 在 Rails 6 中使用 fetch 解决了这个问题。只需将 format.js 置于 format.html 上方即可解决我的问题。谢谢@holino。以下提取有效。

  fetch(event.currentTarget.dataset.destroyPath, {
    method: 'DELETE',
    dataType: 'script',
    credentials: 'include',
    headers: {
      'X-CSRF-Token': csrf
    },
  })