Рельсы - Разработка - Обработка - devise_error_messages

на моей странице редактирования пользователя есть следующая строка:

<%= devise_error_messages! %>

Проблема заключается в том, что это не выводит ошибки стандартным способом, который выполняет остальная часть приложения:

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

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

Спасибо.

Ответ 1

Я пытаюсь понять это сам. Я только что нашел эту проблему в журнале Github https://github.com/plataformatec/devise/issues/issue/504/#comment_574788

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

Итак, я собираюсь попробовать снова открыть модуль и переопределить этот метод, эффективно переопределяя реализацию по умолчанию. Я дам вам знать, как это происходит.

Update

Да, это работает. Я создал 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 - это фактически модель, которая используется при разработке (рисунок).

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>

Ответ 2

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

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

Создать частичное для сообщений об ошибках:    layouts/_error_messages.html.erb Вставьте следующий код (здесь я использую несколько классов загрузки):

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

Ответ 3

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

Во всем приложении Devise вы увидите, что есть вызовы с помощью render_with_scope. Я считаю, что это метод, определенный при разработке, и в основном применяет текущую область к следующему визуализированному представлению.

Почему это актуально? В Devise содержатся ваши ошибки в resource.errors ( не @resource.errors). Devise отлично работает, если вы хотите использовать его из коробки, так сказать.

Проблемы с этими ошибками возникают, если вы начнете изменять поведение пользователя. Добавив redirect_to или render (вместо render_with_scope), в котором у Devise ранее не было одного, вы в основном отбрасываете сообщения об ошибках. Это делает 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 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 обрабатывает сообщения об ошибках, чтобы сделать это во всем моем приложении, так как кажется, что гораздо чище иметь одну систему сообщений об ошибках вместо двух.

Ответ 4

Я просто хочу принести сюда новый кусочек:

Итак, я нашел более простой способ получить результат, который хотел получить "AnApprentice".

Прежде всего, если вы хотите настроить что-либо в подключаемом модуле Devise, я настоятельно рекомендую вам скопировать код из "\ Ruby_repertory\lib\ruby ​​\ gems\1.9.1\gems\devise-version\app\controllers | helpers | mailers..." в файл, который вы хотите в своем проекте.

[Изменить] Или вы можете сделать свой файл наследованием от "нормальных" файлов разработки... Как... сказать... Вы хотите переписать только одну функцию в devise/registrations_controller.rb, первую строку ваш пользовательский контроллер регистрации будет:

class Users::RegistrationsController < Devise::RegistrationsController

[Edit August 7th 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_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

Здесь нет большого смысла, я снова использовал код, который я написал в своем представлении, чтобы показать только один атрибут pey ошибки, потому что часто первый из них является единственным релевантным (например, когда пользователь забывает одно обязательное поле).

Я подсчитываю эти "уникальные" ошибки, и я делаю HTML-заголовок H2, используя pluralize и помещая его перед списком ошибок.

Итак, теперь я могу использовать "devise_error_messages!" как и по умолчанию, и делает именно то, что я уже показывал раньше.

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

Сея, Kulgar.

Ответ 5

Я решил это аналогично 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

Работали!

Ответ 6

Я использую Devise in Rails 3, и ваш flash-код в значительной степени идентичен тому, что у меня есть. В моем приложении код работает так, как ожидалось; Т.е. сообщения об ошибках проекта выводятся вместе с остальными моими флэш-сообщениями:

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

Попробуйте этот точный код и посмотрите, не имеет значения - может помочь другой атрибут ID.

Ответ 7

Я подошел к этому, и он работает до сих пор. Это добавляет готовые сообщения к вспышке, поэтому ее можно использовать как обычно. Пожалуйста, учтите, что я новичок в 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

Edit:

Извините, что я работал охранником, и появилось какое-то нежелательное поведение. Поскольку 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 -->

Ответ 8

Если вы хотите отображать более одной вспышки данного типа (: alert,: notice и т.д.) и не тратьте время на то, чтобы изменить поведение драгоценного камня, это решение, которое я использовал с Разрабатывают. Я уверен, что он может быть использован с любым камнем, который использует флэш-сообщения.

Прежде всего, в вашем приложении 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.")

Ответ 9

Я просто делаю это, работал у меня: в приложении/помощниках/, я создаю файл 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

во всех файлах просмотра я change

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

надеюсь, что это вам поможет;)

Ответ 10

Допустимо, немного взломано, но я использую этот помощник (app/helpers/devise_helper.rb), чтобы захватить вспышки и использовать те, которые установлены, а затем по умолчанию - resource.errors. Это просто основано на помощнике, который в разработке 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

Ответ 11

Очень простой способ отображения сообщения об ошибке для каждого поля

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

поместить для каждого поля с именем поля в квадратной скобке ниже каждой строки, где u хочет отображать встроенное сообщение об ошибке.

Ответ 12

Чтобы показать свою ошибку разработки с вашего контроллера, вы увидите только первую ошибку.

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

Ответ 13

Если вы хотите скомпоновать 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 

Ответ 14

Просто добавьте к Эрику Ху ответ выше, где используются все операторы 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 %>

Ответ 15

  • Удалите "devise_error_messages!" из шаблона "app/views/users/passwords/new".
  • Создайте пользовательский контроллер для своего пользователя (приложение/контроллеры/users/passwords_controller.rb), а в после фильтрации добавьте ошибки в 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

Ответ 16

Мне нравится делать это так же, как это делалось в другом контроллере 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 %>

Ответ 17

Я только что создал app/helpers/devise_helper.rb как Джон, но перепробовал такой метод:

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

С этим мне не нужно ничего изменять. Это плохая идея? Я новичок в рельсах, не стесняйтесь меня поправлять. Спасибо.

Ответ 18

Я только что объявил devise_error_messages! как пустой помощник. И вручную извлекал и обрабатывал ошибки в общем _errors, частичном для моего приложения. Казалось, что это самое простое решение, и мне не нужно проходить все файлы разработки и удалять вызов обработчику ошибок.