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.
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.
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. :)
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>
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">×</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 %>
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.
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é!
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.
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.
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 -->
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.")
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! %>
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;)
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
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
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.
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 %>
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
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 %>
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
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