7

Update Feb 23 - See github issue https://github.com/heartcombo/devise/issues/5446, this should now be fixed. My apologies to all here that since I discovered this and created the issue a year ago I've had zero time to code and respond (I code for fun alone and this year was too busy for me, that time will come again and I appreciate all of your help).

I have a new rails 7 app [rails new devisetest], a simple controller with static page and added devise [gem 'devise' + rails g devise user]. All defaults. Flash messages added to the application.html.erb as per devise instructions

Devise error messages are not being shown, but I see them being generated in the console (or at least, I see the rendered message but they don't show, and I see rendered for the _links partial and that does show) In another test using a quick scaffold (and having copied the devise views into the app but not modified them), flash messages show for the scaffold but still not for devise.

If I visit http://localhost:3000/users/sign_up and create a user with a too short password I see this in the log

Started POST "/users" for ::1 at 2021-12-24 08:02:10 +0000
Processing by Devise::RegistrationsController#create as TURBO_STREAM
  Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"email"=>"asdf@asdf.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Sign up"}
  TRANSACTION (0.1ms)  begin transaction
  User Exists? (2.7ms)  SELECT 1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "asdf@asdf.com"], ["LIMIT", 1]]
  TRANSACTION (0.2ms)  rollback transaction
  Rendering layout layouts/application.html.erb
  Rendering /home/sroot/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/devise-4.8.1/app/views/devise/registrations/new.html.erb within layouts/application
  Rendered /home/sroot/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/devise-4.8.1/app/views/devise/shared/_error_messages.html.erb (Duration: 0.7ms | Allocations: 582)
  Rendered /home/sroot/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/devise-4.8.1/app/views/devise/shared/_links.html.erb (Duration: 0.2ms | Allocations: 92)
  Rendered /home/sroot/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/devise-4.8.1/app/views/devise/registrations/new.html.erb within layouts/application (Duration: 3.9ms | Allocations: 2199)
  Rendered layout layouts/application.html.erb (Duration: 77.5ms | Allocations: 4429)
Completed 200 OK in 473ms (Views: 78.7ms | ActiveRecord: 3.1ms | Allocations: 15288)

View that should show error message. Devise links are shown but not flash messages

Can anyone point out my mistake please? Or suggest other steps I can follow to get closer to my error?

Thanks

Steve Root
  • 368
  • 3
  • 13
  • 1
    I have this problem too, and It's happening when I'm using the `javascript_importmap_tags` in the layout. if I change it to the regular `javascript_tag` it will work again. but the thing is I want to use `javascript_importmap_tags`, still couldn't find the solution – Zakaria Dec 31 '21 at 22:21
  • 1
    I created an issue on GitHub, I think for once this might not be me alone, https://github.com/heartcombo/devise/issues/5446 – Steve Root Jan 01 '22 at 08:41

8 Answers8

7

I am following the tutorials video https://gorails.com/episodes/devise-hotwire-turbo as well as tried it with this tutorial here:
https://medium.com/@nejdetkadir/how-to-use-devise-gem-with-ruby-on-rails-7-33b89f9a9c13

But the flash messages are not showing up. I added to my devise initializer:

# config/initializers/devise.rb

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

config.navigational_formats = ['*/*', :html, :turbo_stream]

config.warden do |manager|
  manager.failure_app = TurboFailureApp
  # manager.intercept_401 = false
  # manager.default_strategies(scope: :user).unshift :some_external_strategy
end

As well as created a devise controller:

# app/controllers/users/devise_controller.rb

class Users::DeviseController < 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
Alex
  • 16,409
  • 6
  • 40
  • 56
Sabrina
  • 309
  • 1
  • 14
5

If you just want it, "to work", you can add data: { turbo: false } to your devise form. This will prevent turbo from hijacking your form submissions with all it's automagic.

Credit to john-999 here https://github.com/heartcombo/devise/issues/5446#issuecomment-1083485163

In my case my views/devise/sessions/new.html.erb looked like:

    <%= form_for(resource, as: resource_name, url: session_path(resource_name), data: { turbo: false }) do |f| %>
      <% resource.errors.full_messages.each do |msg| %>
        <div><%= msg %></div>
      <% end %>

      <div class="mb-3">
        <%= f.label :email, class: "form-label" %>
        <%= f.email_field :email, autofocus: true, autocomplete: "email", class: 'form-control' %>
      </div>

      <div class="mb-3">
        <%= f.label :password, class: "form-label" %>
        <%= f.password_field :password, autocomplete: "current-password", class: 'form-control' %>
      </div>

      <div class="mb-4 mt-4">
        <%= f.submit t('.sign_in'), class: "btn btn-lg btn-primary" %>
      </div>
    <% end %>
