rails - Devise - Handling - devise_error_messages


125

di halaman edit pengguna saya, ada baris sebagai berikut:

<%= devise_error_messages! %>

Masalahnya adalah ini tidak menghasilkan kesalahan dengan cara standar yang dilakukan oleh aplikasi lainnya:

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

Pertanyaan saya adalah, bagaimana cara saya mendapatkan pesan kesalahan yang dirancang agar berfungsi seperti yang lain yang menggunakan flash.each?

Terima kasih.


1
Harap perhatikan bahwa Devise sudah menggunakan flash seperti yang dilakukan aplikasi lainnya. devise_error_messages bukan tentang pesan flash (informasi dari halaman terakhir), melainkan kesalahan validasi dari ActiveRecord Validation guides.rubyonrails.org/v2.3.11/…
Christopher Oezbek

Jawaban:


135

Saya mencoba mencari tahu sendiri. Saya baru saja menemukan masalah ini masuk di Github https://github.com/plataformatec/devise/issues/issue/504/#comment_574788

Jose mengatakan bahwa devise_error_messsages!metode itu hanya sebuah rintisan (meskipun mengandung implementasi) dan kita seharusnya menimpanya / menggantinya. Akan lebih baik jika ini ditunjukkan di suatu tempat di wiki, itulah sebabnya saya kira ada beberapa orang seperti kita yang telah menebak.

Jadi saya akan mencoba membuka kembali modul dan mendefinisikan kembali metode, secara efektif mengesampingkan implementasi default. Saya akan memberi tahu Anda bagaimana hasilnya.

Memperbarui

Yap, itu berhasil. Saya membuat app/helpers/devise_helper.rbdan mengesampingkannya seperti ini:

module DeviseHelper
  def devise_error_messages!
    'KABOOM!'
  end
end

Jadi mengetahui hal ini, saya dapat memodifikasi metode untuk menampilkan pesan kesalahan seperti yang saya inginkan.

Untuk membantu Anda memecahkan masalah asli Anda: Inilah yang asli devise_helper.rbdi Github . Lihatlah bagaimana pesan kesalahan dilalui:

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

Itu akan membantu Anda memulai. :)

Pembaruan lain

The resourceobjek sebenarnya model yang sedang digunakan oleh merancang (gambar go).

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

Tampaknya juga didefinisikan dalam lingkup yang lebih tinggi (mungkin berasal dari controller), sehingga dapat diakses di berbagai tempat.

Di mana saja di Pembantu Anda

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

Pandangan Anda

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

Saya baru saja mencoba ini tetapi itu tidak berhasil. Tujuannya adalah untuk mendapatkan kesalahan untuk keluaran di sini: <% flash.each do | key, value | %>
AnApprentice

@ColdTree tidak, tujuannya adalah agar berfungsi seperti pesan kilat. Mampu mengendalikan markup adalah solusi yang baik.
Benjamin Atkin

... Saya tidak berpikir ini menjawab pertanyaan meskipun ini adalah pekerjaan penelitian yang bagus.
membagi

37

Solusi di bawah ini berfungsi dengan rancangan terbaru seperti yang sekarang (4.1.1) dan Rails 4.2.6. Tetapi sangat sederhana sehingga saya tidak melihat alasan mengapa itu tidak akan bekerja 10 tahun dari sekarang;)

Jika Anda ingin mendaur ulang pesan kesalahan Anda dan membuatnya terlihat sama di aplikasi Anda, saya akan merekomendasikan sesuatu seperti ini (cara saya telah belajar dengan Michael Hartl tut):

