Yup. It does work with 303 if the form is out of the frame. I am trying to combine the both. Having the grace of frames to show the validation errors without re-rendering the whole page. But at the same time break out of a frame when the form submission is successful.
I understand that maybe the frames are not designed to handle this use case. But I am curious to know how others are handling situations like this
<%= turbo_frame_tag "new_discussion_message", target: "_top" do %>
<%= form_with model: [@discussion, @message], class: 'new-discussion__form push-double--top d-flex flex-column' do |f| %>
<%= f.hidden_field :creator_id %>
<%= f.rich_text_area :content, data: { controller: "signatured-input", signatured_input_signature: Current.user.signature.to_s } %>
<footer class="new-discussion__footer">
<p class="push--bottom" style="font-size: 1.8rem;">
<%= image_tag @discussion.parent.client.avatar_url %>
<%= @discussion.parent.client.name %> will receive your message in their email.
</p>
<%= f.button class: 'btn btn-primary btn--with-icon', type: 'submit', data: { "disable_with": "Sending…" } do %>
Reply
<% end %>
</footer>
<% end %>
<% end %>
def create
@message = @discussion.messages.new(message_params)
respond_to do |format|
if @message.save
format.any(:html, :js) { redirect_to @discussion.parent.client, flash: { follow_up_with_new_task: true } }
end
end
end
This, however, doesn’t cover the case where @message.save fails the validations. Though you could send a turbo_stream on validation failure to render the errors in your frame.
I haven’t played much with Turbo yet but couldn’t you handle this scenario with Turbo Stream instead of Turbo Frame? You just display the login error message with a Turbo Stream and redirect with Turbo Drive if the login is successful.
The pattern of re-rendering a form with validation errors and redirecting on success is very common - is there a reason Turbo Drive prevents this? What is the simplest workaround here?
See also: https://github.com/hotwired/turbo/issues/22. This seems to be quite a serious breaking bug, especially as there is no easy way to disable Turbo in forms (data-turbo=“false” does not work with links, and the Turbo API does not appear to allow insertion of HTML as with the old Turbolinks.controller.cache.put method).
Either the JS will raise if the frame has a target _top and the backend does not redirect, either the frame has no target but nothing will happen on redirect.
Try adding data-turbo-frame="_top" to your form(not the frame). I tried this and it works.
If you’re using rails form builder: form_with(..., data: {'turbo-frame' => '_top'})
Adding data-turbo-frame="_top" to your form has bad side effect that page jump to top after form submission. Especially visible on long pages on mobile.
now that the new version of turbo is listening to 422 responses, for me it works like this:
I have a standard rails form (not wrapped in a turbo_frame_tag), like so:
= form_with(model: user) do |form|
...
then, a standard rails controller with an action, like so:
def create
@user = User.new(user_params)
if @user.save
redirect_to users_path, status: 303, notice: 'User was successfully created.'
else
render :new, status: :unprocessable_entity
end
end
The important bits are the 303 status code for the redirect, and the status :unprocessable_entity to rerender the form with validation errors.
If you have a setup like this, you don’t need to wrap your forms into a frame. Turbo takes care of rerendering the form with validation errors, and you can redirect to any path you’d like!
So the best solution that I have found was this. Do not use turbo_frame_tag. The redirect process is not working the way it should. Meaning, when you redirect it should redirect to the new page, that’s the intent of that command.
Right Turbo, on redirect, tries to replace a turbo_frame_tag that it came from. And if it doesn’t find it after it processes HTML from redirected page, it fails to “redirect” to the new page. Maybe, the solution should be this: if frame_tag is present, replace it, if not replace the whole page.
For now, here’s the workaround that works best:
Instead of using turbo_frame_tag. create a new.turbo_stream.erb file for your form.
Wrap your form into a div with id. And use turbostream to replace that part of the page.
_form.html.erb
<%= tag.div "new_board_form" do %>
<%= form_with board, url: new_board_path do %>
<% end %>
<% end %>
new.turbo_stream.erb ← to render errors
<%= turbo_stream.replace "new_board_form" do %>
<%= render "form", board: @new_board %>
<% end %>
And then simple redirect on successful form completion
boards_controller.rb
if @new_board.save
redirect_to board_path (@new_board) }
else
render :new
end
Agrees with this solution. I’m using same. The most annoying part is that I have to write turbo_stream.replace. WTF? why Turbo then?! I’ve been using same technique with jQuery. Why then I need Turbo . I hope it will be changed in future Turbo versions.
Is this still a problem in the current version of Turbo? I’m running into this same issue. I want to re-render within the turbo frame except for when I want to break out and redirect. Setting the container frame target to _top doesn’t work because it applies to every request. I was thinking it would be nice if I could change/specify the turbo frame in the response header, or something like this: