Forms without redirect

They recently merged the fact that 4XX or 5XX form responses will be displayed without the need for a redirect.

Not sure what status code devise returns but it should be easily overloadable if necessary

I’ve got everything working with the latest Turbo and recorded a screencast on how to set it up. Nice part is we don’t have to change any Devise controllers or views. :+1:

Code for it:

# config/initializers/devise.rb
# frozen_string_literal: true

class TurboFailureApp < Devise::FailureApp
  def respond
    if request_format == :turbo_stream
      redirect
    else
      super
    end
  end

  def skip_format?
    %w(html turbo_stream */*).include? request_format.to_s
  end
end

class TurboController < ApplicationController
  class Responder < ActionController::Responder
    def to_turbo_stream
      controller.render(options.merge(formats: :html))
    rescue ActionView::MissingTemplate => error
      if get?
        raise error
      elsif has_errors? && default_action
        render rendering_options.merge(formats: :html, status: :unprocessable_entity)
      else
        redirect_to navigation_location
      end
    end
  end

  self.responder = Responder
  respond_to :html, :turbo_stream
end

Devise.setup do |config|

  # ==> Controller configuration
  # Configure the parent class to the devise controllers.
  config.parent_controller = 'TurboController'

  # ==> Warden configuration
  config.warden do |manager|
    manager.failure_app = TurboFailureApp
  end
end
5 Likes

@excid3 @woto just done some more playing around and removing my controller method override and just including the turbo_frame_tag call in the view, into which goes the form seems to have done the trick.

@excid3 I like your approach too - we now have a choice as to which suits best :slight_smile:

1 Like

I didn’t want to have to modify any views or controllers, so this route worked pretty well. My original solution was to add turbo_stream.erb templates. Always good to have a few options. :+1:

Working on this issue adding a helped, then I just need to disable caching I think (because my auth controller is redirecting to the same page on error). I’ll write what happens when I get it working.

Beta 2 is out, addressing the issue of 4xx and 5xx HTML error responses.

I’ve fixed it with a combination of @excid3 and data: {turbo: false}:
my inititalizer

BTW @excid3 you’re missing
config.navigational_formats = ["*/*", :html, :turbo_stream]
change in the Code block. You do it in the video, just not in the text.

Lol, yeah this might fix my problem entirely, but I fixed it using turob streams which allowed me to set a response that would replace the form with the errors (non websockets, just HTTP).

I ended up with this code based on @excid3 example:

class TurboFailureApp < Devise::FailureApp
  def respond
    if request_format == :turbo_stream
      recall
      # turbo wants a 422 https://turbo.hotwire.dev/handbook/drive#redirecting-after-a-form-submission
      response[0] = 422
    else
      super
    end
  end

  def skip_format?
    %w[html turbo_stream */*].include? request_format.to_s
  end
end

Didn’t need to do any of the parent controller stuff. Has been working well in production with our turbo-ios/turbo-android apps too.

Okay I lied a bit. That code was enough to get a login form (SessionsController) working with native Turbo.

For other forms (eg. RegistrationsController) I’d suggest using Respond with 422 Unprocessable Entity for non-GET HTML/JS requests with errors by carlosantoniodasilva · Pull Request #223 · heartcombo/responders · GitHub over adding a new TurboController.

Hey all, this is Carlos, developer maintaining devise/responders (and the heartcombo libs). I appreciate you all get things working here with Hotwire, I haven’t been able to follow along on the first few weeks but I’d be happy to get things working more out of the box with responders first, & devise second in the coming weeks.

@ghiculescu thanks for linking to that PR! Please let me know if you (or anyone else really) are able to test it out and if you bump into any issues, before I move on with it and release a new version in the upcoming days. It’s a breaking change on responders so it will likely result in a major bump.

That should also help make Devise work mostly out of the box as well with Hotwire, at least for most of its form interactions, I think. I’ll have to circle back on it and see what’s necessary to change for turbo streams, like the changes that @excid3 showed above (thanks for that!). To be honest I haven’t been able to test things out with Hotwire myself yet, so it’s been helpful to read through your conversation here, and any help you can provide with testing is appreciated.

Thanks all.

3 Likes

@pjo336 I have the exact same issue as you described. Did you figure out a work around?

Ok, I got this to work, but I’m not using target="_top". I’m using a Stimulus controller and listening for the turbo:submit-end event and then performing a Turbo.visit on a successful form submission. Invalid form errors are handled by Turbo by returning a 422 response.

The code if anyone is interested:

import { Controller } from "stimulus"
import * as Turbo from "@hotwired/turbo"


export default class extends Controller {
  connect() {
    this.element.addEventListener('turbo:submit-end', function (event){
        if (event.detail.success) {
            Turbo.visit(window.location.href)
        }
    })
  }
}

Then my turbo-frame:

<turbo-frame id="contact-name-edit" data-controller="form"></turbo-frame>
2 Likes

For everyone in this thread using Devise, can you please give these two PRs a try:

If you test these, please also remove any changes you made to your devise config, failure app, etc. Combined, these should make Devise work out of the box with Turbo Drive and Turbo Native. Please leave feedback on either/both PRs if you have any issues or suggestions! :slight_smile:

1 Like

I’ve added

gem "devise", git: "https://github.com/ghiculescu/devise.git", branch: "error-code-422"
gem "responders", git: "https://github.com/heartcombo/responders.git"

removed TurboController, this line, and all the data: {turbo: false} I had in overriden views.

Seems to work exactly how it should @ghiculescu!

But I still needed config.navigational_formats = ["*/*", :html, :turbo_stream] in for sign in to show errors. And maybe other things, but here’s where I noticed the difference.

Thank you!

2 Likes

Thanks for testing @miharekar and good pickup! I missed that I’d changed that in my Devise config. Can you please have another look now?

@ghiculescu yup, looks like it’s working as it should now :partying_face:

These gem locations seem to work well for me. Hopefully we can see new versions of the gems up soon.

@ghiculescu Still waiting… :sweat_smile:

Haha yep. Production gemfile:

gem "devise", github: "ghiculescu/devise", branch: "error-code-422" # https://github.com/heartcombo/devise/pull/5340 not yet merged
gem "responders", github: "heartcombo/responders" # https://github.com/heartcombo/responders/pull/223 not yet released

does this issue has any progress ? devise is the de-facto solution for many rails apps; i guess it’d helped many people