рейки - Придумати - Поводження - devise_error_messages


125

на моїй сторінці редагування користувача є такий рядок:

<%= devise_error_messages! %>

Проблема полягає в тому, що це не видає помилок стандартним способом, який робить решта програми:

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

Моє запитання полягає в тому, як змусити повідомлення про помилку розробляти так, як інші, які використовують flash.each?

Дякую.


1
Зауважте, що Devise вже використовує спалах, як це робить інша програма. devise_error_messages - це не про флеш-повідомлення (інформація з останньої сторінки), а скоріше помилки перевірки з ActiveRecord Validation guides.rubyonrails.org/v2.3.11/…
Крістофер Оезбек

Відповіді:


135

Я сам намагаюся це зрозуміти. Щойно я знайшов цю проблему, зареєстровану на Github https://github.com/plataformatec/devise/isissue/issue/504/#comment_574788

Хосе каже, що devise_error_messsages!метод - це лише заглушка (хоча вона містить реалізацію) і що ми повинні її замінити / замінити. Було б добре, якби це було зазначено десь у вікі, саме тому я здогадуюсь, що є кілька людей на кшталт нас, які здогадувались.

Тож я спробую повторно відкрити модуль і переозначити метод, ефективно переосмисливши реалізацію за замовчуванням. Я дам вам знати, як це йде.

Оновлення

Так, це працює. Я створив app/helpers/devise_helper.rbі переоцінив її так:

module DeviseHelper
  def devise_error_messages!
    'KABOOM!'
  end
end

Знаючи це, я можу змінити метод відображення повідомлень про помилки так, як мені хочеться.

Щоб допомогти вам вирішити свою первісну проблему: Ось оригінал devise_helper.rbна Github . Погляньте, як проходять повідомлення про помилки:

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

Це повинно допомогти вам почати роботу. :)

Ще одне оновлення

resourceОб'єкт насправді модель , яка використовується за заповітом (Go Figure).

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 | %>
Підручник

@ColdTree ні, мета полягає в тому, щоб він працював як флеш-повідомлення. Вміння контролювати розмітку - це хороше рішення.
Бенджамін Аткін

... Я не думаю, що це відповідає на питання, хоча це хороша дослідницька робота.
deivid

37

Нижче рішення працює з останніми розробками на даний момент (4.1.1) та рейками 4.2.6. Але це так просто, що я не бачу причини, чому б він не працював 10 років з цього часу;)

Якщо ви хочете переробити повідомлення про помилки, щоб вони виглядали однаково у вашій програмі, я б рекомендував щось подібне (як я дізнався з Майклом Хартлом тут):

Створіть часткове повідомлення для помилок: 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 %>

Ви можете розмістити його в будь-якій формі. Замість того, щоб передавати ресурс придумування, ви можете передати змінну з форми так:

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

1
Напевно, найкраща та найінтуїтивніша відповідь.
Віктор

2
Прохолодний розчин. плюралізувати (object.errors.count, 'помилки' слід змінити на плюралізацію (object.errors.count, хоча «помилка»
mizurnix

1
@LukaszMuzyka в цьому рішенні .. мені потрібно видалити: перевіряється з user.rb .. ???
Вішал

1
@Vishal - ні. Наведене вище рішення просто використовує інший HTML для відображення повідомлень, які не змінюють жодної механіки Devise
Лукаш Музика,

1
@Vishal, коли ви використовуєте devise, він вже робить валідації, про які ви згадали, без зайвого коду. Вищевказане рішення полягає лише в тому, щоб змінити поведінку за замовчуванням. Ви повинні створити роботу в першу чергу. Ви впевнені, що дотримуєтесь інструкцій щодо інтеграції пристрою зі своїм проектом?
Лукаш Музика

22

Я знаю, що минуло деякий час з моменту опублікування цього питання, але я просто хотів прокоментувати те, що знайшов. Двоє людей, які вже відповіли, були мені величезною допомогою, і я просто хотів зробити свій внесок.

Ви побачите в усьому Інтернеті, що використовуються дзвінки render_with_scope. Я вважаю, що це метод, визначений методом devise, і в основному застосовує поточну область застосування до наступного поданого перегляду.

Чому це актуально? Devise містить ваші помилки в межах resource.errors( не @resource.errors ). Придумайте, що це добре, якщо ви хочете використовувати його поза коробкою, так би мовити.

Проблеми з цими помилками виникають, якщо ви почнете змінювати свою поведінку управління користувачем. Додавши 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 обробляє повідомлення про помилки, щоб це робити протягом усього мого додатка, оскільки, здається, набагато чистіше мати одну систему повідомлень про помилки замість двох.


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 \ kontroleri | помічники | mailer ... "до потрібного файлу у вашому проекті.

[Редагувати] Або ви можете зробити ваш файл успадкованим із "звичайних" файлів розробки ... Як ... скажімо ... Ви хочете перезаписати лише одну функцію в devise / registrations_controller.rb, перший рядок у вашому користувальницькому користувальницькому режимі Контролером реєстрації буде:

class Users::RegistrationsController < Devise::RegistrationsController

[Редагувати 7 серпня 2013] Тепер 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_mes! метод для того, щоб використовувати його в моїх поглядах і змусити його відображати трюк, який я пояснив вище.

Отже, ось мій метод:

 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 error, тому що часто перший є єдиним релевантним (наприклад, коли користувач забуває одне обов'язкове поле).

Я рахую ці "унікальні" помилки, і я роблю HTML-заголовок H2, використовуючи плюралізацію, і ставлю її перед списком помилок.

Тож тепер я можу використовувати "devise_error_messages!" як за замовчуванням, і він робить саме те, що я вже робив раніше.

Якщо ви хочете отримати доступ до певного повідомлення про помилку у вашому перегляді, я зараз рекомендую використовувати безпосередньо "resource.errors [: attribute] .first" або будь-яке інше.

Сея, Кулгар.


6

Я використовую Devise in Rails 3, і ваш флеш-код майже ідентичний тому, що у мене є. У моєму додатку код працює так, як очікувалося; тобто Повідомлення про помилки виводяться з рештою моїх флеш-повідомлень:

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

Спробуйте цей точний код і подивіться, чи він має значення - різний атрибут ID може допомогти.


дякую, але це в підсумку нічого не показує. "<% = devise_error_messages!%>" видає помилку. вище нічого не зробив? ідеї?
AnApprentice

Вибачення - Я тільки що бачив ваш коментар. Якщо чесно, мені не вистачає ідей. Я припускаю, що ви переглядали джерело у своєму браузері та перевіряли створений HTML? Про всяк випадок, якщо щось заховано CSS. Використовуєте останню версію Devise 1.1.3?
Скотт

5

Я підійшов до цього, і це працює до цих пір. Це додає розроблені повідомлення у спалах, тому їх можна використовувати як завжди. Зверніть увагу, що я новачок у Ruby and 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

3

Якщо ви хочете відобразити більше одного спалаху заданого типу (: сповіщення,: сповіщення тощо) і не витрачати свій час на спроби змінити дорогоцінне зображення дорогоцінного каміння, це рішення, яке я використав із Devise. Я впевнений, що його можна використовувати з будь-яким самоцвітом, який використовує флеш-повідомлення.

Перше, що потрібно зробити у своєму 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>

Третє, що потрібно робити, коли ви хочете додати флеш-повідомлення в будь-який контролер:

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

Але як змусити повідомлення "Розробити", щоб викликати flash_messages, а не зберігати об'єкт помилки.
Крістофер Оезбек

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 [: сигнал]
BM

2

Щоправда, трохи хакіт, але я використовую цей помічник (додаток / 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

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, замість цього зробіть щось подібне.

# 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

я просто роблю це, працював для мене: у додатку / 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! %>і не відповісти на питання. Питання задає питання, як застосувати спалах до кожного повідомлення.
Марк

0
  1. Видаліть "devise_error_messages!" з шаблону "app / views / users / password / new".
  2. Створіть користувальницький контролер для свого користувача (додаток / контролери / користувачі / 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 відображав повідомлення про помилки як тост, я додав цей код у додаток / 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 використовує частковий "under" 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-тег. Зазвичай ви додаєте лише рядок у своє флеш-повідомлення.
AZ.

Можливо, але нова лінія все ж працює. Запропонуйте інше рішення, якщо воно вам не подобається.
YoyoS

-2

Я щойно оголосив devise_error_messages! як порожній помічник. І вручну вибирали та обробляли помилки в загальній частковій помилці для мого додатка. Здавалося, що це найпростіше рішення, і мені не доведеться проходити всі файли розробки та видаляти виклик до обробника помилок.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.