出现Turbo Frame错误,提示“内容丢失”。

10

我正在尝试在我的Rails应用程序中使用turbo_frame_tag来管理一些任务。我为我的任务创建了一个脚手架。

我将要使用的页面包装在以下的turbo frame标记内:

<%= turbo_frame_tag "modal" do %>

      <h1>New task</h1>
      <%= render "form", task: @task %>
      <br>
      <div>
        <%# <%= link_to "Back to tasks", tasks_path %> %>
        <%= link_to "Cancel", "#", data: {
          controller: "modals",
          action: "modals#close"
        }, class: "cancel-button" %>
      </div>

<% end %>

在此输入图片描述

在我的主页index.html.erb文件中,我使用相同的标签向我的添加按钮添加了数据:

<%= link_to "Add", new_task_path, data: { turbo_frame: "modal" }, class: "btn btn-secondary", remote: true %>

模态框正常工作。当我在主页上单击“添加”按钮时,它会打开。当我尝试提交我的操作以创建新任务时,在我的终端上看到200响应,并将新任务添加到我的数据库中。

但是(也)我在主页上收到“内容丢失”的文本信息。页面没有重新加载。在开发者浏览器中,我得到了这个错误:

turbo.es2017-esm.js:3650 Uncaught (in promise) Error: The response (200) did not contain the expected \<turbo-frame id="modal"\> and will be ignored. To perform a full page visit instead, set turbo-visit-control to reload.
at c.delegateConstructor.throwFrameMissingError (turbo.es2017-esm.js:3650:15)
at c.delegateConstructor.handleFrameMissingFromResponse (turbo.es2017-esm.js:3646:14)
at c.delegateConstructor.loadFrameResponse (turbo.es2017-esm.js:3567:18)
at async c.delegateConstructor.loadResponse (turbo.es2017-esm.js:3441:34)

在此输入图片描述

我刚开始学习Ruby on Rails,一切都很新鲜。如果有人遇到过这样的问题并知道如何解决,我将不胜感激,希望能得到帮助和信息。

3个回答

9

这种行为是在PR中引入和记录的。请注意,Hotwire文档已经更新了推荐解决方案

您可以通过添加[turbo visit control tag](https://turbo.hotwired.dev/reference/attributes#meta-tags)来指定页面应该重定向而不是呈现Content Missing错误。

为了使它更容易,您可以利用一个turbo helper方法。

<!-- show.html.erb -->

<meta name="turbo-visit-control" content="reload">

<!-- or -->

<%= turbo_page_requires_reload %>

或者,您可以明确添加 data: {turbo_frame: "_top"} 来指定链接应该跳出 turbo_frame。


2
"data: {turbo_frame: "_top"}" 是我正在寻找的。谢谢。 - heyrolled

5

当表单提交后重定向时,没有与相同id的<turbo-frame>,因此会出现框架丢失错误。有关详细信息和我第一次尝试可重用解决方案,请参见:

https://dev59.com/RddopIgBRmDukGFELDwM#75704489

我不想再发一篇重复的答案了,但是我要提到一个 turbo:frame-missing 事件,你可以监听并处理这个错误:

document.addEventListener("turbo:frame-missing", (event) => {
  const { detail: { response, visit } } = event;
  event.preventDefault();
  visit(response.url);
});

使用简单的visit(response.url)有一个注意事项,那就是它将是对该URL的第二个请求(第一个请求是重定向)。

处理这个问题的理想位置应该是在重定向响应处,因为那里您会知道表单提交是否成功,并将重定向作为常规的Turbo.visit发送到框架外。您可以通过在turbo_stream中发送一些JavaScript来实现:

render turbo_stream: turbo_stream.append(
  request.headers["Turbo-Frame"],
  helpers.javascript_tag(
    "Turbo.visit('#{model_url(@model)}')",
    data: {turbo_temporary: true} # one time only; don't cache it
  )
)

为了让它看起来更漂亮,可以使用自定义的 Turbo Stream 操作来完成同样的事情。

创建自定义的turbo_stream.redirect操作

“redirect” 动作的标签如下所示:

<turbo-stream action="redirect" target="/url"></turbo-stream>

在前端中添加新的操作到Turbo来处理它:
https://turbo.hotwired.dev/handbook/streams#custom-actions

// app/javascript/application.js

import { Turbo } from "@hotwired/turbo-rails";

Turbo.StreamActions.redirect = function () {
  Turbo.visit(this.target);
};

// or using event listener

document.addEventListener("turbo:before-stream-render", (event) => {
  const fallbackToDefaultActions = event.detail.render;
  event.detail.render = function (streamElement) {
    if (streamElement.action == "redirect") {
      Turbo.visit(streamElement.target);
    } else {
      fallbackToDefaultActions(streamElement);
    }
  };
});

使用通用的action方法在控制器或模板中呈现:

turbo_stream.action(:redirect, model_url(@model))

您还可以在turbo_stream助手中添加redirect方法:

# config/initializers/stream_actions.rb

module CustomTurboStreamActions
  # turbo_stream.redirect(model_url(@model))
  def redirect url
    action(:redirect, url)
  end
end
Turbo::Streams::TagBuilder.include(CustomTurboStreamActions)

如何跳出框架的示例:

# app/controllers/*_controller.rb

def update
  respond_to do |format|
    if @model.update(model_params)
      format.turbo_stream do
        render turbo_stream: turbo_stream.redirect(model_url(@model)) 
      end
      format.html { redirect_to model_url(@model), notice: "Updated." }
    else
      format.html { render :edit, status: :unprocessable_entity }
    end
  end
end

有一件事我不太明白... Turbo 帧请求本身不会是一个 format.turbo_stream 请求,而是一个 format.html 请求,对吧?我认为我看到的就是这样... 我想使用重定向的方式跳出 turbo-frame。在那种情况下,我可以使用这种(优雅的)方法来扩展 turbo 的自定义操作吗? - gap
@当您提交表单时,它应该是一个turbo_stream请求。如果您只需要点击框架之外的区域,请在链接中添加data-turbo-frame="_top" - Alex
我只知道基于服务器端的决策(错误情况/重定向)需要退出。客户端不知道或假设这一点。 - gap
@gap 实际上,你应该能够使用 turbo_stream 响应 html turbo_frame 请求。如果不起作用,请使用你的设置发布另一个问题。 - Alex
1
这应该被添加到 turbo-rails 宝石中。 - Ben Trewern

2

我在从turbo-rails 1.3.3更新到1.4.0后遇到了相同的错误消息。看起来这是1.4.0中的破坏性变更。这是因为当前版本的turbo-rails假定所有操作都会导致turbo-frame内的更新。当然,并非总是如此,特别是如果turbo-frame的内容是搜索结果列表时。如果您希望turbo-frame内的链接更新整个页面,则需要将target="_top"添加到turbo-frame标记中。以下是一个完整的示例:

<turbo-frame id="supplier_contracts" data-turbo-action="advance" target="_top" >

这对我解决了问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接