rails - 设计 - 处理 - devise_error_messages
Posted
技术标签:
【中文标题】rails - 设计 - 处理 - devise_error_messages【英文标题】:rails - Devise - Handling - devise_error_messages 【发布时间】:2011-05-05 08:06:17 【问题描述】:在我的用户编辑页面中,有一行如下:
<%= devise_error_messages! %>
问题是这不会像应用程序的其他部分那样输出错误:
<% flash.each do |key, value| %>
<div class="flash <%= key %>"><%= value %></div>
<% end %>
我的问题是,如何让设计错误消息像使用 flash.each 的其他人一样工作?
谢谢。
【问题讨论】:
请注意,Devise 已经在使用闪存,就像应用程序的其余部分一样。 devise_error_messages 不是关于 flash 消息(来自最后一页的信息),而是来自 ActiveRecord Validation guides.rubyonrails.org/v2.3.11/… 的验证错误 【参考方案1】:我正在尝试自己解决这个问题。我刚刚发现这个问题记录在 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.rb
on Github。看看错误消息是如何被遍历的:
messages = resource.errors.full_messages.map |msg| content_tag(:li, msg) .join
这应该可以帮助您入门。 :)
另一个更新
resource
对象实际上是设计正在使用的模型(见图)。
resource.class #=> User
resource.errors.class #=> ActiveModel::Error
它似乎也被定义在更高的范围内(可能来自控制器),因此可以在各种地方访问它。
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
你的观点
<div><%= resource.errors.inspect %></div>
【讨论】:
我刚试过这个,但不起作用。目标是在此处输出错误: @ColdTree 不,我们的目标是让它像闪存消息一样工作。能够控制标记是一个很好的解决方案。 ...尽管这是一项很好的研究工作,但我认为这不能回答问题。【参考方案2】:以下解决方案适用于目前最新的设计 (4.1.1) 和 Rails 4.2.6。但是太简单了,我看不出它为什么不能在 10 年后工作;)
如果您想回收错误消息并让它们在您的应用程序中看起来相同,我会推荐这样的东西(我从 Michael Hartl tut 学到的方法):
为错误消息创建部分:
layouts/_error_messages.html.erb
放入以下代码(这里我使用了一些 bootstrap 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">×</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 %>
【讨论】:
可能是有史以来最好、最直观的答案。 很酷的解决方案。复数(object.errors.count, 'errors' 应该改为复数(object.errors.count, 'error' 虽然 @LukaszMuzyka 在这个解决方案中.. 我需要从 user.rb 中删除 :validatable ..??? @Vishal - 没有。上述解决方案只是使用不同的 HTML 来显示消息,它不会改变任何设计机制 @Vishal 当您使用设计时,它已经在进行您提到的验证,无需任何额外代码。上述解决方案只是覆盖默认设计行为。您必须首先设计工作。您确定您是否已按照说明将设计与您的项目集成?【参考方案3】:我知道这个问题发布已经有一段时间了,但我只是想评论一下我的发现。已经回答的两个人对我有很大的帮助,我只是想做出贡献。
您会在整个设计中看到使用render_with_scope
的调用。我相信这是 devise 定义的一种方法,基本上将当前范围应用于渲染的下一个视图。
为什么这是相关的?设计包含您在resource.errors
中的错误(not @resource.errors
)。如果您想开箱即用,Devise 可以正常使用,可以这么说。
如果您开始更改用户管理行为,就会出现这些错误的问题。通过在 Devise 以前没有的地方添加 redirect_to
或 render
(而不是 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 处理错误消息的方式,以便在整个应用程序中执行此操作,因为拥有一个错误消息系统而不是两个似乎更干净。
【讨论】:
非常感谢你,因为我试图这样做,我正撞在墙上。 现在已经 5 年了,这个回复救了我的培根。非常感谢@eric-hu。【参考方案4】:我只是想在这里带来一个新的小片段:
所以我找到了一种更简单的方法来获得“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” 或其他任何内容。
赛亚, 库尔加。
【讨论】:
【参考方案5】:我通过创建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
成功了!
【讨论】:
【参考方案6】:我在 Rails 3 中使用 Devise,您的 flash 代码与我所拥有的几乎相同。在我的应用程序中,代码按预期工作;即设计错误消息与我的其余闪存消息一起输出:
<% flash.each do |name, msg| %>
<%= content_tag :div, msg, :id => "flash_#name" if msg.is_a?(String) %>
<% end %>
试试这个确切的代码,看看它是否有任何不同 - 不同的 ID 属性可能会有所帮助。
【讨论】:
谢谢,但最终什么也没显示。 "" 输出错误。以上什么都没做?想法? 道歉 - 我刚刚看到你的评论。老实说,我的想法已经不多了。我假设您已经在浏览器中查看了源代码并检查了生成的 HTML?以防万一某些东西被 CSS 隐藏了。您使用的是最新版本的 Devise 1.1.3 吗?【参考方案7】:我想到了这个,到目前为止它正在工作。 这会将设计消息添加到闪存中,因此可以照常使用。请考虑到我是 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 很好的答案。我认为这绝对是最干净的解决方案,并且非常适合我当前的架构。答案不是很清楚 - 基本上编辑之前的所有内容都应该被忽略(并通过 imo 删除或删除)。【参考方案8】:如果您希望能够显示多个给定类型的 flash(:alert、:notice 等...)并且不浪费时间尝试修改 gem 行为,这是我使用的解决方案设计。我很确定它可以与任何使用 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消息时,这样做:
flash_message(:success, "The user XYZ has been created successfully.")
【讨论】:
但是如何让设计消息调用 flash_messages 而不是保留错误对象。【参考方案9】:创建 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! %>
【讨论】:
其实应该使用:flash.now[:alert]【参考方案10】:诚然,有点 hacky,但我正在使用这个帮助程序 (app/helpers/devise_helper.rb) 来抓取闪光灯并使用那些如果设置则默认为 resource.errors
。这只是基于设计库中的帮助程序。
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
【讨论】:
【参考方案11】:如果您希望搭载 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
【讨论】:
【参考方案12】:为每个字段显示错误消息的非常简单的方法
<%= resource.errors.messages[:email].join(" ") %>
将每个字段的字段名称放在方括号中,在您要显示内联错误消息的每一行下方。
【讨论】:
【参考方案13】:从控制器显示您的设计错误,仅显示第一个错误。
flash[:error] = @resource.errors.full_messages.first
【讨论】:
【参考方案14】:只是在上面使用所有 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 %>
【讨论】:
【参考方案15】:我只是这样做,对我有用:在 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 应用于每条消息。【参考方案16】:
DeviseHelper#devise_error_messages!
已弃用并将被
在下一个主要版本中删除。
Devise 现在使用devise/shared/error_messages
下的部分显示
默认错误消息,并使它们更易于自定义。
更新您的视图更改调用:
<%= devise_error_messages! %>
到:
<%= render "devise/shared/error_messages", resource: resource %>
【讨论】:
【参考方案17】:很简单,把下面的代码放到views/devise/sessions/new.html.erb中
<% if flash[:alert] %>
<div class='alert alert-danger'><%= flash[:alert] %></div>
<% end %>
就是这样!
【讨论】:
这很有帮助【参考方案18】:-
删除“devise_error_messages!”来自“app/views/users/passwords/new”模板。
为您的用户创建自定义控制器 (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
【讨论】:
【参考方案19】:我喜欢这样做,就像在其他 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 %>
【讨论】:
【参考方案20】:为了让 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
我相信他们会是最干净的方式来写它,但它工作得很好
【讨论】:
【参考方案21】:我刚刚创建了一个像 John 一样的 app/helpers/devise_helper.rb
,但覆盖了这样的方法:
module DeviseHelper
def devise_error_messages!
flash[:error] = resource.errors.full_messages.join('<br />')
return ''
end
end
有了这个,我不必修改任何其他内容。 这是个坏主意吗?我是rails新手,请不要犹豫纠正我。谢谢。
【讨论】:
这将无法正常工作,Flash 消息现在包含 html 标记 。通常你只在你的 flash 消息中加入字符串。 也许,但新行仍然有效。如果您不喜欢这个解决方案,请提出另一种解决方案。【参考方案22】:我刚刚声明了 devise_error_messages!作为一个空帮手。 并为我的应用程序手动获取并处理一般 _errors 部分中的错误。似乎是最简单的解决方案,我不必浏览所有设计文件并删除对错误处理程序的调用。
【讨论】:
以上是关于rails - 设计 - 处理 - devise_error_messages的主要内容,如果未能解决你的问题,请参考以下文章