web-dev-qa-db-ja.com

rails-工夫-処理-devise_error_messages

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

<%= devise_error_messages! %>

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

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

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

ありがとう。

124
AnApprentice

私は自分でこれを理解しようとしています。この問題がGithubに記録されていることがわかりました https://github.com/plataformatec/devise/issues/issue/504/#comment_574788

ホセは、devise_error_messsages!メソッドは単なるstub(実装が含まれていますが)であり、オーバーライド/置換することになっていると言っています。これが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オブジェクトは、実際にはdeviseが使用しているモデルです(図を参照)。

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

以下のソリューションは、現在の最新デバイス(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">&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 %>
33
Lukasz Muzyka

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

Devise全体で、render_with_scopeを使用した呼び出しがあることがわかります。これはdeviseによって定義されたメソッドであり、基本的に現在のスコープをレンダリングされた次のビューに適用すると信じています。

なぜこれが関連するのですか? Deviseには、resource.errorsnot@resource.errors)内にエラーが含まれています。いわば、箱から出して使用する場合は、デバイスは正常に機能します。

ユーザー管理動作の変更を開始すると、これらのエラーの問題が発生します。 Deviseに以前はなかった場所にredirect_toまたはrenderrender_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がアプリ全体でエラーメッセージを処理する方法を変更することになると思います。

23
Eric Hu

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

私はここに新しい小さな作品を持ち込みたいだけです。

そこで、「AnApprentice」が望んでいた結果を得るための簡単な方法を見つけました。

まず、Deviseプラグイン内で何かをカスタマイズする場合は、「\ Ruby_repertory\lib\Ruby\gems\1.9.1\gems\devise-version\app\controllers」からコードをコピーしてコピーすることを強くお勧めします| helpers | mailers ... "をプロジェクトに必要なファイルに追加します。

[編集]または、「通常の」deviseファイルからファイルを継承させることもできます。登録コントローラーは次のようになります。

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つだけ表示しました。

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

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

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

瀬谷、クルガー。

11
Kulgar

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

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

この正確なコードを試して、違いがあるかどうかを確認してください-異なるID属性が役立つ場合があります。

6
Scott

私はこれを思いつき、今のところうまくいっています。これにより、フラッシュにデバイスメッセージが追加されるため、通常どおり使用できます。私が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 -->
5
ddidier

特定のタイプの複数のフラッシュ(: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.")
3
Dany Marcoux

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! %>
2
B-M

私はこれを行うだけで、私のために働いた: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) %>

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

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
typeoneerror

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

uインラインエラーメッセージを表示するすべての行の下の角かっこ内のフィールド名を持つ各フィールドに配置します。

2
SSR

上記のEric Huの回答に、すべてのIfステートメントが使用されている場所に追加するだけで、代わりにこのようなことをしてください。

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

最初のエラーのみを表示して、コントローラーからデバイスエラーを表示します。

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

このチートを使って、他の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
botbot

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