如何让删除链接响应Rails 7中的turbo_stream和html?

hel*_*ion 5 ruby-on-rails turbo ruby-on-rails-7

我在 Rails 7 中有一个删除链接,可以使用 Turbo_stream 或 html 正常运行,但不是每一个都可以。

link_to 'delete', @object, data: { turbo_method: 'delete', turbo_confirm: 'Really?' }
Run Code Online (Sandbox Code Playgroud)

我从索引页调用此链接,该链接应使用turbo_stream 响应来删除记录并删除表行。索引页包含在turbo-frame 标签中。我还从显示页面调用此方法,其中 html 响应应删除记录并重定向回索引页面。显示页面未包含在 Turbo-Frame 标签中。

显示页面链接正确命中销毁操作并销毁记录——但是,它不会重定向。它实际上响应turbo_stream。如果我从销毁操作中删除 format.turbo_stream 块,则同一链接会正确命中 format.html 响应并重定向。该链接知道如何响应 format.html,但它会尝试响应 format.turbo_stream,即使该链接未包含在 Turbo-frame 标记中。

在 Rails 7 中,数据属性“turbo_method: 'delete'”会导致turbo_stream 调用。有没有办法告诉该链接响应 format.html?

当来自链接的传入响应是turbo_stream 时,如何获取显示页面上的链接以响应 format.html 并重定向?

Ale*_*lex 15

我最近多次提到标题,所以我将简短地介绍这部分

当您发送TURBO_STREAM请求时,优先使用的第一个格式是turbo_stream。如果您没有turbo_stream格式块或turbo_stream.erb模板,则html使用format。由于Turbo可以处理这两种响应,因此它在Accept标头中设置这两种类型,这决定了要运行的格式块。你可以从行动上看一下destroy

puts request.headers["Accept"]
#=> text/vnd.turbo-stream.html, text/html, application/xhtml+xml
#   ^                           ^
#   turbo is first in line      html is second
Run Code Online (Sandbox Code Playgroud)
def destroy
  @model.destroy

  respond_to do |format|
    format.turbo_stream { render turbo_stream: turbo_stream.remove(@model) }
    format.html { redirect_to models_url, notice: "Destroyed." }
  end
end
Run Code Online (Sandbox Code Playgroud)

获得turbo_stream响应

<%= link_to "Turbo destroy", model_path(model),
  data: {turbo_method: :delete}
%>

<%= button_to "Turbo destroy", model_path(model),
  method: :delete
%>
Run Code Online (Sandbox Code Playgroud)

获取html响应

Rails还可以忽略Accept标头并根据 url 扩展确定格式。Turbo 请求将以html/models/1.html响应。

<%= link_to "HTML turbo destroy", model_path(model, format: :html),
  data: {turbo_method: :delete}
%>

<%= button_to "HTML turbo destroy", model_path(model, format: :html),
  method: :delete
%>

# using a form field also works
# like `hidden_field_tag :format, :html` inside your form:
<%= button_to "HTML turbo destroy with format input", model_path(model),
  method: :delete,
  params: {format: :html}
%>
Run Code Online (Sandbox Code Playgroud)

我最不喜欢的选择turbo: false,哎呀:

<%= button_to "HTML rails destroy", model_path(model),
  method: :delete,
  data: {turbo: false}
%>
Run Code Online (Sandbox Code Playgroud)

使用 url 或表单参数做任何你想做的事情

<%= button_to "Turbo destroy with params", model_path(model),
  method: :delete,
  params: {redirect_to: "/anywhere/you/like"} # or maybe just true/false
%>
Run Code Online (Sandbox Code Playgroud)
def destroy
  @model.destroy

  respond_to do |format|
    # just pass a param and skip turbo_stream block
    unless params[:redirect_to]
      format.turbo_stream { render turbo_stream: turbo_stream.remove(@model) }
    end
    format.html { redirect_to (params[:redirect_to] || models_url), notice: "Destroyed." }
  end
end
Run Code Online (Sandbox Code Playgroud)

您还可以显式设置格式

# it doesn't have to be a callback, just has to happen before `respond_to` block.

before_action :guess_destroy_format, only: :destroy

def guess_destroy_format
  # this way you don't need `unless params[:redirect_to]` around turbo_stream
  request.format = :html if params[:redirect_to]

  # don't need to do anything extra if deleting from a show page
  request.format = :html if request.referrer.start_with?(request.url)
end
Run Code Online (Sandbox Code Playgroud)

https://api.rubyonrails.org/classes/ActionDispatch/Http/MimeNegotiation.html


获取带有标头的任何响应Accept

也许您需要隐藏丑陋的东西.html,或者您不想过多地干扰控制器。设置Accept标题并获取您需要的内容。请注意,Turbo将处理htmlTurbo_stream,但您必须自己处理任何其他响应:

// app/javascript/application.js

const Mime = {
  turbo_stream: "text/vnd.turbo-stream.html",
  html:         "text/html",
  json:         "application/json",
}

document.addEventListener('turbo:submit-start', function (event) {
  const {
    detail: {
      formSubmission: {
        fetchRequest: { headers },
        submitter: { dataset: { accept } },
      },
    },
  } = event

  if (Mime[accept]) {
    headers["Accept"] = Mime[accept]
  }
})
Run Code Online (Sandbox Code Playgroud)

用于data-accept设置类型:

// app/javascript/application.js

const Mime = {
  turbo_stream: "text/vnd.turbo-stream.html",
  html:         "text/html",
  json:         "application/json",
}

document.addEventListener('turbo:submit-start', function (event) {
  const {
    detail: {
      formSubmission: {
        fetchRequest: { headers },
        submitter: { dataset: { accept } },
      },
    },
  } = event

  if (Mime[accept]) {
    headers["Accept"] = Mime[accept]
  }
})
Run Code Online (Sandbox Code Playgroud)

  • 哇,亚历克斯!多么好的答案和解释啊。这有助于解释很多事情。:) (2认同)