genkilabs
  • 2,966
  • 30
  • 36
  • This is quick, easy, and works. I post some additional links which may help: [this](https://www.youtube.com/watch?v=XJ27X06GVrI) video explainer, and [this](https://github.com/corsego/74-rails7-devise/commit/f5d5656cb5b699ed55a6d8ba72fab67fcf10e23d) commit showing which views need editing and how. – stevec May 24 '22 at 14:06
4

I have the same issue. It is someway linked with the compatibility of Rails 7.0 and Devise 4.8.1. I am unable to sign up a user. The sqlite3 database also remains empty, so I don't think its just the issue of flash messages and the users aren't being entered. Some people have addressed various issue with Rails 7 / Devise by adding the following to config/initializers/devise.rb:

config.navigational_formats = ['*/*', :html, :turbo_stream]
Divij Jain
  • 89
  • 8
  • 2
    I'm traveling so haven't tried, but there's now steps to solve on the GitHub issue and a link there to a gorails video, visit https://github.com/heartcombo/devise/issues/5446 – Steve Root Jan 04 '22 at 19:35
  • This `navigational_formats` worked for me, thanks (`Rails 7.0.2.3`, `Devise 4.8.1`) but Sabrina's proposal actually fixed also the rest of the devise pages. – Adit Saxena Mar 31 '22 at 07:53
2

I added in config/initializers/devise.rb, at the top this:

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

inside devise.setup do |config|, this:

  # ==> Navigation configuration
  # Lists the formats that should be treated as navigational. Formats like
  # :html, should redirect to the sign in page when the user does not have
  # access, but formats like :xml or :json, should return 401.
  #
  # If you have any extra navigational formats, like :iphone or :mobile, you
  # should add them to the navigational formats lists.
  #
  # The "*/*" below is required to match Internet Explorer requests.
  config.navigational_formats = ['*/*', :html, :turbo_stream]

  # ==> 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

then I created a controller in app/controllers/turbo_controller.rb whit this:

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
ericksk
  • 41
  • 4
1

I think the best answers are here by Alex Flash is not displayed in the same view in Rails

and again by Alex Rails 7 signup form doesn't show error messages

This is also true without devise, the array of error messages belong to Active Record and associated to the Active Record @user object failing validation in the model which inherits from ApplicationRecord and ActiveRecord, not devise. Even if you created a user model, controller, views and all the actions from scratch, the error messages will still be there when validations fail in the model, yet not showing up in the view. Even the flash messages don't show. Instead in your controller if user fails to save, then, in the else statement to redirect to request referrer with a flash error message, maybe like redirect_to request.referrer, flash: {error: @user.errors.full_messages}, in the create user method or update, but error messages don't seem to show if you are rendering back to new action unless server responds with 422, so you can do same as scaffolds render 'new', status: :unprocessable_entity, flash: {error: @user.errors.full_messages} which leads to a server response of 422, this will work . This also applies to success message if @user is saved in the DB. or updated you can just show redirect_to user_url(@user), notice: " user was successfully created / updated."

You will notice in the browser console, if you register with invalid fields, you won't get the error full_messages and you'll notice your browser console showing: Error: Form responses must redirect to another location, that's if you're rendering back to new action, if the object fails to save in your create action.

It seems like if you go to application.html.erb and remove <%= javascript_importmap_tags %>, save the file and try to register with invalid fields, you will get all your @user.errors.full_messsages keys and values. Not a solution.

or better leave javascript_importmap_tags alone, and you can add data: { turbo: false } in your form_with. But still not a sustainable solution.

or like others suggested if you're using devise for registering users in initializers.rb you can add config.navigational_formats = ['*/*', :html, :turbo_stream]

Elias Glyptis
  • 470
  • 5
  • 9
0

Using Devise Gem alerts and flash messages are not shown in rails 7 because of Turbo Stream

I added in config/initializers/devise.rb, at the top this:

# config/initializers/devise.rb

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

# I added in config/initializers/devise.rb, at the top this:

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

# Add these lines inside devise.setup do |config|, this:

config.parent_controller = 'TurboController'

config.navigational_formats = ['*/*', :html, :turbo_stream]

config.warden do |manager|
  manager.failure_app = TurboFailureApp
  # manager.intercept_401 = false
  # manager.default_strategies(scope: :user).unshift :some_external_strategy
end

Also create new file with name (turbo_controller.rb) in the controller folder. On some places I saw that with out createing this controller file they added it into the devise.rb file which don't work in my case so I create that file.

# app/controllers/turbo_controller.rb

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
Ali Raza
  • 9
  • 1
0

Devise has been updated to work with Hotwire/Turbo and you can work with flash messages in any app using the latest Devise version out of the box.

To update an existing app using an older Devise version with the latest configuration, you will need to add the following config to the Devise initializers (config/initializers/devise.rb):

Devise.setup do |config|
  # ...
  # When using Devise with Hotwire/Turbo, the http status for error responses
  # and some redirects must match the following. The default in Devise for existing
  # apps is `200 OK` and `302 Found respectively`, but new apps are generated with
  # these new defaults that match Hotwire/Turbo behavior.
  # Note: These might become the new default in future versions of Devise.
  config.responder.error_status = :unprocessable_entity
  config.responder.redirect_status = :see_other
end

Based on Devise Docs

Amine
  • 1
  • 2
-3

adding data-turbo="false" or data: {turbo: false} with form helpers might solve this issue