Rails 7 + Devise Not seeing flash messages from devise

Viewed 1929

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

6 Answers

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]

I am following the tutorials (video: https://gorails.com/episodes/devise-hotwire-turbo?autoplay=1) 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 or not showing up.

I added to my devise.rb now :

    # 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


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.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

Follow this link for a workaround.

  1. create a controller to respond to html and turbo_stream
  2. modify config/initializers/devise.rb

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 %>

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]

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
Related