Hello. I was wondering if there’s a way to perform a redirect after returning a Turbo stream response in Rails.
For instance, in the chat app from the Hotwire screencast, they say that one can use a Stimulus controller to clear the message form field, but if we have form errors we would have to clear those too; the new message frame kind of keeps unmodified, without a render or redirection.
The following code from the chat app makes it think that it could return either a stream or a redirect when successfully saving the message model, but not both:
respond_to do |format|
if @message.save
format.turbo_stream # maybe something here to respond with a render/redirect too?
format.html { redirect_to @room }
else
format.html { render :new, status: :unprocessable_entity }
end
end
One use case I can think of is to clear the form and its errors by redirecting the frame to the same path (this would avoid the need to create Stimulus controllers to clear form fields/errors). Another use case is to render a “New comment” button which replaces its frame to the comment’s form when clicked, and redirect back to the new comment button when the form submission is successful (apart from using Turbo streams to broadcast the new comment and rendering it in another frame, like the chat app example).
My workaround to this is to save the redirect path as a data attribute in the DOM and create a Stimulus controller to redirect using Turbo.visit, like so:
import { Controller } from "stimulus";
import { Turbo } from "@hotwired/turbo-rails";
export default class extends Controller {
formRedirect(event) {
const redirectPath = this.element.dataset.redirect;
if (event.detail.success) {
Turbo.visit(redirectPath);
}
}
}
But what I would want is not to rely on custom JavaScript code, and for Turbo[-rails] to be able to return both a stream and render/redirect.
I’ve had a similar use case. A flash message (turbo_stream.erb) needs to be briefly shown inside a modal. Afterward, the page has to be redirected.
A solution for me was to add a data-controller=“redirect” and set the URL value on the wrapping div from flash message. The path is being passed as a local from the turbo_stream.erb file.
It works(loads the whole page of the path we provided), thanks @thomasvanholder . But not sure how do we load only the turbo frame of the path we provided.
The solution while retarded is pretty simple to this problem.
For a turbo_stream.erb response, you want the flash.now[:alert] way but for a redirect you need to set the flash[:alert] before redirecting. See the below code for a working example:
def create
authorize! :create, current_company.sell_orders.new
@sell_order = current_company.sell_orders.new(sell_order_params.compact_blank)
@out_car = current_company.cars.includes(:branch).find(params[:out_car_id])
if params[:trade]
trade_regnr = trade_params[:regnr].upcase
if (trade = Car.find_or_create_by_regnr(trade_regnr))
trade.update!(trade_params)
else
respond_to do |format|
format.turbo_stream do
flash.now[:alert] = t("flash.trade_car_not_found", regnr: trade_regnr)
render :new, status: :unprocessable_entity, alert: alert_message
end
end
end
end
if @sell_order.save
respond_to do |format|
format.turbo_stream do
flash[:success] = t("flash.sell_order_created")
redirect_to [:edit, @sell_order], status: :see_other
end
end
else
respond_to do |format|
format.turbo_stream do
flash.now[:alert] = t("flash.actions.create.alert", resource_name: "Säljorder")
render :new, status: :unprocessable_entity
end
end
end
end