导轨-设计-处理-devise_error_messages


125

在我的用户编辑页面中,一行如下:

<%= devise_error_messages! %>

问题是这不会以其他应用程序的标准方式输出错误:

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

我的问题是,如何获得设计错误消息,使其像其他使用flash.each的消息一样工作?

谢谢。


1
请注意,与其他应用程序一样,Devise已在使用闪光灯。devise_error_messages不是关于Flash消息(上一页中的信息),而是ActiveRecord验证guide.rubyonrails.org/v2.3.11/…中的
Christopher Oezbek,2015年

Answers:


135

我试图自己解决这个问题。我刚刚发现这个问题记录在Github上https://github.com/plataformatec/devise/issues/issue/504/#comment_574788

Jose说该devise_error_messsages!方法只是一个存根(尽管它包含实现),并且我们应该覆盖/替换它。如果在Wiki的某处指出了这一点,那就太好了,这就是为什么我猜有些人像我们一样一直在猜测。

因此,我将尝试重新打开模块并重新定义方法,以有效地覆盖默认实现。我会让你知道怎么回事。

更新资料

是的,那行得通。我这样创建app/helpers/devise_helper.rb并覆盖它:

module DeviseHelper
  def devise_error_messages!
    'KABOOM!'
  end
end

因此,知道了这一点,我便可以修改该方法以按我希望的方式显示错误消息。

为了帮助您解决原始问题:这是devise_helper.rbGithub上的原始文件。看一下如何遍历错误消息:

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

那应该可以帮助您入门。:)

另一个更新

resource对象实际上是正在使用由色器件(去图)模型。

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

它似乎在更高的范围内定义(可能来自控制器),因此可以在许多地方进行访问。

助手中的任何地方

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

您的看法

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

我只是试过了,但是不起作用。目的是使错误在此处输出:<%flash.each do | key,value | %>
AnApprentice's

@ColdTree不,目标是使其 Flash消息一样工作。能够控制标记是一个很好的解决方案。
本杰明·阿特金

...尽管这是一项很好的研究工作,但我认为这无法回答问题。
2013年

37

以下解决方案适用于目前(4.1.1)和Rails 4.2.6的最新设计。但是它是如此简单,以至于我不明白为什么从现在起10年后它不起作用;)

如果您想回收您的错误消息并使它们在您的应用程序中看起来一样,我建议您使用以下方法(我从Michael Hartl tut中学到的方法):

创建部分错误消息: 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 %>

现在,您可以回收利用某些东西,并且可以全盘使用它。代替标准设计:

<%= devise_error_messages! %>

以您的形式调用它,如下所示:

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

您可以采用任何形式。除了传递设计资源,您可以像这样从表单传递变量:

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

1
可能是有史以来最好,最直观的答案。
维克多

2
很酷的解决方案。多元化(object.errors.count,'错误'应该更改为多元化(object.errors.count,'错误'
mizurnix

1
@LukaszMuzyka在此解决方案中..我需要从user.rb中删除:validatable吗?
维沙尔

1
@Vishal-不 上面的解决方案只是使用了不同的HTML来显示消息,并没有改变任何Devise的机制
Lukasz Muzyka

1
@Vishal在使用devise时已经在进行您提到的验证,而没有任何额外的代码。以上解决方案仅是覆盖默认的设计行为。首先必须要有设计工作。您确定是否已按照指示将devise与项目集成?
卢卡斯·穆兹卡

22

我知道这个问题发布已经有一段时间了,但是我只想评论一下我发现的内容。已经回答的两个人对我有很大的帮助,我只想做出贡献。

您会在整个Devise中看到使用进行呼叫render_with_scope。我相信这是由devise定义的方法,基本上将当前范围应用于下一个呈现的视图。

为什么这是相关的?Devise在resource.errorsnot @resource.errors)中包含您的错误。如果您想开箱即用,可以使用Devise。

如果您开始更改用户管理行为,则会出现这些错误的问题。通过在Devise以前没有的地方添加一个(redirect_torender代替render_with_scope),基本上就是在抛出错误消息。我认为,这使得Devise不适合修改。

我的解决方案是这样

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

# 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

后面的代码块将Devise的错误消息作为数组,并将其附加到flash[:notice](作为数组)。每条消息将一次打印出一行。如果有时间,我想我将更改Devise处理错误消息的方式以在我的整个应用程序中执行此操作,因为使用一个错误消息系统而不是两个错误消息系统似乎更清洁。


3
非常感谢您,我为此努力将头撞在墙上。
卢卡斯

1
现在已经5年了,这个回应挽救了我的培根。非常感谢@ eric-hu。
marcamillion 2016年

12

我通过创建一个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

工作了!


11

我只想在这里带来一个新的小片段:

因此,我找到了一种更简单的方法来获得“ AnApprentice”想要的结果。

首先,如果要自定义Devise插件中的任何内容,我强烈建议您从“ \ Ruby_repertory \ lib \ ruby​​ \ gems \ 1.9.1 \ gems \ devise-version \ app \ controllers”中复制代码| helpers | mailers ...”添加到项目中所需的文件。

[编辑]或者,您可以使文件从“常规”设计文件继承而来...像...说...您只想覆盖devise / registrations_controller.rb(用户自定义的第一行)中的一个功能注册控制器将是:

class Users::RegistrationsController < Devise::RegistrationsController

[2013年8月7日编辑]现在,Devise甚至提供了生成控制器的工具:https : //github.com/plataformatec/devise/wiki/Tool :-Generate-and-customize-controllers

所以...无论如何...我设法得到了“ AnApprentice”想要的内容,只是编写了此内容(有关更清洁的解决方案,请参见以下大修改):

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

      return resource.errors
   end
end

在我看来,接下来的几行效果很好:

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

好吧...然后您可以访问特定属性的错误,如下所示:

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

还有...一个小技巧,每个属性仅显示一个错误(第一个被捕获):

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

我知道这个问题发布已经有一段时间了,但是我认为这会帮助很多设计用户:)。

大修改:

由于我喜欢扩展代码,使其更整洁并与他人共享,因此我最近想更改devise_error_messages!为了在我的视图中使用它并使它显示我上面解释的技巧。

所以,这是我的方法:

 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

在这里没什么大不了的,我重用了我在视图中编写的代码,以仅显示一个错误pey属性,因为通常第一个属性是唯一相关的(例如,当用户忘记了一个必填字段时)。

我正在计算那些“独特的”错误,并且使用复数形式制作了H2 HTML标题并将其放在错误列表之前。

所以现在,我可以使用“ devise_error_messages!”了。作为默认设置,它可以完全渲染我之前已经渲染的内容。

如果要访问视图中的特定错误消息,我现在建议直接使用“ resource.errors [:attribute] .first”或其他任何方式。

世嘉,喀尔加尔。


6

我在Rails 3中使用Devise,而您的Flash代码与我得到的代码几乎相同。在我的应用中,代码按预期工作;即Devise错误消息与我的其余Flash消息一起输出:

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

试试这个确切的代码,看看它是否有区别-不同的ID属性可能会有所帮助。


谢谢,但最终什么也没显示。“ <%= devise_error_messages!%>”输出错误。上面什么都没做?想法?
AnApprentice,2010年

抱歉-我刚刚才看到您的评论。说实话,我的想法已经用完了。我认为您已经在浏览器中查看了源代码并检查了生成的HTML?以防万一CSS隐藏了某些东西。您是否在使用最新版本的Devise 1.1.3?
Scott

5

我想到了,到目前为止,它一直在工作。这会将设计消息添加到闪存中,因此可以照常使用。请考虑我是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

编辑:

抱歉,我正在值班,并且出现了一些不良行为。因为after_filter在渲染之后被调用,所以它不能按预期工作。如果有人知道如何在操作之后但在渲染之前调用方法...

但是您可以改用类似的方法:

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

views/shared/_messages.html.erb

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

1
+1好答案。我认为这绝对是最干净的解决方案,非常适合我当前的体系结构。答案不是很清楚-基本上,编辑之前的所有内容都应忽略(并通过imo删除或删除)。
zelanix 2014年

3

如果您希望能够显示给定类型的多个闪光灯(:alert,:notice等),并且不浪费时间尝试修改gem行为,那么这就是我与Devise一起使用的解决方案。我很确定它可以与使用Flash消息的任何gem一起使用。

首先,在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

第二件事,在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>

第三件事,每当您想在任何控制器中添加即显消息时,请执行以下操作:

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

但是,如何获取Devise消息以调用flash_messages而不是保留错误对象。
Christopher Oezbek

3

创建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

您认为替代

<%= devise_error_messages! %>

至:

<% devise_error_messages! %>

1
实际上,您应该使用:flash.now [:alert]
BM

2

诚然,有点hacky,但是我正在使用此帮助程序(app / helpers / devise_helper.rb)来获取闪存并使用这些闪存(如果已设置),则默认为 resource.errors。这只是基于devise库中的帮助程序。

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

2

如果您希望搭载devise_error_messages,则可以通过添加到resource.errors中来实现。

如果您要过度使用注册控制器,它可能看起来像

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 

2

显示每个字段错误消息的非常简单的方法

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

在要显示内联错误消息的每行下方,将每个字段的名称放在方括号中。


1

要显示控制器中的设计错误,并且只显示第一个错误。

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

1

只是在上面使用所有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

我只是这样做,为我工作:在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

在所有查看文件中,我都会更改

<%= devise_error_messages! %>

对于:

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

对我来说,它使我可以编辑和新用户:

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

希望它能对您有所帮助;)


我真的不明白这是怎么做的?这是标准行为吗?这只是另一种解决方法,<%= devise_error_messages! %>不能回答问题。该问题询问如何对每条消息应用Flash。
马克

0
  1. 删除“ devise_error_messages!” 来自“应用程序/视图/用户/密码/新”模板。
  2. 为您的用户(app / controllers / users / passwords_controller.rb)创建自定义控制器,并在after过滤器中添加错误Flash数组:
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

0

我喜欢这样做,就像在其他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 %>

0

为了使Materialisecss将设计错误消息显示为Toast,我在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

我敢肯定,他们将是最干净的写法,但是它完全可以正常工作


0

DeviseHelper#devise_error_messages! 已不推荐使用,并将在下一个主要版本中删除。

现在,Devise现在devise/shared/error_messages默认使用部分下方显示错误消息,并使错误消息更易于自定义。更新您的视图,更改来自以下位置的呼叫:

      <%= devise_error_messages! %>

至:

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

-1

我刚刚创建了一个app/helpers/devise_helper.rb像John一样的方法,但是覆盖了这样的方法:

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

有了这个,我不必修改其他任何东西。这是一个坏主意吗?我是新手,请随时纠正我。谢谢。


Flash消息现在包含html标签<br>,因此无法正常工作。通常,您只在Flash消息中放入字符串。
AZ。

也许可以,但是新生产线仍然有效。如果您不喜欢此解决方案,请提出另一种解决方案。
YoyoS 2015年

-2

我刚刚声明了devise_error_messages!作为空的帮手。并手动为我的应用程序获取并处理了通用_errors部分中的错误。好像是最简单的解决方案,我不必遍历所有devise的文件并删除对错误处理程序的调用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.