【问题标题】:Display link in Rails form error message在 Rails 表单错误消息中显示链接
【发布时间】:2011-07-16 18:31:24
【问题描述】:

在我们的注册表单上,我们validates_uniqueness_of :email

当用户尝试使用我们的注册表单并指定现有的电子邮件地址时,我希望他们看到这样的错误消息

此电子邮件地址已被使用。如果您在登录时遇到问题,可以reset your password

显然,我想为链接使用命名路由,但我的用户模型无权访问它。我怎样才能做到这一点?

旁注:我们将很快为我们的应用程序提供翻译,所有这些错误消息都将在 YAML 文件中结束。我可以以某种方式在我的 YAML 语言环境文件中的消息中注入我的 new_password_url 吗? (例如,config/locales/en.yml

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 validation internationalization


    【解决方案1】:

    您可以在错误消息中放置自己的标签,例如 ~[new_password_url]。然后在用实际的 gsub ur 标记呈现错误消息时。如果您想通用地执行此操作,您可以使用 regexp 获取路径,然后对其进行 eval 以获取 url,然后将其重新输入。如果您将 html 放入文本,请使用 raw 方法。

    【讨论】:

    • 抱歉粗鲁,但这个建议简直让我毛骨悚然。
    • 但是您希望通过了解如何在模型中创建 url 链接来打破 mvc 的约定。必须付出一些东西。您也可以将您的请求对象传递给您的模型,甚至将其存储在您的线程类中,但对我来说,这更像是“皮肤爬行”然后有一个替换标签。
    • 我通常不参与互联网冲突,但我刚刚收到了 5 个完全不相关的问题的 5 个反对票,这些问题与你今天的 5 个反对票完全吻合。这是非常幼稚的行为。
    • Kalendae,感谢您恢复这些选票。只是为了确保@macek 也知道,请参阅here。现在:和平!
    【解决方案2】:

    这可能无法很好地简化翻译,但这里有一个建议:

    在您的 user_controller#create 操作中,使用 if 语句包装您已有的所有内容。这是一个粗略的例子:

    class UserController < ApplicationController
    ...
    
    def create
      if User.find(params[:email])
        flash[:alert] = "This email address is in use.  You can ".concat(generate_reset_password_link(params[:email])
        render :action => 'new'
      else
        <your current code>
      end
    end
    

    在此之后,您将不得不编写一个辅助方法generate_reset_password_link,但我认为这主要是尊重 MVC 布局。控制器旨在与视图和模型交互。它有点违反 DRY 原则,因为您实际上绕过了validates_uniqueness_of :email,但您会得到一些自定义行为。如果您想制作更复杂的应用程序,我似乎无法 100% 实现 DRY,但也许您可以改进这一点并证明我错了 ;)

    您可能需要稍微调整一下,以便render :action =&gt; 'new' 将使用之前输入的数据重新填充自己(以防用户只是错误地输入了自己的电子邮件地址而它实际上不在系统中)。

    如果您决定使用这种方法,我会在 控制器和模型中添加注释,表明电子邮件的唯一性基本上在 2 个位置进行了检查。如果其他人必须查看此代码,它将帮助他们理解和维护它。

    【讨论】:

    • 我不想这么说,但这也不是一个优雅的解决方案。我希望错误与User 模型上的:email 字段特别相关。一个通用的flash[:alert] 可以工作,但我不希望在页面上的不同位置显示两个发生在一个字段中的错误。
    • 我不明白你会如何产生两个错误。您不会看到验证错误——您只会在尝试保存时看到。在这里,您会看到与该电子邮件特别相关的错误。这就是generate_reset_password_link(params[:email]) 会做的事情。您将用户提供的电子邮件地址提供给它,它会为您提供一个链接,以重置具有该特定电子邮件地址的用户的密码。如果您不想在 flash 部分中使用它,可以将其作为参数传递给视图并让它显示在您喜欢的位置。
    【解决方案3】:

    如果您使用的是 2.3.x,请将您对 error_messages 的调用替换为您自己的助手,用 UsersHelper 编写。它应该接受 FormBuilder 或 ActiveRecord 对象,并根据您的需要调整错误消息。您可以进行任意数量的自定义,也可以像 gsub 一样简单:

    def user_error_messages(f)
      find_error = "This email address is already in use."
      replacement = "This email address is already in use. #{link_to(...)} to reset your password"
      f.error_messages.sub(find_error, replacement).html_safe
    end
    

    如果您使用的是 Rails3,请创建一个辅助方法来简单地处理 @user.errors.full_messages,然后再将它们发送到视图。

    【讨论】:

      【解决方案4】:

      今天偶然发现:

      http://api.rubyonrails.org/classes/ActionDispatch/Routing/UrlFor.html

      如果您需要从其他地方(例如模型)访问此自动生成的方法,则可以通过在您的类中包含 ActionController::UrlFor 来实现:


      步骤 1

      了解模型的命名路径是困难的部分;这让我得到了大部分的帮助。

      现在我可以做一些类似的事情

      class User < ActiveRecord::Base
        include Rails.application.routes.url_helpers
      
        def reset_password_uri
          new_user_password_path(self)
        end
      end
      
      # User.find(1).reset_password_uri => "/users/password/new"
      

      第二步

      所以我们可以访问命名路由,但是现在我们需要将它注入到 YAML 消息中。

      这是我刚刚了解到的有关 YAML 变量的内容:

      # en.yml
      en:
        welcome: "Hello, %{username}!"
      
      # es.yml
      es:
        welcome: "¡Hola, %{username}!"
      

      我可以通过在t 方法中包含一个哈希来注入username

      <div id="welcome">
        <%= t :welcome, :username => @user.username %>
      </div>
      

      第三步

      现在我们只需要一种方法来为原始问题中描述的错误消息添加插值。这是我目前卡住的地方:(

      【讨论】:

        【解决方案5】:

        我知道这是一个老问题,但对于想要在错误消息中插入链接的未来用户,这里有一些对我有用的指南。

        首先,I18n 错误消息已被假定为 html 安全,因此您可以继续编写合适的错误消息。在此示例中,我将更改“电子邮件已被接收”消息。

        # config/locales/en.yml
        activerecord:
          errors:
            models:
              user:
                attributes:
                  email:
                    taken: 'has already been taken. If this is your email address, try <a href="%{link}">logging in</a> instead.'
        

        注意插值变量%link

        现在您只需在验证器中为该变量传递一个值,如下所示:

        # app/models/user.rb
        validates :email, :uniqueness => {:link => Rails.application.routes.url_helpers.login_path}
        

        (默认情况下,您在此处传递的任何选项都将作为变量自动发送到 I18n 翻译器,包括一些特殊的预填充变量,如 %value%model 等)

        就是这样!现在,您的错误消息中有一个链接。

        【讨论】:

        • 我一直想知道如何访问这样的 url/path 助手。非常感谢:)
        • github.com/svenfuchs/rails-i18n/blob/… 提供的完整翻译列表另外,如果您正在运行自定义验证并希望将其注册为唯一性,您可以传入 :taken,就像链接一样:user.errors.add(:email, :taken, :link)
        • 澄清@d3vkit 的评论,最后一个参数必须是哈希,而不是符号。例如:user.errors.add(:email, :taken, :link =&gt; '/some/path')
        • 消息显示为文本/未解释的 html。关于如何从 ActiveRecord 错误消息中输出 html 的任何想法?
        • @csi 您需要在渲染它的消息上调用 .html_safe
        【解决方案6】:

        经过数小时尝试使用设计解决 Rails 4 的问题,我意识到您可以直接将链接添加到验证中:

        # app/models/user.rb
        validates :username, presence: true, uniqueness: {:message => "username has already been taken - <a href='/users'>search users</a>" }
        

        在这种情况下,链接是我的用户索引。希望这会对某人有所帮助!

        【讨论】:

        • 您在模型验证代码中混合了 HTML、静态路由和本地化文本。
        • 感谢您的支持 - 对“正确”的做事方式仍然很陌生。我无法让它以任何其他方式工作。欢迎对替代解决方案的任何想法,修改 en.yml 对我不起作用。
        猜你喜欢
        • 2017-04-26
        • 1970-01-01
        • 1970-01-01
        • 2023-03-29
        • 2015-06-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-27
        相关资源
        最近更新 更多