web-dev-qa-db-fra.com

rails - Concevoir - Manipulation - devise_error_messages

dans ma page d'édition utilisateur, il y a une ligne comme suit:

<%= devise_error_messages! %>

Le problème est que cela ne génère pas d'erreurs de la manière standard utilisée par le reste de l'application:

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

Ma question est la suivante: comment puis-je faire en sorte que le message d'erreur de schéma fonctionne comme les autres utilisateurs de flash.each?

Merci.

124
AnApprentice

J'essaie de comprendre cela moi-même. Je viens de trouver ce problème connecté à Github https://github.com/plataformatec/devise/issues/issue/504/#comment_574788

Jose dit que devise_error_messsages!Methode est juste un stub (bien qu'il contienne une implémentation) et que nous sommes supposés le remplacer/le remplacer. Cela aurait été bien si cela avait été signalé quelque part dans le wiki, c'est pourquoi je suppose qu'il y a quelques personnes comme nous qui ont deviné.

Je vais donc essayer de rouvrir le module et de redéfinir la méthode, en remplaçant efficacement l'implémentation par défaut. Je vous ferai savoir comment ça se passe.

Mise à jour

Oui, ça marche. J'ai crée app/helpers/devise_helper.rb et l'a remplacé comme suit:

module DeviseHelper
  def devise_error_messages!
    'KABOOM!'
  end
end

Sachant cela, je peux modifier la méthode pour afficher les messages d'erreur comme je le souhaite.

Pour vous aider à résoudre votre problème initial: Voici l’original devise_helper.rb sur Github . Jetez un coup d'œil à la façon dont les messages d'erreur sont traversés:

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

Cela devrait vous aider à démarrer. :)

Une autre mise à jour

L'objet resource est en fait le modèle utilisé par le système (voir figure).

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

Il semble également être défini dans une portée plus élevée (provenant probablement du contrôleur), afin de pouvoir y accéder à divers endroits.

N'importe où dans votre assistant

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

Ta vue

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

La solution ci-dessous fonctionne avec les dernières versions actuelles (4.1.1) et Rails 4.2.6. Mais est si simple que je ne vois pas pourquoi il ne fonctionnerait pas dans 10 ans. à présent;)

Si vous souhaitez recycler vos messages d'erreur et leur donner la même apparence dans votre application, je vous recommanderais quelque chose comme ceci (comme je l'ai appris avec Michael Hartl tut):

Création partielle pour les messages d'erreur: layouts/_error_messages.html.erb Mettez le code suivant à l'intérieur (ici, j'utilise quelques bootstrap 3 classes):

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

Maintenant, vous avez quelque chose de recyclable et vous pouvez l'utiliser à tous les niveaux. Au lieu d'un équipement standard:

<%= devise_error_messages! %>

Appelez-le dans votre formulaire comme ceci:

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

Vous pouvez le mettre sous n'importe quelle forme. Au lieu de transmettre la ressource de conception, vous pouvez transmettre une variable de votre formulaire comme suit:

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

Je sais que cela fait longtemps que cette question n'a pas été posée, mais je voulais simplement commenter ce que j'ai trouvé. Les deux personnes qui ont déjà répondu ont été d'une aide précieuse pour moi et je voulais simplement contribuer.

Vous verrez tout au long de Devise qu'il y a des appels utilisant render_with_scope. Je crois que ceci est une méthode définie par invent et applique fondamentalement la portée actuelle à la prochaine vue rendue.

Pourquoi est-ce pertinent? Devise contient vos erreurs dans resource.errors (pas@resource.errors). Devise fonctionne très bien si vous voulez l’utiliser hors de la boîte, pour ainsi dire.

Des problèmes liés à ces erreurs surviennent si vous commencez à modifier votre comportement de gestion des utilisateurs. En ajoutant un redirect_to ou render (au lieu de render_with_scope) là où Devise n'en avait pas auparavant, vous jetez les messages d'erreur. Cela rend Devise hostile à la modification, à mon avis.

Ma solution est la suivante

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

et

# 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

Ce dernier bloc de code prend les messages d'erreur de Devise sous forme de tableau et les ajoute à flash[:notice] (sous forme de tableau). Chaque message sera imprimé ligne par ligne. Si j'en ai le temps, je pense que je vais changer la façon dont Devise gère les messages d'erreur pour le faire tout au long de mon application, car il semble beaucoup plus simple de disposer d'un système de message d'erreur au lieu de deux.

23
Eric Hu

J'ai résolu ce problème de la même façon que Yoyos, en créant un app/helpers/devise_helper.rb et y placer ceci:

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

Travaillé!

11
Ryan Yu

Je veux juste apporter un nouveau petit morceau ici:

J'ai donc trouvé un moyen plus facile d'obtenir le résultat souhaité par "AnApprentice".

Tout d'abord, si vous souhaitez personnaliser quoi que ce soit dans le plug-in Devise, je vous conseille vivement de copier le code de "\ Ruby_repertory\lib\Ruby\gems\1.9.1\gems\version-version\app\controllers | helpers | mailers ... "au fichier que vous voulez dans votre projet.

[Modifier] Vous pouvez également faire hériter votre fichier des fichiers classiques "classiques" ... Comme ... par exemple ... Vous souhaitez écraser une seule fonction dans le fichier /inscriptions_controller.rb, la première ligne de votre commande personnalisée Utilisateurs contrôleur des inscriptions serait:

class Users::RegistrationsController < Devise::RegistrationsController

[Éditer le 7 août 2013] Maintenant, Devise fournit même un outil pour générer des contrôleurs: https://github.com/plataformatec/devise/wiki/Tool:-Generate-and-customize-controllers

Donc ... en tout cas ... j'ai réussi à obtenir ce que "AnApprentice" voulait juste écrire ceci (pour une solution plus propre, voir le gros montage suivant):

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

      return resource.errors
   end
end

Et, à mon avis, les lignes suivantes ont plutôt bien fonctionné:

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

Eh bien ... alors vous pouvez accéder aux erreurs pour un attribut spécifique comme ceci:

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

Et ... Un petit truc pour n'avoir qu'une seule erreur (la première à se faire prendre) par attribut:

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

Je sais que cette question a été postée il y a longtemps, mais je pense que cela aidera beaucoup d'utilisateurs de cartes SIM :).

Big Edit:

Comme j'aime étendre mon code, le rendre plus propre et le partager avec d'autres personnes, j'ai récemment voulu changer les devise_error_messages! méthode pour pouvoir l’utiliser dans mes vues et lui faire afficher le truc que j’ai expliqué plus haut.

Donc, voici ma méthode:

 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

Ce n’est pas grave, j’ai réutilisé le code que j’ai écrit à mon avis pour ne montrer qu’un seul attribut d’erreur pey, car le premier est souvent le seul facteur pertinent (comme lorsque l’utilisateur oublie un champ obligatoire).

Je compte ces erreurs "uniques" et je crée un titre HTML H2 en utilisant le pluriel et en le mettant AVANT la liste des erreurs.

Alors maintenant, je peux utiliser les "devise_error_messages!" comme celui par défaut et il rend exactement ce que je rendais déjà avant.

Si vous souhaitez accéder à un message d'erreur spécifique dans votre vue, je vous recommande maintenant d'utiliser directement "resource.errors [: attribut] .first" ou autre chose.

Seya, Kulgar.

11
Kulgar

J'utilise Devise dans Rails 3 et votre code flash est à peu près identique à ce que j'ai obtenu. Dans mon application, le code fonctionne comme prévu. Par exemple, les messages d'erreur de Devise sont générés avec le reste de mes messages flash:

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

Essayez ce code exact et voyez si cela fait une différence - l'attribut ID différent peut vous aider.

6
Scott

Je suis venu à cela et ça marche jusqu'à présent. Cela ajoute des messages au flash, de sorte qu'il peut être utilisé comme d'habitude. S'il vous plaît considérez que je suis nouveau sur Ruby et 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:

Désolé, je courais la garde et certains comportements indésirables étaient présents. Puisque after_filter est appelé après le rendu afin que cela ne fonctionne pas comme prévu. Si quelqu'un sait comment appeler une méthode après l'action mais avant le rendu ...

Mais vous pouvez utiliser quelque chose comme ça à la place:

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

Dans views/shared/_messages.html.erb

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

Si vous voulez pouvoir afficher plus d'un flash d'un type donné (: alert,: notice, etc ...) et ne pas perdre votre temps à essayer de modifier le comportement d'une gemme, c'est la solution que j'ai utilisée avec Devise. Je suis presque sûr qu'il pourrait être utilisé avec n'importe quelle gemme utilisant des messages flash.

Première chose à faire, dans votre application_controller.rb, ajoutez ceci:

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

Seconde chose à faire, afficher vos messages flash avec ceci dans application.html.erb (ou où vous voulez):

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

Troisième chose à faire, procédez comme suit lorsque vous souhaitez ajouter un message flash dans n’importe quel contrôleur:

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

Créer 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

À votre avis, remplacez

<%= devise_error_messages! %>

À:

<% devise_error_messages! %>
2
B-M

je fais simplement ceci, a travaillé pour moi: dans app/helpers /, je crée un fichier 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

dans tous les fichiers de vue je change

<%= devise_error_messages! %>

pour:

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

pour moi, il fait de mon côté éditer et nouvel utilisateur:

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

espérons que cela vous aidera;)

2
dev.guillaumem59

Certes, un peu hacky, mais j'utilise cet assistant (app/helpers/devise_helper.rb) pour saisir les flashs et les utiliser si ceux-ci sont définis par défaut sur resource.errors. Ceci est juste basé sur l'assistant qui se trouve dans la bibliothèque d'imprimante.

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

Si vous envisagez de tirer profit de la fonction imagette_error_messages, vous pouvez le faire en ajoutant à resource.errors

Si vous deviez dépasser le contrôleur d'enregistrement, cela pourrait ressembler à

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

Moyen très facile d'afficher un message d'erreur pour chaque champ

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

mettre pour chaque champ avec le nom du champ entre crochets en dessous de chaque ligne où vous voulez afficher un message d'erreur en ligne.

2
SSR

Juste pour ajouter à Eric Hu la réponse ci-dessus où toutes les déclarations If sont utilisées, faites plutôt quelque chose comme ceci à la place.

# 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

Pour afficher votre erreur de conception de votre contrôleur avec seulement la première erreur à apparaître.

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

J'aime le faire comme dans l'autre contrôleur Devise avec cette triche.

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

Pour que materialisecss affiche les messages d’erreur de la sorte sous forme de pain grillé, j’ai ajouté ce code dans 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

Je suis sûr que leur serait la façon la plus propre de l'écrire mais ça marche parfaitement

0
Gregoire Mulliez
  1. Supprimez les "devise_error_messages!" à partir du modèle "app/views/utilisateurs/mots de passe/nouveau".
  2. Créez un contrôleur personnalisé pour votre utilisateur (app/controllers/users/passwords_controller.rb) et dans un tableau après filtre ajouter des erreurs flash array:
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