rotaie - Devise - Gestione - devise_error_messages


125

nella mia pagina di modifica utente, c'è una riga come segue:

<%= devise_error_messages! %>

Il problema è che questo non genera errori nel modo standard utilizzato dal resto dell'app:

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

La mia domanda è: come faccio a far funzionare il messaggio di errore ideato come gli altri che usano flash.each?

Grazie.


1
Tieni presente che Devise sta già utilizzando il flash come sta facendo il resto dell'app. devise_error_messages non riguarda i messaggi flash (informazioni dall'ultima pagina), ma piuttosto errori di convalida da ActiveRecord Validation guide.rubyonrails.org/v2.3.11/…
Christopher Oezbek

Risposte:


135

Sto cercando di capirlo da solo. Ho appena trovato questo problema collegato a Github https://github.com/plataformatec/devise/issues/issue/504/#comment_574788

Jose sta dicendo che il devise_error_messsages!metodo è solo uno stub (sebbene contenga un'implementazione) e che dovremmo sostituirlo / sostituirlo. Sarebbe stato bello se questo fosse stato sottolineato da qualche parte nel wiki, motivo per cui immagino ci siano alcune persone come noi che hanno indovinato.

Quindi proverò a riaprire il modulo e a ridefinire il metodo, ignorando efficacemente l'implementazione predefinita. Ti farò sapere come va.

Aggiornare

Sì, funziona. L'ho creato app/helpers/devise_helper.rbe scavalcato in questo modo:

module DeviseHelper
  def devise_error_messages!
    'KABOOM!'
  end
end

Quindi, sapendo questo, posso modificare il metodo per visualizzare i messaggi di errore nel modo desiderato.

Per aiutarti a risolvere il tuo problema originale: ecco l'originale devise_helper.rbsu Github . Dai un'occhiata a come vengono attraversati i messaggi di errore:

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

Questo dovrebbe aiutarti a iniziare. :)

Un altro aggiornamento

L' resourceoggetto è in realtà il modello utilizzato da escogitare (vai a figura).

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

Sembra inoltre essere definito in un ambito più elevato (probabilmente proveniente dal controller), quindi è possibile accedervi in ​​vari luoghi.

Ovunque nel tuo aiutante

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

Il tuo punto di vista

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

Ho appena provato questo, ma non funziona. L'obiettivo è ottenere l'errore di output qui: <% flash.each do | chiave, valore | %>
AnApprentice,

@ColdTree no, l'obiettivo è che funzioni come i messaggi flash. Essere in grado di controllare il markup è una buona soluzione.
Benjamin Atkin,

... Non penso che questo risponda alla domanda, sebbene sia un buon lavoro di ricerca.
dal

37

La soluzione di seguito funziona con gli ultimi sistemi attualmente disponibili (4.1.1) e Rails 4.2.6. Ma è così semplice che non vedo il motivo per cui non dovrebbe funzionare tra 10 anni;)

Se vuoi riciclare i tuoi messaggi di errore e farli apparire uguali in tutta la tua app, consiglierei qualcosa del genere (come ho imparato con Michael Hartl tut):