Buat sebagian untuk pesan kesalahan: layouts/_error_messages.html.erb Letakkan di dalam kode berikut (di sini saya menggunakan beberapa kelas 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 %>

Sekarang Anda memiliki sesuatu yang dapat didaur ulang dan Anda dapat menggunakannya di seluruh papan. Alih-alih merancang standar:

<%= devise_error_messages! %>

Sebut saja dalam formulir Anda seperti ini:

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

Anda bisa menaruhnya dalam bentuk apa pun. Alih-alih melewati sumber daya merancang Anda bisa melewati variabel dari formulir Anda seperti ini:

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

1
Mungkin jawaban terbaik dan paling intuitif yang pernah ada.
Victor

2
Solusi keren pluralize (object.errors.count, 'error' harus diubah menjadi pluralize (object.errors.count, meskipun 'error'
mizurnix

1
@LukaszMuzyka dalam solusi ini .. apakah saya harus menghapus: validatable dari user.rb .. ???
Vishal

1
@Vishal - tidak. Solusi di atas hanya menggunakan HTML yang berbeda untuk menampilkan pesan-pesan yang tidak mengubah mekanisme Rancangan
Lukasz Muzyka

1
@Vishal ketika Anda menggunakan merancang ini sudah melakukan validasi yang Anda sebutkan tanpa kode tambahan. Solusi di atas hanya untuk mengganti perilaku merancang standar. Anda harus memiliki rencana kerja sejak awal. Apakah Anda yakin jika Anda telah mengikuti instruksi untuk mengintegrasikan rancangan dengan proyek Anda?
Lukasz Muzyka

22

Saya tahu sudah lama sejak pertanyaan ini diposting, tetapi saya hanya ingin mengomentari apa yang saya temukan. Dua orang yang sudah menjawab sangat membantu saya dan saya hanya ingin berkontribusi.

Anda akan melihat seluruh Menyusun bahwa ada panggilan menggunakan render_with_scope. Saya percaya ini adalah metode yang didefinisikan oleh merancang dan pada dasarnya menerapkan lingkup saat ini untuk tampilan selanjutnya yang diberikan.

Mengapa ini relevan? Rencanakan berisi kesalahan Anda dalam resource.errors( tidak @resource.errors ). Rancang bekerja dengan baik jika Anda ingin menggunakannya di luar kotak, jadi untuk berbicara.

Masalah dengan kesalahan ini muncul jika Anda mulai mengubah perilaku manajemen pengguna Anda. Dengan menambahkan redirect_toatau render(bukannya render_with_scope) di mana Rancangan sebelumnya tidak memiliki satu, Anda pada dasarnya membuang pesan kesalahan. Ini membuat Devise tidak ramah untuk modifikasi, menurut saya.

Solusi saya adalah ini

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

dan

# 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

Blok kode yang terakhir mengambil pesan kesalahan Devise sebagai sebuah array dan menambahkannya flash[:notice](sebagai sebuah array). Setiap pesan akan dicetak satu baris sekaligus. Jika saya punya waktu, saya pikir saya akan mengubah bagaimana Rancangan menangani pesan kesalahan untuk melakukan ini di seluruh aplikasi saya, karena tampaknya jauh lebih bersih untuk memiliki satu sistem pesan kesalahan daripada dua.


3
Terima kasih banyak untuk itu, saya membenturkan kepala ke dinding karena mencoba melakukan itu.
Lucas

1
Sekarang 5 tahun kemudian dan respons ini menyelamatkan bacon saya. Terima kasih banyak @ eric-hu.
marcamillion

12

Saya memecahkan ini mirip dengan YoyoS, dengan membuat app/helpers/devise_helper.rbdan menempatkan ini di dalamnya:

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

Bekerja!


11

Saya hanya ingin membawa potongan kecil baru di sini:

Jadi saya menemukan cara yang lebih mudah untuk mendapatkan hasil yang diinginkan "AnApprentice".

Pertama-tama, jika Anda ingin mengkustomisasi apa pun dalam plug-in Devise, saya sangat menyarankan Anda untuk menyalin melewati kode dari "\ Ruby_repertory \ lib \ ruby ​​\ permata \ 1.9.1 \ permata \ devise-version \ app \ controllers | helpers | mailer ... "ke file yang Anda inginkan dalam proyek Anda.

[Sunting] Atau Anda dapat membuat file Anda mewarisi dari file merancang "normal" ... Seperti ... katakan ... Anda ingin menimpa hanya satu fungsi di dalam merancang / registrations_controller.rb, baris pertama kustom Pengguna Anda pengendali pendaftaran adalah:

class Users::RegistrationsController < Devise::RegistrationsController

[Sunting 7 Agustus 2013] Sekarang Rancangan bahkan menyediakan alat untuk menghasilkan pengontrol: https://github.com/plataformatec/devise/wiki/Tool:-Generate-and-customize-controllers

Jadi ... ngomong-ngomong ... Saya berhasil mendapatkan apa yang "AnApprentice" inginkan hanya menulis ini (untuk solusi yang lebih bersih, lihat edit besar berikut):

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

      return resource.errors
   end
end

Dan, dalam pandangan saya, baris berikutnya bekerja dengan cukup baik:

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

Baiklah ... maka Anda dapat mengakses kesalahan untuk atribut tertentu seperti ini:

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

Dan ... Trik kecil untuk hanya memiliki satu kesalahan (yang pertama ditangkap) muncul per atribut:

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

Saya tahu sudah lama sejak pertanyaan ini diposting, tapi saya pikir itu akan membantu banyak pengguna merancang :).

Edit Besar:

Karena saya suka memperpanjang kode saya, membuatnya lebih bersih dan membaginya dengan orang lain, saya baru-baru ini ingin mengubah devise_error_messages! metode untuk menggunakannya dalam pandangan saya dan membuatnya menampilkan trik yang saya jelaskan di atas.

Jadi, inilah metode saya:

 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

Bukan masalah besar di sini, saya menggunakan kembali kode yang saya tulis dalam pandangan saya untuk menunjukkan hanya satu atribut kesalahan pey, karena sering yang pertama adalah satu-satunya yang relevan (seperti ketika pengguna lupa satu bidang yang diperlukan).

Saya menghitung kesalahan "unik" dan saya membuat judul H2 HTML menggunakan pluralize dan menaruhnya SEBELUM daftar kesalahan.

Jadi sekarang, saya bisa menggunakan "devise_error_messages!" sebagai default dan itu membuat persis apa yang sudah saya render sebelumnya.

Jika Anda ingin mengakses pesan kesalahan tertentu dalam tampilan Anda, saya sekarang merekomendasikan untuk menggunakan langsung "resource.errors [: atribut] .first" atau apa pun.

Seya, Kulgar.


6

Saya menggunakan Rancangan di Rails 3 dan kode flash Anda cukup identik dengan apa yang saya dapatkan. Di aplikasi saya, kode berfungsi seperti yang diharapkan; yaitu merancang pesan kesalahan adalah output dengan sisa pesan flash saya:

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

Coba kode persis ini dan lihat apakah ada bedanya - atribut ID yang berbeda mungkin membantu.


terima kasih tetapi akhirnya tidak menunjukkan apa-apa. "<% = devise_error_messages!%>" menghasilkan kesalahan. di atas tidak melakukan apa pun? ide ide?
AnApprentice

Permintaan maaf - Saya baru saja melihat komentar Anda. Sejujurnya, saya kehabisan ide. Saya berasumsi Anda telah melihat sumber di browser Anda dan memeriksa HTML yang dihasilkan? Kalau-kalau ada sesuatu yang disembunyikan oleh CSS. Apakah Anda menggunakan versi terbaru dari Rancangan 1.1.3?
Scott

5

Saya datang ke ini dan itu bekerja sejauh ini. Itu menambahkan merancang pesan ke flash, sehingga dapat digunakan seperti biasa. Harap perhatikan bahwa saya baru di Ruby dan 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

Edit:

Maaf saya menjalankan penjaga dan beberapa perilaku yang tidak diinginkan hadir. Karena after_filterdipanggil setelah rendering sehingga tidak berfungsi seperti yang diharapkan. Jika seseorang tahu cara memanggil metode setelah tindakan tetapi sebelum rendering ...

Tapi Anda bisa menggunakan sesuatu seperti itu sebagai gantinya:

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

Di views/shared/_messages.html.erb

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

1
+1 Jawaban bagus. Saya pikir ini jelas merupakan solusi terbersih dan cocok dengan arsitektur saya saat ini. Jawabannya tidak begitu jelas - pada dasarnya segala sesuatu sebelum pengeditan harus diabaikan (dan dihapus atau dicoret melalui imo).
zelanix

3

Jika Anda ingin dapat menampilkan lebih dari satu flash dari jenis yang diberikan (: waspada,: pemberitahuan, dll ...) dan tidak membuang waktu Anda mencoba memodifikasi perilaku permata, ini adalah solusi yang saya gunakan dengan Merancang. Saya cukup yakin itu bisa digunakan dengan permata apa pun yang menggunakan pesan flash.

Hal pertama yang harus dilakukan, di application_controller.rb Anda, tambahkan ini:

  # Adds the posibility to have more than one flash of a given type
  def flash_message(type, text)
    flash[type] ||= []
    flash[type] << text
  end

Hal kedua yang harus dilakukan, menampilkan pesan flash Anda dengan ini di application.html.erb (atau di mana pun Anda inginkan):

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

Hal ketiga yang harus dilakukan, setiap kali Anda ingin menambahkan pesan flash di pengontrol apa pun, lakukan ini:

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

Tapi bagaimana cara mendapatkan pesan Rancangan untuk memanggil flash_messages alih-alih menyimpan objek kesalahan.
Christopher Oezbek

3

Buat 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

Menurut Anda, gantikan

<%= devise_error_messages! %>

Untuk:

<% devise_error_messages! %>

1
Sebenarnya, Anda harus menggunakan: flash.now [: alert]
BM

2

Memang, sedikit hacky, tapi saya menggunakan pembantu ini (app / helpers / devise_helper.rb) untuk mengambil flash dan menggunakan yang jika diatur maka default ke resource.errors. Ini hanya berdasarkan pada pembantu yang ada di lib merancang.

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

Jika Anda mencari dukungan dari devise_error_messages maka Anda dapat melakukannya dengan menambahkan ke resource.errors

Jika Anda menumpang pengendali registrasi, mungkin terlihat seperti

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

Cara yang sangat mudah untuk menampilkan pesan kesalahan untuk setiap bidang

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

letakkan untuk setiap bidang dengan nama bidang dalam kurung siku di bawah setiap baris tempat Anda ingin menampilkan pesan kesalahan sebaris.


1

Untuk menunjukkan kesalahan Anda dari controller Anda dengan hanya kesalahan pertama yang muncul.

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

1

Hanya untuk menambahkan jawaban Eric Hu di atas di mana semua pernyataan If digunakan, lebih baik lakukan sesuatu seperti ini sebagai gantinya.

# 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

saya cukup melakukan ini, bekerja untuk saya: di app / helpers / , saya membuat file 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

di semua file tampilan saya ubah

<%= devise_error_messages! %>

untuk:

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

bagi saya itu membuat dalam pandangan saya edit dan pengguna baru:

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

harap ini akan membantu Anda;)


Saya benar-benar tidak mengerti bagaimana ini melakukan sesuatu? apakah ini perilaku standar? Ini hanyalah cara lain untuk melakukan <%= devise_error_messages! %>dan tidak menjawab pertanyaan. Pertanyaannya adalah bagaimana menerapkan flash ke setiap pesan.
Markus

0
  1. Hapus "devise_error_messages!" dari templat "app / views / users / passwords / new".
  2. Buat pengontrol khusus untuk pengguna Anda (app / controllers / users / passwords_controller.rb) dan setelah filter tambahkan kesalahan array 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

Saya suka melakukannya persis seperti yang dilakukan di controller Rancangan lain dengan cheat ini.

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

Agar materialisecss menampilkan pesan kesalahan rancangan sebagai roti panggang, saya menambahkan kode ini di 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

Saya yakin mereka akan menjadi cara terbersih untuk menulisnya tetapi terbangun dengan sempurna


0

DeviseHelper#devise_error_messages! sudah usang dan akan dihapus di versi utama berikutnya.

Rancang sekarang menggunakan parsial bawah devise/shared/error_messagesuntuk menampilkan pesan kesalahan secara default, dan membuatnya lebih mudah untuk disesuaikan. Perbarui tampilan Anda untuk mengubah panggilan dari:

      <%= devise_error_messages! %>

untuk:

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

-1

Saya baru saja membuat app/helpers/devise_helper.rbseperti John tetapi mengalahkan metode seperti itu:

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

Dengan ini saya tidak perlu memodifikasi yang lain. Apakah itu ide yang buruk? Saya baru mengenal rail, jangan ragu untuk mengoreksi saya. Terima kasih.


Ini tidak akan berfungsi seperti yang diinginkan pesan flash sekarang berisi tag html Situs. Biasanya Anda hanya memasukkan string dalam pesan flash Anda.
AZ.

Mungkin, tetapi baris baru masih berfungsi. Usulkan solusi lain jika Anda tidak suka yang ini.
YoyoS

-2

Saya baru saja menyatakan devise_error_messages! sebagai penolong kosong. Dan secara manual mengambil dan menangani kesalahan dalam _errors umum parsial untuk aplikasi saya. Tampak seperti solusi paling sederhana dan saya tidak harus melalui semua file devise dan menghapus panggilan ke penangan kesalahan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.