Devise: Changing Flash Keys

Created on 31 Aug 2012  路  19Comments  路  Source: heartcombo/devise

I'm using the devise gem and would like to change the flash keys from [:notice] and [:alert] to something custom. The rest of my app is using keys like [:success] and [:warning].

Is there any way to change the key from notice to something else?

Thanks!

Most helpful comment

For those using bootstrap 4 alpha and rails 4+ there is another really easy sass work-around to keep your devise flash messages styled like all your others if that is all you after. @extend is simpler than @mixin and @import.

You won't need a devise helper method for this either, but you need to have set up flash messages say in your application.html.erb file using bootstrap styles:

<% flash.each do |key, value| %>
  <div class="alert alert-<%= key %> alert-dismissable" role="alert">
    <button type="button" data-dismiss="alert" class="close">
      <span aria-hidden="true">&times;</span>
    </button>
    <%= value %>
    </div>
<% end %>

So then in a scss file use @extend to change styling of "alert-alert" etc to bootstrap styles,
eg:

.alert-notice {
  @extend .alert-success;
}

All 19 comments

You cannot do that. Just use :notice and :alert which are Rails conventions. Thanks.

I know this issue has been closed but I think it would make a lot of sense if developers could determine what methods to use, either to stick to convention or breakout as it suits their app.

I agree with @verygreenboi. Something like this would be really helpful.

Especially not that Bootstrap 3 does not use :notice

:+1:

In my case I'm using shared sessions between Rails 3 and 4 apps, and flash messages are breaking this integration. I'm trying to use different flash keys than notice or alert but Devise don't let me do it :(

You can do something like this in a helper:

def switch_flash existing_key, new_key
    message = flash[existing_key].dup
    flash.discard existing_key
    flash[new_key] = message
end

and then in a controller overridden from a Devise controller:

def show
    super
    switch_flash :notice, :success
end

It's not great as it's going against the API provided by Devise and may break your future code, so I'd ensure to test if using it. Like all of you I wanted to switch the keys so I have had to put this in my code. In my opinion a 'Account successfully confirmed' should be :success rather than :notice.

This needs to be configurable. It's extra special sad that devise doesn't play well with the responders gem (which, for the record, lets you customize the flash keys).

For those who come after, using bootstrap-sass gem v 3.1.1.1, I was able to add this to my screen.css.sass (which is required by application.css after bootstrap):

.alert-notice
  @include alert-variant($alert-success-bg, $alert-success-border, $alert-success-text)

.alert-alert
  @include alert-variant($alert-danger-bg, $alert-danger-border, $alert-danger-text)

Which makes the devise flash messages _appear_ to follow the convention without _actually_.

@BM5k Thank you. Your code works.

Here's another option, to keep the bootstrap convention in place:
http://stackoverflow.com/questions/15305586/changing-devise-flash-messages-from-notice-to-error

though this does not work for success messages, only errors.

class BootstrapDeviseFailure < Devise::FailureApp
  def recall
    env["PATH_INFO"]  = attempted_path
    flash.now[:danger] = i18n_message(:invalid)
    self.response = recall_app(warden_options[:recall]).call(env)
  end
end


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

@BM5k Thank you very much man, that was awesome and solved my problem :)

You cannot do that. Just use Sorcery or AuthLogic which are much more convenient. Thanks.

@BM5k, I got a Sass::SyntaxError Undefined mixin 'alert-variant' error.

  • in _alerts.scss (in boostrap gem) there is @mixin alert-variant.
  • in my application.css.sass I have @import "screen.css.sass" below @import "bootstrap".
    I tried with @import "screen", @import "screen.css" and without before (as it should not even be required afaik).
  • in screen.css.sass I have the code you provided.

It looks like screen.css.sass is found and evaluated properly but it does not find alert-variant of bootstrap even though it should be imported just before. Do you have any idea why this in not working or what I misunderstood?

@florentcuret those instructions may have been specific to bootstrap-sass v3.1.1.1, my current project using v3.3.7 handles this in javascript instead by adding the classes

$ ->
  $('div.alert-notice').addClass 'alert-success'
  $('div.alert-alert').addClass 'alert-danger'

It's hackier and I can't remember off hand _why_ I did it this way instead.

For those using bootstrap 4 alpha and rails 4+ there is another really easy sass work-around to keep your devise flash messages styled like all your others if that is all you after. @extend is simpler than @mixin and @import.

You won't need a devise helper method for this either, but you need to have set up flash messages say in your application.html.erb file using bootstrap styles:

<% flash.each do |key, value| %>
  <div class="alert alert-<%= key %> alert-dismissable" role="alert">
    <button type="button" data-dismiss="alert" class="close">
      <span aria-hidden="true">&times;</span>
    </button>
    <%= value %>
    </div>
<% end %>

So then in a scss file use @extend to change styling of "alert-alert" etc to bootstrap styles,
eg:

.alert-notice {
  @extend .alert-success;
}

This would be a convenient feature.

As a frequent user of devise, the fact that this is not configurable is astounding. While I appreciate the work the team does with Devise, and the overall functionality of the gem is great, the stalwart opposition to this is puzzling especially considering the proliferation of using bootstrap as a front end.

Another option instead of using SCSS like @eatoncw did is to simply catch Devise-specific flash types and map them to Bootstrap

(this is Slim code, but you get the idea)

partials/_flash.slim:

- devise_bootstrap_mapping = {'alert' => 'danger', 'notice' => 'info'}

- flash.each do |key, value|
  - if devise_bootstrap_mapping.keys.include? key
    - key = devise_bootstrap_mapping[key]
  div class = "alert alert-#{key} alert-dismissible" role="alert" 
    button.close aria-label="Close" data-dismiss="alert" type="button" 
      span aria-hidden="true" &times;
    = value.html_safe

This transforms the Devise-specific alert and info keys to Bootstrap danger and info alert classes.

I think this or SCSS is the easiest method, because including various helpers, class_evaling Devise classes, etc. is pretty complex. This just maps Devise to Bootstrap, which is pretty straightforward.

What I did to get around this was to just write a simple view helper. It's not perfect but its implementation is simple and it is entirely in the presentation layer rather than a switch in the controller, like this:

module FlashHelper
  def bootstrap_alert(key)
    case key
      when 'alert'
        'warning'
      when 'notice'
        'success'
      when 'error'
        'danger'
    end
  end
end

And then in your view like this:

<% flash.each do |key, value| %>
<div class="alert alert-<%= bootstrap_alert key %> fade in alert-dismissable">
  <%= value %>
  <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
</div>
<% end %>

This is similar to the workaround that @cireficc suggested, but I prefer the use of case over a hash mapping. Probably isnt too much difference between the two.

Was this page helpful?
0 / 5 - 0 ratings