Search code examples
ruby-on-railssimple-formturbolinksujsruby-on-rails-6

Rails 6: form with remote: true and Turbolinks not showing flash after redirect


I have <%= simple_form_for @offer, remote: true %> with Turbolinks enabled. I'm trying to redirect and show success flash with something like this in my update action:

respond_to do |format|
  if @offer.update(offer_params)
    flash[:success] = "#{@offer.name} #{t(:updated)}"
    format.html { redirect_to seller_offers_path }
    format.js
  else
    format.html { render :edit }
  end
end

My update.js.erb has code not related to redirect or flash:

$(".ibox-content.actions").removeClass('sk-loading');
$('.sk-spinner').css("display", "none");

In application.html.erb I can render flash with this:

<div class="row">
  <% flash.each do |message_type, message| %>
     <%= content_tag(:div, content_tag(:strong, message), id: "flash_message",
                   class: "text-bold alert alert-#{message_type}") %>
  <% end %>
</div>

At the moment my form is saved, however no redirect.

When I add Turbolinks.visit(<%= seller_offers_path %>) in update.js.erb form is saved, redirect is done, however there is no flash.

Is there a way I can have flash shown on redirected page with Turbolinks and remote: true , please? So far I've tried this suggestion, however it works only for showing flash on same page - it doesn't work with redirect_to.

Update

This does redirect, however no flash:

respond_to do |format|
  if @offer.update(offer_params)
    format.js { redirect_to seller_offers_path, success: "#{@offer.name} #{t(:updated)}" }
  else
    format.html { render :edit }
  end
end

Update 2

At the moment I'm trying to implement something like Turbolinks.visit(url, { keep: 'flash' }); as mentioned in docs, however doesn't seem to be working with Turbolinks 5.2

Update 3

I've removerd update.js.erb and in my controller I have

def update
  if @offer.update(offer_params)
    flash[:success] = "#{@offer.name} #{t(:updated)}"
    redirect_to seller_offers_path
  else
    render :edit
  end
end

Redirect is done as expected, however still no flash displayed on index. I can print my flash at the end of update action:

#<ActionDispatch::Flash::FlashHash:0x00007f180d2a1c60 @discard=#<Set: {}>, @flashes={"success"=>"My offer updated!"}, @now=nil>

however at the end of index action (where I redirect_to) it is empty:

#<ActionDispatch::Flash::FlashHash:0x00007f180eb3b780 @discard=#<Set: {}>, @flashes={}, @now=nil>

Looks like my flash is not kept for next request. Is it because of Turbolinks, or other some reason? I'd be happy for any hint.


Solution

  • Redirecting after remote form submissions

    When you use forms that submit via JS (e.g. remote: true or the default form_with), Rails will lookup and render the conventional js response (in this case offers/update.js.erb) and ignore any others, such as the html redirect.

    Your original update.js.erb did not include any code to perform the visit to the redirect location, and so it only removed the class name and hid the spinner.

    As you've figured out, the js response needs to make a Turbolinks visit to the redirect location. It's also a good idea to clear the Turbolinks cache after a form submission, so your update.js.erb might look something like:

    $(".ibox-content.actions").removeClass('sk-loading');
    $('.sk-spinner').css("display", "none");
    Turbolinks.clearCache();
    Turbolinks.visit(<%= seller_offers_path %>);
    

    If you have the turbolinks-rails gem installed, and do not need to run any custom JS, like the first two lines above, then in your controller you can simply do:

    def update
      if @offer.update(offer_params)
        flash[:success] = "#{@offer.name} #{t(:updated)}"
        redirect_to seller_offers_path
      else
        render :edit # see note below*
      end
    

    You won't need update.js.erb; turbolinks-rails will automatically clear the cache and visit the redirect path. This is nice as it keeps your controller code tidy (no need for respond_to), and works for both HTML and JS requests.

    * It's worth noting that error responses from JS requests will still need to be handled manually. To do this, you may find this comment useful: https://github.com/turbolinks/turbolinks/issues/85#issuecomment-338784510

    Missing Flash

    I think the reason why your flash isn't showing in your updated code is because you are using the shorthand style of setting flash messages in redirect_to, which only supportsnoticeandalert`:

    redirect_to seller_offers_path, notice: '…' # sets flash[:notice]
    redirect_to seller_offers_path, alert: '…' # sets flash[:slert]
    redirect_to seller_offers_path, success: '…' # doesn't work
    

    If you wish to use your own flash key you'll need to set the flash explicitly:

    flash[:success] = "#{@offer.name} #{t(:updated)}"
    

    I should also add that the Turbolinks keep option is not supported in Turbolinks 5. I think it was a feature in Turbolinks 2 or 3. The Turbolinks README is the place for the most up-to-date docs.

    I hope that helps :)