レール-デバイス-処理-devise_error_messages


125

私のユーザー編集ページには、次のような行があります。

<%= devise_error_messages! %>

問題は、これがアプリの他の部分が行う標準的な方法でエラーを出力しないことです:

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

私の質問は、flash.eachを使用する他のデバイスと同様にdeviseエラーメッセージを機能させるにはどうすればよいですか?

ありがとう。


1
アプリの他の部分と同じように、Deviseはすでにフラッシュを使用していることに注意してください。devise_error_messagesは、フラッシュメッセージ(最後のページからの情報)に関するものではありませんが、ActiveRecordの検証からではなく、検証エラーguides.rubyonrails.org/v2.3.11/...
クリストファーOezbek

回答:


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、2011年

@ColdTreeいいえ、目標はフラッシュメッセージのように機能することです。マークアップを制御できることは良い解決策です。
Benjamin Atkin

...これは良い調査研究ですが、これは質問に答えるとは思いません。
deivid 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 %>

あなたはそれをどんな形にでも置くことができます。deviseリソースを渡す代わりに、次のようにフォームから変数を渡すことができます。

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

1
おそらく最高かつ最も直感的な答えです。
ビクター

2
クールなソリューション。pluralize(object.errors.count、 'errors'はpluralize(object.errors.count、 'error'に変更する必要があります
mizurnix

1
このソリューションの@LukaszMuzyka .. user.rb .. ??から:validatableを削除する必要がありますか?
Vishal

1
@ヴィシャル-いいえ。上記のソリューションは、異なるHTMLを使用してメッセージを表示するだけで、Deviseの仕組みを変更しません
Lukasz Muzyka

1
@Vishalは、deviseを使用している場合、追加のコードなしで、すでに述べた検証をすでに行っています。上記の解決策は、デフォルトのデバイスの動作をオーバーライドすることだけです。そもそも工夫が必要です。deviseをプロジェクトに統合するための指示に従っているかどうか、確信がありますか?
Lukasz Muzyka 2016

22

この質問が投稿されてからしばらく経ちましたが、私が見つけたものについてコメントしたいと思います。すでに回答していただいた2人の方は私に多大な助けをしてくれたので、貢献したいと思いました。

Devise全体を通して、を使用しrender_with_scopeた通話があることがわかります。これはdeviseによって定義された方法であり、基本的には現在のスコープを次にレンダリングされるビューに適用します。

これはなぜ関連があるのですか?Devise内にエラーが含まれていますresource.errors(ではありません @resource.errors)。言うまでもなく、すぐに使用したい場合はDeviseが正常に機能します。

ユーザー管理動作の変更を開始すると、これらのエラーの問題が発生します。以前はDeviseになかったredirect_toorのrender代わりにorを追加することで、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](配列として)。各メッセージは一度に1行ずつ出力されます。時間があれば、アプリ全体でDeviseがエラーメッセージを処理する方法を変更するつもりです。エラーメッセージシステムを2つではなく1つにするほうがずっときれいに思えるからです。


3
本当にありがとうございました。そうしようとして壁に頭をぶつけていました。
ルーカス、

1
それは今から5年後のことであり、この反応は私のベーコンを救いました。@ eric-huに感謝します。
marcamillion

12

これをYoyoSと同じように作成し、app/helpers/devise_helper.rbこれを作成して配置しました。

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ファイルからファイルを継承させることもできます...例...言う...ユーザーカスタムの最初の行であるdevise / registrations_controller.rb内の1つの関数のみを上書きしたい登録コントローラーは次のようになります。

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

そして...属性ごとに1つだけエラー(最初に検出される)を表示するための小さなトリック:

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

ここで大したことはありません。私が自分のビューで記述したコードを再利用して、1つのエラーpey属性のみを表示しました。これは、多くの場合、最初の属性のみが関連するためです(ユーザーが1つの必須フィールドを忘れた場合など)。

私はそれらの「ユニークな」エラーを数え、複数形を使用してH2 HTMLタイトルを作成し、それをエラーリストの前に配置しています。

これで、「devise_error_messages!」を使用できます。デフォルトのものとして、それは私が以前にレンダリングしていたものを正確にレンダリングします。

ビューで特定のエラーメッセージにアクセスする場合は、「resource.errors [:attribute] .first」などを直接使用することをお勧めします。

瀬谷、クルガー。


6

Rails 3のDeviseを使用していますが、フラッシュコードは私が入手したものとほとんど同じです。私のアプリでは、コードは期待どおりに機能します。つまり、残りのフラッシュメッセージとともにDeviseエラーメッセージが出力されます。

<% 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を使用していますか?
スコット

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で使用したソリューションです。フラッシュメッセージを使用するすべての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(または任意の場所)にフラッシュメッセージを表示します。

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

3番目に行うことは、フラッシュメッセージを任意のコントローラーに追加する場合は常に、次のようにします。

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

確かに少しハッキーですが、私はこのヘルパー(app / helpers / devise_helper.rb)を使用してフラッシュを取得し、フラッシュを使用して、デフォルトのに設定している場合はそれを使用しresource.errorsます。これは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

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

それがあなたを助けることを願っています;)


これがどのように機能するのか本当に理解できませんか?これは標準的な動作ですか?これはのもう1つの方法であり<%= devise_error_messages! %>、質問には答えません。質問は、各メッセージにフラッシュを適用する方法を尋ねます。
マーク

0
  1. 「devise_error_messages!」を削除します 「app / views / users / passwords / new」テンプレートから。
  2. ユーザー(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

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がトーストとしてデバイスエラーメッセージを表示するために、このコードを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ジョンのようなものを作成しましたが、そのようなメソッドをオーバーライドしました:

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

これにより、他に何も変更する必要はありません。それは悪い考えですか?私はレールの初心者です。修正するのをためらわないでください。ありがとう。


これは期待どおりに機能しません。フラッシュメッセージにHTMLタグ<br>が含まれるようになりました。通常、あなたはフラッシュメッセージに文字列を入れるだけです。
AZ。

おそらく、しかし、新しいラインはまだ機能します。これが気に入らない場合は、別の解決策を提案してください。
YoyoS、2015年

-2

devise_error_messagesを宣言しました!空のヘルパーとして。また、アプリケーションの一般的な_errorsパーシャルでエラーを手動でフェッチして処理しました。最も単純な解決策のように見え、すべてのdeviseのファイルを調べて、エラーハンドラーへの呼び出しを削除する必要はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.