Crea parziale per i messaggi di errore: layouts/_error_messages.html.erb inserisci il seguente codice (qui uso alcune classi 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 %>

Ora hai qualcosa di riciclabile e puoi usarlo su tutta la linea. Invece di ideare standard:

<%= devise_error_messages! %>

Chiamalo nel tuo modulo in questo modo:

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

Puoi metterlo in qualsiasi forma. Invece di passare risorse elaborate puoi passare variabili dal tuo modulo in questo modo:

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

1
Probabilmente la risposta migliore e più intuitiva di sempre.
Victor,

2
Soluzione interessante. pluralize (object.errors.count, 'errors' dovrebbe essere modificato in pluralize (object.errors.count, 'error' comunque
mizurnix

1
@LukaszMuzyka in questa soluzione .. devo rimuovere: validabile da user.rb .. ???
Vishal,

1
@Vishal - no. La soluzione sopra utilizza semplicemente HTML diverso per visualizzare i messaggi che non cambia alcun meccanismo di Devise
Lukasz Muzyka,

1
@Vishal quando stai usando escogita sta già facendo le validazioni che hai menzionato senza alcun codice aggiuntivo. La soluzione sopra è solo quella di ignorare il comportamento predefinito. Devi escogitare di lavorare prima di tutto. Sei sicuro di aver seguito le istruzioni per integrare l'ideazione con il tuo progetto?
Lukasz Muzyka,

22

So che è passato un po 'di tempo da quando questa domanda è stata pubblicata, ma volevo solo commentare ciò che ho trovato. Le due persone che hanno già risposto mi sono state di grande aiuto e volevo solo contribuire.

Vedrai in Devise che ci sono chiamate che usano render_with_scope. Credo che questo sia un metodo definito da idea e fondamentalmente applica l'ambito corrente alla vista successiva resa.

Perché è rilevante? Devise contiene i tuoi errori all'interno resource.errors( non @resource.errors ). Devise funziona bene se vuoi usarlo fuori dalla scatola, per così dire.

Problemi con questi errori sorgono se si inizia a modificare il comportamento di gestione degli utenti. Aggiungendo un redirect_too render(anziché render_with_scope) dove Devise in precedenza non ne aveva uno, fondamentalmente stai lanciando i messaggi di errore. Questo rende Devise ostile alla modifica, secondo me.

La mia soluzione è questa

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

e

# 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

Quest'ultimo blocco di codice accetta i messaggi di errore di Devise come array e li aggiunge flash[:notice](come array). Ogni messaggio verrà stampato una riga alla volta. Se ho tempo, penso che cambierò il modo in cui Devise gestisce i messaggi di errore per farlo in tutta la mia app, poiché sembra molto più pulito avere un sistema di messaggi di errore anziché due.


3
Grazie mille per quello, stavo sbattendo la testa contro il muro per aver provato a farlo.
Lucas,

1
Sono passati 5 anni e questa risposta mi ha salvato la pancetta. Grazie mille @ eric-hu.
marcamillion,

12

Ho risolto questo in modo simile a YoyoS, creando un app/helpers/devise_helper.rbe inserendo questo:

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

Lavorato!


11

Voglio solo portare un nuovo piccolo pezzo qui:

Così ho trovato un modo più semplice per ottenere il risultato desiderato da "AnApprentice".

Prima di tutto, se vuoi personalizzare qualcosa all'interno del plug-in Devise, ti consiglio vivamente di copiare oltre il codice da "\ Ruby_repertory \ lib \ ruby ​​\ gems \ 1.9.1 \ gems \ devise-version \ app \ controller | helpers | mailers ... "nel file desiderato nel progetto.

[Modifica] O puoi fare in modo che il tuo file erediti dai file "normali" escogita ... Come ... diciamo ... Vuoi sovrascrivere solo una funzione in devise / registrations_controller.rb, la prima riga dei tuoi utenti personalizzati il responsabile delle registrazioni sarebbe:

class Users::RegistrationsController < Devise::RegistrationsController

[Modifica 7 agosto 2013] Ora Devise fornisce anche uno strumento per generare controller: https://github.com/plataformatec/devise/wiki/Tool:-Generate-and-customize-controllers

Quindi ... comunque ... Sono riuscito a ottenere quello che "AnApprentice" voleva solo scrivere questo (per una soluzione più pulita, vedi la seguente modifica grande):

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

      return resource.errors
   end
end

E, a mio avviso, le righe successive hanno funzionato abbastanza bene:

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

Bene ... allora puoi accedere agli errori per un attributo specifico come questo:

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

E ... Un piccolo trucco per avere un solo errore (il primo a essere scoperto) che compare per attributo:

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

So che è passato un po 'di tempo da quando questa domanda è stata pubblicata, ma penso che aiuterà molti utenti a concepire :).

Modifica grande:

Dato che adoro estendere il mio codice, renderlo più pulito e condividerlo con gli altri, recentemente ho voluto cambiare devise_error_messages! metodo per usarlo nelle mie viste e fargli visualizzare il trucco che ho spiegato sopra.

Quindi, ecco il mio metodo:

 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

Non è un grosso problema qui, ho riutilizzato il codice che ho scritto a mio avviso per mostrare solo un attributo pey di errore, perché spesso il primo è l'unico rilevante (come quando l'utente dimentica un campo richiesto).

Sto contando quegli errori "unici" e sto facendo un titolo HTML H2 usando pluralize e mettendolo PRIMA dell'elenco degli errori.

Quindi ora posso usare "devise_error_messages!" come quello predefinito e rende esattamente quello che stavo già eseguendo il rendering prima.

Se si desidera accedere a un messaggio di errore specifico nella vista, ora consiglio di utilizzare direttamente "resource.errors [: attributo] .first" o altro.

Seya, Kulgar.


6

Sto usando Devise in Rails 3 e il tuo codice flash è praticamente identico a quello che ho. Nella mia app, il codice funziona come previsto; cioè i messaggi di errore Devise vengono emessi con il resto dei miei messaggi flash:

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

Prova questo codice esatto e vedi se fa differenza: il diverso attributo ID può essere d'aiuto.


grazie ma finisce per non mostrare nulla. "<% = devise_error_messages!%>" genera un errore. quanto sopra non ha fatto nulla? idee?
AnApprentice,

Mi scuso - ho appena visto il tuo commento. Ad essere sincero, sto finendo le idee. Presumo che tu abbia visualizzato l'origine nel tuo browser e verificato l'HTML generato? Nel caso in cui qualcosa venga nascosto dai CSS. Stai utilizzando l'ultima versione di Devise 1.1.3?
Scott,

5

Sono arrivato a questo e sta funzionando finora. Ciò aggiunge messaggi elaborati al flash, quindi può essere utilizzato come al solito. Per favore, considera che sono nuovo di Ruby e 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

Modificare:

Mi dispiace stavo correndo di guardia ed era presente un comportamento indesiderato. Poiché after_filterviene chiamato dopo il rendering, quindi non funziona come previsto. Se qualcuno sa come chiamare un metodo dopo l'azione ma prima del rendering ...

Ma puoi usare qualcosa del genere invece:

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

Nel views/shared/_messages.html.erb

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

1
+1 Ottima risposta. Penso che questa sia sicuramente la soluzione più pulita e si adatta perfettamente alla mia architettura attuale. La risposta non è così chiara: praticamente tutto ciò che precede la modifica dovrebbe essere ignorato (e rimosso o barrato con imo).
zelanix,

3

Se vuoi essere in grado di visualizzare più di un flash di un determinato tipo (: avviso,: avviso, ecc ...) e non perdere tempo a cercare di modificare il comportamento di una gemma, questa è la soluzione che ho usato con Devise. Sono abbastanza sicuro che potrebbe essere utilizzato con qualsiasi gemma che utilizza messaggi flash.

La prima cosa da fare, nel tuo application_controller.rb, aggiungi questo:

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

Seconda cosa da fare, visualizzando i tuoi messaggi flash con questo in application.html.erb (o dove vuoi):

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

Terza cosa da fare, ogni volta che si desidera aggiungere un messaggio flash in qualsiasi controller, procedere come segue:

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

Ma come ottenere i messaggi Devise per chiamare flash_messages invece di mantenere un oggetto errore.
Christopher Oezbek,

3

Crea 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

A tuo avviso, sostituisci

<%= devise_error_messages! %>

Per:

<% devise_error_messages! %>

1
In realtà, dovresti usare: flash.now [: alert]
BM

2

Certo, un po 'confuso, ma sto usando questo helper (app / helpers / devise_helper.rb) per catturare i flash e usarli se impostato su default resource.errors. Questo si basa solo sull'helper che è nella libreria ideata.

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

Se stai cercando di abbandonare le spalle a devise_error_messages, puoi farlo aggiungendo a resource.errors

Se dovessi cavalcare il controller di registrazione, potrebbe sembrare

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

Modo molto semplice per visualizzare un messaggio di errore per ciascun campo

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

metti per ogni campo con il nome del campo tra parentesi quadre sotto ogni riga dove vuoi visualizzare un messaggio di errore in linea.


1

Per mostrare il tuo errore di progettazione dal tuo controller con solo il primo errore da mostrare.

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

1

Solo per aggiungere alla risposta Eric Hu sopra dove vengono usate tutte le istruzioni If, piuttosto fare qualcosa del genere invece.

# 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

lo faccio semplicemente, ha funzionato per me: in app / helpers / , creo un 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

in tutti i file di visualizzazione che cambio

<%= devise_error_messages! %>

per:

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

per me, a mio avviso, modifica e nuovo utente:

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

spero che ti possa aiutare;)


Davvero non capisco come questo fa qualcosa? questo è un comportamento standard? È solo un altro modo di fare <%= devise_error_messages! %>e non risponde alla domanda. La domanda chiede come applicare il flash a ciascun messaggio.
Segna il

0
  1. Rimuovi "devise_error_messages!" dal modello "app / visualizzazioni / utenti / password / nuovo".
  2. Crea controller personalizzato per il tuo utente (app / controller / users / passwords_controller.rb) e in un filtro after aggiungi errori 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

Mi piace farlo proprio come è stato fatto nell'altro controller Devise con questo trucco.

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

Per materialisecss per visualizzare i messaggi di errore ideati come brindisi Ho aggiunto questo codice in 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

Sono sicuro che il loro sarebbe il modo più pulito per scriverlo, ma si sveglia perfettamente


0

DeviseHelper#devise_error_messages! è obsoleto e verrà rimosso nella prossima versione principale.

Devise ora utilizza un under parziale devise/shared/error_messagesper visualizzare i messaggi di errore per impostazione predefinita e renderli più facili da personalizzare. Aggiorna le tue visualizzazioni cambiando le chiamate da:

      <%= devise_error_messages! %>

per:

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

-1

Ho appena creato un app/helpers/devise_helper.rbJohn simile ma ho annullato il metodo in questo modo:

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

Con questo non devo modificare nient'altro. È una cattiva idea? Sono nuovo alle rotaie, non esitate a correggermi. Grazie.


Questo non funzionerà come desiderato, il messaggio flash ora contiene tag html <br>. Normalmente inserisci solo una stringa nel tuo messaggio flash.
AZ.

Forse, ma la nuova linea funziona ancora. Proponi un'altra soluzione se non ti piace questa.
YoyoS,

-2

Ho appena dichiarato devise_error_messages! come aiutante vuoto. E recuperato e gestito manualmente gli errori in un _errors parziale generale per la mia applicazione. Sembrava la soluzione più semplice e non devo esaminare tutti i file di escavazione e rimuovere la chiamata al gestore degli errori.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.