简体   繁体   English

Rails 7 实例变量未传递给 controller

[英]Rails 7 instance variable not passed to controller

On a new vanilla Rails 7 project, I'm trying to create a simple view with a form that, when submitted, renders the same view with some additional information.在一个新的 vanilla Rails 7 项目中,我试图创建一个带有表单的简单视图,该表单在提交时会呈现带有一些附加信息的相同视图。

Here's my controller:这是我的 controller:

class MyController < ApplicationController
  def index
    puts "got index request"
    @message = "type something into the form"
  end

  def create
    puts "got create request"
    @message = "hello world"
    render :index
  end
end

Here's index.html.erb :这是index.html.erb

<p style="color: green"><%= notice %></p>

<h1>New search</h1>

<%= render "form" %>

<p>Message is <%= @message.inspect %>.</p>

I expected the view to say Message is "hello world".我希望视图说Message is "hello world". since the instance variable should've been overwritten.因为实例变量应该被覆盖。 However, it still says Message is "type something into the form".但是,它仍然说Message is "type something into the form". . .

I tried 3 variants of render and got the same result:我尝试了 3 种render变体并得到了相同的结果:

  • render:index
  • render:index, message: @message
  • render:index, locals: {message: @message}

I can see from the server logs that the create method is getting called on submit (it prints got create request ) and it is rendering the index view (i see index.html.erb in the log);我可以从服务器日志中看到create方法在提交时被调用(它打印got create request )并且它正在呈现索引视图(我在日志中看到index.html.erb ); it's just that the instance variable isn't getting passed through.只是实例变量没有被传递。

Why isn't the instance variable passed to the view?为什么不将实例变量传递给视图? Is something getting cached?有什么东西被缓存了吗? What should I do instead?我应该怎么做?

While it's technically explained here:虽然这里有技术上的解释:
https://turbo.hotwired.dev/handbook/drive#redirecting-after-a-form-submission https://turbo.hotwired.dev/handbook/drive#redirecting-after-a-form-submission

Sometimes it's hard to connect all the dots in Turbo .有时很难将Turbo中的所有点连接起来。

# app/controllers/messages_controller.rb
class MessagesController < ApplicationController
  def index
    @message = "type something into the form"
  end

  def create
    @message = params[:form_message]
    render :index
  end
end

# app/views/messages/index.html.erb
<%= form_with url: "/messages" do |f| %>
  <%= f.text_field :form_message %>
  <%= f.submit %>
<% end %>
<div id="message_id"><%= @message %></div>

If you submit this form you get this in the browser console:如果您提交此表单,您会在浏览器控制台中看到:

turbo.es2017-esm.js:2348 Error: Form responses must redirect to another location
    at O.requestSucceededWithResponse (turbo.es2017-esm.js:780:27)
    at I.receive (turbo.es2017-esm.js:543:27)
    at I.perform (turbo.es2017-esm.js:518:31)

status: :unprocessable_entity状态::不可处理的实体

After submitting a form, you have to redirect or render an error response, default is status 422 aka :unprocessable_entity :提交表单后,您必须重定向或呈现错误响应,默认状态为422又名:unprocessable_entity

render :index, status: :unprocessable_entity

render turbo_stream:渲染 turbo_stream:

Because forms are submitted as TURBO_STREAM format, you can render turbo_stream response:因为 forms 以TURBO_STREAM格式提交,您可以渲染turbo_stream响应:

respond_to do |format|
  format.html { render :index, status: 422 }
  format.turbo_stream do
    render turbo_stream: turbo_stream.update(:message_id, params[:form_message])
  end #    ^       helper^      action^      ^id          ^content
end   # response format

This is probably the preferred way.这可能是首选方式。


turbo_frame turbo_frame

Put it in a frame.把它放在一个框架中。 Not as efficient as turbo_stream, because it renders the whole page on the server, but on the front end effect is the same, only message is updated:不如 turbo_stream 高效,因为它在服务器上渲染整个页面,但在前端效果是一样的,只更新消息:

<%= form_with url: "/messages", data: { turbo_frame: :message_id } do |f| %>
  <%= f.text_field :form_message %>
  <%= f.submit %>
<% end %>

<%= turbo_frame_tag :message_id do %>
  <%= @message %>
<% end %>

Just render:index works fine.只是render:index工作正常。


method: :get方法::获取

Send GET request instead:改为发送GET请求:

# app/views/messages/index.html.erb
<%= form_with url: "/messages", method: :get do |f| %>
...

# app/controllers/messages_controller.rb
def index
  @message = params[:form_message] || "type something into the form"
end

override turbo render覆盖涡轮渲染

Because the server is actually rendering a response and sending it back, we can override turbo rendering logic and just render the response instead of complaining:因为服务器实际上正在渲染响应并将其发回,所以我们可以覆盖涡轮渲染逻辑并只渲染响应而不是抱怨:

// app/javascript/application.js

document.addEventListener("turbo:before-fetch-response", async (event) => {
  if (event.target.hasAttribute("data-render-post")) {
    event.preventDefault()
    const response = new DOMParser().parseFromString(await event.detail.fetchResponse.responseHTML, "text/html")
    // you're free, do what you like here
    document.body.replaceWith(response.body)
  }
})
# app/views/messages/index.html.erb

# i imagine you don't want to do that for every form
#                                       vvvvvvvvvvvvvvvvv
<%= form_with url: "/messages", data: { render_post: true } do |f| %>
...

redirect_to重定向到

The usual redirect then render will work.通常的重定向然后渲染将起作用。 You can also have turbo_stream response in redirected action.您还可以在重定向操作中使用turbo_stream响应。 Or turbo_frame_tag in the template if form is targeting a turbo_frame:或者模板中的turbo_frame_tag如果表单针对的是 turbo_frame:

def create
  redirect_to message_path(1, form_message: params[:form_message])
end

def show
  @message = params[:form_message]
  respond_to do |format|
    format.html
    format.turbo_stream { render turbo_stream: turbo_stream.update(:message_id, @message) }
  end
end

And it behaves the same as described above, there is just an extra redirect in between.它的行为与上面描述的一样,只是两者之间有一个额外的重定向。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM