简体   繁体   English

rails - 设计 - 处理 - devise_error_messages

[英]rails - Devise - Handling - devise_error_messages

in my user edit page, there is a line as follows:在我的用户编辑页面中,有一行如下:

<%= devise_error_messages! %>

The problem is this does not output errors the standard way that the rest of the app does:问题是这不会以应用程序其余部分的标准方式输出错误:

<% flash.each do |key, value| %>
    <div class="flash <%= key %>"><%= value %></div>
<% end %>

My question is, how do I get the devise error message to work like the others that use the flash.each?我的问题是,如何让设计错误消息像使用 flash.each 的其他人一样工作?

Thanks.谢谢。

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我刚刚发现这个问题登录在 Github https://github.com/plataformatec/devise/issues/issue/504/#comment_574788

Jose is saying that devise_error_messsages!何塞说的是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:我创建了app/helpers/devise_helper.rb并像这样覆盖它:

module DeviseHelper
  def devise_error_messages!
    'KABOOM!'
  end
end

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 .帮助您解决最初的问题:这是Github上的原始devise_helper.rb 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对象实际上是设计正在使用的模型(见图)。

resource.class         #=> User
resource.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
  end
end

Your View你的看法

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

Below solution works with latest devise as of now (4.1.1) and Rails 4.2.6.以下解决方案适用于目前的最新设计 (4.1.1) 和 Rails 4.2.6。 But is so simple that I don't see the reason why wouldn't it work 10 years from now;)但是太简单了,我不明白为什么它在 10 年后不起作用;)

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):如果你想回收你的错误消息并让它们在你的应用程序中看起来相同,我会推荐这样的东西(我从 Michael Hartl tut 那里学到的方式):

Create partial for error messages: layouts/_error_messages.html.erb Put inside following code (here I use some bootstrap 3 classes):为错误消息创建部分: layouts/_error_messages.html.erb放入以下代码(这里我使用了一些引导程序 3 类):

<% 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">&times;</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 .您将在整个设计中看到使用render_with_scope调用。 I believe this is a method defined by devise and basically applies the current scope to the next view rendered.我相信这是一种由 devise 定义的方法,基本上将当前范围应用于渲染的下一个视图。

Why is this relevant?为什么这是相关的? Devise contains your errors within resource.errors ( not @resource.errors ).设计在resource.errors不是@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.通过在 Devise 之前没有的地方添加redirect_torender (而不是render_with_scope ),您基本上是在render_with_scope错误消息。 This makes Devise unfriendly to modification, in my opinion.在我看来,这使得 Devise 对修改不友好。

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).后一个代码块将 Devise 的错误消息作为一个数组,并将其附加到flash[:notice] (作为一个数组)。 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.如果我有时间,我想我将改变 Devise 处理错误消息的方式,以便在我的整个应用程序中执行此操作,因为拥有一个错误消息系统而不是两个错误消息系统似乎要干净得多。

I solved this similarly to YoyoS, by creating an app/helpers/devise_helper.rb and placing this in it:我通过创建一个app/helpers/devise_helper.rb并将其放入其中来解决这个问题,类似于 YoyoS:

module DeviseHelper

  # Hacky way to translate devise error messages into devise flash error messages
  def devise_error_messages!
    if resource.errors.full_messages.any?
        flash.now[:error] = resource.errors.full_messages.join(' & ')
    end
    return ''
  end
end

Worked!工作了!

I just want to bring a new little piece here:我只想在这里带来一个新的小作品:

So I found an easier way to get the result that "AnApprentice" wanted.所以我找到了一种更简单的方法来获得“AnApprentice”想要的结果。

First of all, if you want to customize anything within the Devise plug-in, I highly advise you to copy past the code from "\\Ruby_repertory\\lib\\ruby\\gems\\1.9.1\\gems\\devise-version\\app\\controllers|helpers|mailers..." to the file you want in your project.首先,如果您想自定义 Devise 插件中的任何内容,我强烈建议您将代码从“\\Ruby_repertory\\lib\\ruby\\gems\\1.9.1\\gems\\devise-version\\app\\controllers |helpers|mailers...”添加到您的项目中所需的文件中。

[Edit] Or you can make your file inherit from the "normal" devise files... Like... say... You want to overwrite only one function within the devise/registrations_controller.rb, the first line of your Users custom registrations controller would be: [编辑] 或者你可以让你的文件从“普通”设计文件继承......就像......说......你只想覆盖devise/registrations_controller.rb中的一个函数,你的用户自定义的第一行注册控制器将是:

class Users::RegistrationsController < Devise::RegistrationsController

[Edit August 7th 2013] Now Devise even provides a tool to generate controllers: https://github.com/plataformatec/devise/wiki/Tool:-Generate-and-customize-controllers [Edit August 7th 2013] 现在 Devise 甚至提供了一个生成控制器的工具: https : //github.com/plataformatec/devise/wiki/Tool :-Generate-and-customize-controllers

So... anyway... I managed to get what "AnApprentice" wanted just writing this (for a cleaner solution, see the following big edit) :所以......无论如何......我设法得到了“AnApprentice”想要的东西(为了更清晰的解决方案,请参阅以下大编辑):

#/my_project/app/helpers/devise_helper.rb
module DeviseHelper
   def devise_error_messages!
      return "" if resource.errors.empty?

      return resource.errors
   end
end

And, in my view, the next lines worked pretty well:而且,在我看来,接下来的几行工作得很好:

<% devise_error_messages!.each do |key, value| %>
    <div class="flash <%= key %>"><%= key %> <%= value %></div>
<% end %>

Well... then you can access to errors for a specific attribute like this:嗯...然后您可以访问特定属性的错误,如下所示:

    #Imagine you want only the first error to show up for the login attribute:
    <%= devise_error_messages![:login].first %> 

And... A little trick to have only one error (the first to get catched) showing up per attribute:而且...一个小技巧,让每个属性只显示一个错误(第一个被捕获的错误):

<% if resource.errors.any? %>
  <% saved_key = "" %>
  <% devise_error_messages!.each do |key, value| %>
    <% if key != saved_key %>
        <div class="flash <%= key %>"><%= key %> <%= value %></div>
    <% end %>
    <% saved_key = key %>
  <% end %>
<% end %>

I know it's been a while since this question was posted, but I think that it will help lot's of devise users :).我知道这个问题发布已经有一段时间了,但我认为它会帮助很多设计用户:)。

Big Edit:大编辑:

As I love to extend my code, making it cleaner and share it with others, I recently wanted to change the devise_error_messages!由于我喜欢扩展我的代码,使其更简洁并与他人分享,我最近想更改 devise_error_messages! method in order to use it in my views and make it display the trick I explained above.方法以便在我的视图中使用它并使其显示我上面解释的技巧。

So, here is my method:所以,这是我的方法:

 def devise_error_messages! 
    html = ""

    return html if resource.errors.empty?

    errors_number = 0 

    html << "<ul class=\"#{resource_name}_errors_list\">"

    saved_key = ""
    resource.errors.each do |key, value|
      if key != saved_key
        html << "<li class=\"#{key} error\"> This #{key} #{value} </li>"
        errors_number += 1
      end
      saved_key = key
    end

    unsolved_errors = pluralize(errors_number, "unsolved error")
    html = "<h2 class=\"#{resource_name}_errors_title\"> You have #{unsolved_errors} </h2>" + html
    html << "</ul>"

    return html.html_safe
 end

No big deal here, I reused the code I wrote in my view to show only one error pey attribute, because often the first one is the only relevant (like when the user forgets one required field).这里没什么大不了的,我重用了我在视图中编写的代码来仅显示一个错误 pey 属性,因为通常第一个是唯一相关的(例如当用户忘记一个必填字段时)。

I'm counting those "unique" errors and I'm making a H2 HTML title using pluralize and putting it BEFORE the errors list.我正在计算那些“独特的”错误,我正在使用复数形式制作 H2 HTML 标题并将其放在错误列表之前。

So now, I can use the "devise_error_messages!"所以现在,我可以使用“devise_error_messages!” as the default one and it renders exactly what I was already rendering before.作为默认值,它完全呈现我之前已经呈现的内容。

If you want to access a specific error message in your view, I now recommend to use directly "resource.errors[:attribute].first" or whatever.如果您想访问视图中的特定错误消息,我现在建议直接使用“resource.errors[:attribute].first”或其他任何内容。

Seya, Kulgar.塞亚,库尔加。

I'm using Devise in Rails 3 and your flash code is pretty much identical to what I've got.我在 Rails 3 中使用 Devise,您的 Flash 代码与我所拥有的几乎相同。 In my app, the code works as expected;在我的应用程序中,代码按预期工作; ie Devise error messages are output with the rest of my flash messages:即设计错误消息与我的其余 flash 消息一起输出:

<% flash.each do |name, msg| %>
  <%= content_tag :div, msg, :id => "flash_#{name}" if msg.is_a?(String) %>
<% end %>

Try out this exact code and see if it makes any difference - the different ID attribute may help.试试这个确切的代码,看看它是否有任何不同 - 不同的 ID 属性可能会有所帮助。

I came up to this and it's working so far.我想到了这个,到目前为止它正在工作。 That adds devise messages to the flash, so it can be used as usual.这将向 flash 添加设计消息,因此它可以照常使用。 Please consider that I'm new to Ruby and Rails...请考虑我是 Ruby 和 Rails 的新手...

class ApplicationController < ActionController::Base
  after_filter :set_devise_flash_messages, :if => :devise_controller?
  ...

  private:

  def set_devise_flash_messages
    if resource.errors.any?
      flash[:error] = flash[:error].to_a.concat resource.errors.full_messages
      flash[:error].uniq!
    end
  end
end

Edit:编辑:

Sorry I was running guard and some unwanted behavior was present.抱歉,我正在守卫,并且出现了一些不需要的行为。 Since after_filter is called after the rendering so it doesn't work as expected.由于after_filter是在渲染之后调用的,所以它不能按预期工作。 If someone know how to call a method after the action but before the rendering...如果有人知道如何在操作之后但在渲染之前调用方法......

But you can use something like that instead:但是你可以使用类似的东西:

module ApplicationHelper

  # merge the devise messages with the normal flash messages
  def devise_flash
    if controller.devise_controller? && resource.errors.any?
      flash.now[:error] = flash[:error].to_a.concat resource.errors.full_messages
      flash.now[:error].uniq!
    end
  end

end

In views/shared/_messages.html.erbviews/shared/_messages.html.erb

<% devise_flash %>
<!-- then display your flash messages as before -->

If you want to be able to display more than one flash of a given type (:alert, :notice, etc...) and not waste your time trying to modify a gem behavior, this is the solution I used with Devise.如果您希望能够显示多个给定类型的 flash(:alert、:notice 等...)并且不想浪费时间尝试修改 gem 行为,这就是我与 Devise 一起使用的解决方案。 I'm pretty sure it could be used with any gem that uses flash messages.我很确定它可以与任何使用 flash 消息的 gem 一起使用。

First thing to do, in your application_controller.rb, add this:首先要做的是,在您的 application_controller.rb 中,添加以下内容:

  # Adds the posibility to have more than one flash of a given type
  def flash_message(type, text)
    flash[type] ||= []
    flash[type] << text
  end

Second thing to do, displaying your flash messages with this in application.html.erb (or wherever you want):第二件事,在 application.html.erb(或任何你想要的地方)中显示你的 flash 消息:

   <div class="flashes">
      <% flash.each do |key, messages| %>
        <% messages = Array(messages) unless messages.is_a?(Array) %>
        <% messages.each do |message| %>
        <div class="alert alert-<%= key %>">
          <%= message %>
        </div>
        <% end %>
      <% end %>
    </div>

Third thing to do, whenever you want to add a flash message in any controller, do this:要做的第三件事,无论何时您想在任何控制器中添加一条 flash 消息,请执行以下操作:

flash_message(:success, "The user XYZ has been created successfully.")

Create DeviseHelper:创建设计助手:

module DeviseHelper
  def devise_error_messages!
    return "" if resource.errors.empty?

    messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg)}.join
    return flash.now[:alert] = messages.html_safe
  end
end

In your view, substitute在你看来,替代

<%= devise_error_messages! %>

To:到:

<% devise_error_messages! %>

If you are looking to piggyback off of devise_error_messages then you can so by adding to resource.errors如果您希望利用 devise_error_messages,那么您可以通过添加到 resource.errors 来实现

If you were to over ride the registration controller, it might look like如果您要超越注册控制器,它可能看起来像

def create
  if validation_or_other_check_passes
    super
  else
    build_resource
    clean_up_passwords(resource)
    resource.errors.add(:notice, "The check failed.")
    render :new 

Very easy way to display error message for each field显示每个字段的错误消息的非常简单的方法

<%= resource.errors.messages[:email].join(" ") %>

put for each field with field name in square bracket below every line where u want to display inline error message.将字段名称放在方括号中的每个字段放在您要显示内联错误消息的每一行下方。

Admittedly, a bit hacky, but I'm using this helper (app/helpers/devise_helper.rb) to grab flashes and use those if set then default to resource.errors .诚然,有点hacky,但我正在使用这个助手(app/helpers/devise_helper.rb)来抓取闪光灯并使用那些如果设置然后默认为resource.errors This is just based on the helper that's in the devise lib.这只是基于设计库中的帮助程序。

module DeviseHelper

  def devise_error_messages!
    flash_alerts = []
    error_key = 'errors.messages.not_saved'

    if !flash.empty?
      flash_alerts.push(flash[:error]) if flash[:error]
      flash_alerts.push(flash[:alert]) if flash[:alert]
      flash_alerts.push(flash[:notice]) if flash[:notice]
      error_key = 'devise.failure.invalid'
    end

    return "" if resource.errors.empty? && flash_alerts.empty?
    errors = resource.errors.empty? ? flash_alerts : resource.errors.full_messages

    messages = errors.map { |msg| content_tag(:li, msg) }.join
    sentence = I18n.t(error_key, :count    => errors.count,
                                 :resource => resource.class.model_name.human.downcase)

    html = <<-HTML
    <div id="error_explanation">
      <h2>#{sentence}</h2>
      <ul>#{messages}</ul>
    </div>
    HTML

    html.html_safe
  end

end

i simply do this, worked for me: in app/helpers/ , i create a file devise_helper.rb我只是这样做,对我有用:在app/helpers/ 中,我创建了一个文件devise_helper.rb

  module DeviseHelper

  def devise_error_messages_for(resource)
    return "" if resource.errors.empty?

    messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
    sentence = I18n.t("errors.messages.not_saved",
                      count: resource.errors.count,
                      resource: resource.class.model_name.human.downcase)

    html = <<-HTML
    <div id="error_explanation">
      <h2>#{sentence}</h2>
      <ul>#{messages}</ul>
    </div>
    HTML

    html.html_safe
  end
end

in all view files i change在我更改的所有视图文件中

<%= devise_error_messages! %>

for:为了:

<%= devise_error_messages_for(#your object in your formular)%>

for me it make in my view edit and new user:对我来说,它在我的视图中编辑和新用户:

  <%=form_for resource, as: @user, url: user_path(@user),...
      <%= devise_error_messages_for(@user) %>

hope it will help you ;)希望它会帮助你;)

DeviseHelper#devise_error_messages! is deprecated and will be removed in the next major version.已弃用,将在下一个主要版本中删除。

Devise now uses a partial under devise/shared/error_messages to display error messages by default, and make them easier to customize. Devise 现在默认devise/shared/error_messages下的部分来显示错误消息,并使它们更易于定制。 Update your views changing calls from:更新您的视图更改调用:

      <%= devise_error_messages! %>

to:到:

      <%= render "devise/shared/error_messages", resource: resource %>

Easy, place the following code in views/devise/sessions/new.html.erb很简单,将以下代码放在 views/devise/sessions/new.html.erb 中

<% if flash[:alert] %>
 <div class='alert alert-danger'><%= flash[:alert] %></div>
<% end %>

And that's it!就是这样!

从您的控制器显示您的设计错误,仅显示第一个错误。

flash[:error] = @resource.errors.full_messages.first

Just to add to Eric Hu answer above where all the If statements are used, rather do something like this instead.只是在上面使用所有 If 语句的地方添加 Eric Hu 的答案,而不是执行类似的操作。

# Controller
flash.now[:error] = flash[:error].to_a.concat(resource.errors.full_messages)

# View
<% flash.each do |name, msg| %>
 <% Array(msg).uniq.each do |message| %>
  <%= message %>
 <% end %>
<% end %>
  1. Remove the "devise_error_messages!"删除“devise_error_messages!” from "app/views/users/passwords/new" template.来自“应用程序/视图/用户/密码/新”模板。
  2. Create custom controller for your user (app/controllers/users/passwords_controller.rb) and in an after filter add errors flash array:为您的用户创建自定义控制器 (app/controllers/users/passwords_controller.rb) 并在过滤器后添加错误闪存阵列:
class Users::PasswordsController < Devise::PasswordsController
  after_filter :flash_errors

  def flash_errors
    unless resource.errors.empty?
      flash[:error] = resource.errors.full_messages.join(", ")
    end
  end
end

I like to do it just like it's done in the other Devise controller with this cheat.我喜欢这样做,就像在其他 Devise 控制器中使用此作弊所做的那样。

<% if flash.count > 0 %>
  <div id="error_explanation">
    <h2>Errors prevented you from logging in</h2>
      <ul>
        <% flash.each do |name, msg| %>
        <li>
          <%= content_tag :div, msg, id: "flash_#{name}" %>
        </li>
       <% end %>
     </ul>
   </div>
<% end %>

For materialisecss to display devise error messages as toast I added this code in app/helpers/devise_helper.rb为了使 materialisecss 将设计错误消息显示为吐司,我在 app/helpers/devise_helper.rb 中添加了此代码

module DeviseHelper
  def devise_error_messages!

    messages = resource.errors.full_messages.map { |msg|
      String.new(" M.toast({html: '" + msg + "' }); ".html_safe )
    }.join

    messages = ("<script>" + messages + "</script>").html_safe
  end 
end

I am sure their would be cleanest way to write it but it's woking perfectly我相信他们会是最干净的写法,但它完美地工作

I just created an app/helpers/devise_helper.rb like John but overrode the method like that :我刚刚创建了一个像 John 一样的app/helpers/devise_helper.rb但覆盖了这样的方法:

module DeviseHelper
  def devise_error_messages!
    flash[:error] = resource.errors.full_messages.join('<br />')
    return ''
  end
end

With this I don't have to modify anything else.有了这个,我不必修改任何其他东西。 Is it a bad idea ?这是一个坏主意吗? I'm new to rails, don't hesitate to correct me.我是 Rails 的新手,请不要犹豫纠正我。 Thanks.谢谢。

I just declared devise_error_messages!我刚刚声明了 devise_error_messages! as an empty helper.作为空帮手。 And manually fetched and handled the errors in a general _errors partial for my application.并在我的应用程序的一般 _errors 部分中手动获取和处理错误。 Seemed like the simplest solution and I don't have to go through all of devise's files and remove the call to the error handler.似乎是最简单的解决方案,我不必浏览所有设计文件并删除对错误处理程序的调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM