rails - Devise - Handling - devise_error_messages rails - Devise - Handling - devise_error_messages ruby-on-rails ruby-on-rails

rails - Devise - Handling - devise_error_messages


I'm trying to figure this out myself. I just found this issue logged on Github https://github.com/plataformatec/devise/issues/issue/504/#comment_574788

Jose is saying that devise_error_messsages! method is just a stub (though it contains implementation) and that we're supposed to override/replace it. It would have been nice if this was pointed out somewhere in the wiki, which is why i guess there are a few people like us that have been guessing.

So I'm going to try reopening the module and redefine the method, effectively overriding the default implementation. I'll let you know how it goes.

Update

Yep, that works. I created app/helpers/devise_helper.rb and overrode it like so:

module DeviseHelper  def devise_error_messages!    'KABOOM!'  endend

So knowing this, I can modify the method to display error messages the way I want it to.

To help you solve your original problem: Here's the original devise_helper.rb on Github. Take a look at how the error messages are being traversed:

messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join

That should help you get started. :)

Another update

The resource object is actually the model that is being used by devise (go figure).

resource.class         #=> Userresource.errors.class  #=> ActiveModel::Error

It also appears to be defined in a higher scope (probably coming from the controller), so it can be accessed in a variety of places.

Anywhere in your Helper

module DeviseHelper  def devise_error_messages1!    resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join  end  def devise_error_messages2!    resource.errors.full_messages.map { |msg| content_tag(:p, msg) }.join  endend

Your View

<div><%= resource.errors.inspect %></div>


Below solution works with latest devise as of now (4.1.1) and Rails 4.2.6. But is so simple that I don't see the reason why wouldn't it work 10 years from now;)

If you want to recycle your error messages and have them look the same across your app I would recommend something like this (way I have learned with Michael Hartl tut):

Create partial for error messages: layouts/_error_messages.html.erb Put inside following code (here I use some bootstrap 3 classes):

<% if object.errors.any? %>  <div id="error_explanation">    <div class="alert alert-danger alert-dismissable">      <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>      <p><strong>This form contains <%= pluralize(object.errors.count, 'error') %>.</strong></p>      <ul>        <% object.errors.full_messages.each do |msg| %>          <li><%= msg %></li>        <% end %>      </ul>    </div>  </div><% end %>

Now you have something recyclable and you can use it across the board. Instead of standard devise:

<%= devise_error_messages! %>

Call it in your form like this:

<%= render 'layouts/error_messages', object: resource %>

You can put it in any form. Instead of passing devise resource you can pass variable from your form like this:

<%= form_for @post do |f| %>  <%= render 'layouts/error_messages', object: f.object %>    <%= f.text_field :content %>  <%= f.submit %><% end %>


I know it's been a while since this question was posted, but I just wanted to comment on what I've found. The two people who've already answered have been a tremendous help to me and I just wanted to contribute.

You'll see throughout Devise that there are calls using render_with_scope. I believe this is a method defined by devise and basically applies the current scope to the next view rendered.

Why is this relevant? Devise contains your errors within resource.errors (not @resource.errors). Devise works fine if you want to use it out of the box, so to speak.

Problems with these errors arise if you start changing your user management behavior. By adding a redirect_to or render (instead of render_with_scope) where Devise previously didn't have one, you're basically tossing out the error messages. This makes Devise unfriendly to modification, in my opinion.

My solution is this

# In application.html.erb<% flash.each do |name, msg| %>  # New code (allow for flash elements to be arrays)  <% if msg.class == Array %>    <% msg.each do |message| %>      <%= content_tag :div, message, :id => "flash_#{name}" %>    <% end %>  <% else %>    # old code    <%= content_tag :div, msg, :id => "flash_#{name}" %>  <% end %> #don't forget the extra end<% end %>

and

# Wherever you want Devise's error messages to be handled like # your other error messages# (in my case, registrations_controller.rb, a custom controller)flash[:notice] = flash[:notice].to_a.concat resource.errors.full_messages

The latter code block takes Devise's error messages as an array and appends it to flash[:notice] (as an array). Each message will be printed out one line at a time. If I have the time, I think I'm going to change how Devise handles error messages to do this throughout my app, as it seems much cleaner to have one error message system instead of two.