Messaggio di errore di convalida completamente personalizzato con Rails


260

Utilizzando Rails sto cercando di ottenere un messaggio di errore del tipo "Il campo della canzone non può essere vuoto" al momento del salvataggio. Procedere come segue:

validates_presence_of :song_rep_xyz, :message => "can't be empty"

... visualizza solo "Song Rep XYW non può essere vuoto", il che non va bene perché il titolo del campo non è facile da usare. Come posso cambiare il titolo del campo stesso? Potrei cambiare il nome effettivo del campo nel database, ma ho più campi "song" e ho bisogno di avere nomi di campo specifici.

Non voglio hackerare il processo di convalida delle rotaie e sento che dovrebbe esserci un modo per risolverlo.

Risposte:


432

Ora, il modo accettato per impostare i nomi umanizzati e i messaggi di errore personalizzati è usare le localizzazioni .

# config/locales/en.yml
en:
  activerecord:
    attributes:
      user:
        email: "E-mail address"
    errors:
      models:
        user:
          attributes:
            email:
              blank: "is required"

Ora il nome umanizzato e il messaggio di convalida presenza per l'attributo "email" sono stati modificati.

I messaggi di convalida possono essere impostati per uno specifico modello + attributo, modello, attributo o globalmente.


19
Se si utilizza mongoid, sostituire activerecord: con mongoid:
Intenti

88
@graywh: dove dovrebbero essere postate le domande su una risposta, se non nei commenti? Ecco la guida I18n : guide.rubyonrails.org/i18n.html
Tyler Rick,

4
A proposito: se passi un simbolo per il parametro del messaggio del tuo validatore in Rails 3.1.3, ti dirà l'ambito che stava cercando in quanto non verrà trovato, quindi sai esattamente cosa mettere nel tuo locales yml.
aceofspades,

4
Bene, questo va bene e tutto, ma cosa succede se anteporre ingenuamente il nome della colonna (non importa quanto sia leggibile dall'uomo) porterebbe a una grammatica completamente migliorata (specialmente nelle lingue non inglesi)? Devo davvero usare errors.add :base, msg? Vorrei sapere in quale colonna si trova l'errore, quindi posso visualizzarlo nel campo modulo corretto.
Panzi,

6
@graywh Forse mi manca qualcosa, ma non antepone sempre il nome della colonna prima del messaggio? Anche in inglese mi piacerebbe avere eg The password is wrong.o The email address is not valid.invece di Password is wrong.e Email is not valid..
Panzi,

65

Nel tuo modello:

validates_presence_of :address1, message: 'Put some address please' 

Dal tuo punto di vista

<% m.errors.each do |attr, msg|  %>
 <%= msg %>
<% end %>

Se invece lo fai

<%= attr %> <%= msg %>

viene visualizzato questo messaggio di errore con il nome dell'attributo

address1 Put some address please

se si desidera ottenere il messaggio di errore per un singolo attributo

<%= @model.errors[:address1] %>

Questa non è una soluzione accettabile. Cosa succede se desidero il comportamento predefinito per tutti gli altri attributi (attr + msg)?
Rômulo Ceccon,

Ecco qua ... puoi giocare con quelle 2 cose e farlo funzionare
Federico

Devi usare un simbolo in modo che possa cercare nei tuoi file yml, comevalidates_presence_of :address1, :message => :put_some_address_please
Federico

Questo non è accettabile, poiché viene incluso il nome del campo
fatuhoku,

62

Prova questo.

class User < ActiveRecord::Base
  validate do |user|
    user.errors.add_to_base("Country can't be blank") if user.country_iso.blank?
  end
end

L'ho trovato qui .

Ecco un altro modo per farlo. Quello che fai è definire un metodo human_attribute_name sulla classe del modello. Al metodo viene passato il nome della colonna come stringa e restituisce la stringa da utilizzare nei messaggi di convalida.

class User < ActiveRecord::Base

  HUMANIZED_ATTRIBUTES = {
    :email => "E-mail address"
  }

  def self.human_attribute_name(attr)
    HUMANIZED_ATTRIBUTES[attr.to_sym] || super
  end

end

Il codice sopra è da qui


Il problema è che il mio campo si chiama: song_rep_xyz (beh, qualcosa di complicato), che non è facile da usare
marcgg

16
per Rails 3, "def self.human_attribute_name (attr)" deve essere modificato in "def self.human_attribute_name (attr, options = {})", altrimenti restituisce un errore
spacemonkey

3
Grazie per questo. Avevo bisogno di qualcosa che funzionasse per Rails 2. (Sì, povero me ... :)
Dan Barron,

18

Sì, c'è un modo per farlo senza il plugin! Ma non è così pulito ed elegante come usare il plugin menzionato. Ecco qui.

Supponendo che sia Rails 3 (non so se è diverso nelle versioni precedenti),

tienilo nel tuo modello:

validates_presence_of :song_rep_xyz, :message => "can't be empty"

e nella vista, invece di partire

@instance.errors.full_messages

come sarebbe quando usiamo il generatore di impalcature, metti:

@instance.errors.first[1]

E otterrai solo il messaggio specificato nel modello, senza il nome dell'attributo.

Spiegazione:

#returns an hash of messages, one element foreach field error, in this particular case would be just one element in the hash:
@instance.errors  # => {:song_rep_xyz=>"can't be empty"}

#this returns the first element of the hash as an array like [:key,"value"]
@instance.errors.first # => [:song_rep_xyz, "can't be empty"]

#by doing the following, you are telling ruby to take just the second element of that array, which is the message.
@instance.errors.first[1]

Finora stiamo visualizzando solo un messaggio, sempre per il primo errore. Se si desidera visualizzare tutti gli errori, è possibile eseguire il ciclo nell'hash e mostrare i valori.

Spero che abbia aiutato.


16

Codice Rails3 con messaggi completamente localizzati:

Nel modello user.rb definire la convalida

validates :email, :presence => true

In config / locales / en.yml

en:  
  activerecord:
    models: 
      user: "Customer"
    attributes:
      user:
        email: "Email address"
    errors:
      models:
        user:
          attributes:
            email:
              blank: "cannot be empty"

15

Nel metodo di convalida personalizzato utilizzare:

errors.add(:base, "Custom error message")

poiché add_to_base è stato deprecato.

errors.add_to_base("Custom error message")


13

Relativo alla risposta accettata e un'altra risposta in fondo all'elenco :

Sto confermando che il fork di nanamkim di custom-err-msg funziona con Rails 5 e con le impostazioni locali.

Devi solo avviare il messaggio della locale con un cursore e non dovrebbe visualizzare il nome dell'attributo nel messaggio.

Un modello definito come:

class Item < ApplicationRecord
  validates :name, presence: true
end

con il seguente en.yml:

en:
  activerecord:
    errors:
      models:
        item:
          attributes:
            name:
              blank: "^You can't create an item without a name."

item.errors.full_messages visualizzerà:

You can't create an item without a name

invece del solito Name You can't create an item without a name


11

Consiglio di installare la gemma custom_error_message (o come plugin ) originariamente scritta da David Easley

Ti permette di fare cose come:

validates_presence_of :non_friendly_field_name, :message => "^Friendly field name is blank"

Ho usato questo plugin in passato con grande successo, anche se non sembra più essere regolarmente gestito.
Jared Brown,

1
puoi anche installarlo come una gemma per le rotaie 3. basta aggiungere gem "custom_error_message" al tuo Gemfile - vedi github per maggiori dettagli
Dorian

Esattamente quello di cui avevo bisogno
olleicua il

3
@DickieBoy Confermo che il fork di nanamkim ( github.com/nanamkim/custom-err-msg ) funziona con Rails 5. Funziona davvero bene con la risposta accettata. Scriverò questo come una risposta separata.
Rystraum,

@Rystraum Per la vita di me non ricordo il caso d'uso attorno a questo, ma grazie per la risposta! Sarò sicuro di ricordarlo per il futuro.
DickieBoy,

10

Una soluzione potrebbe essere quella di modificare il formato di errore predefinito i18n:

en:
  errors:
    format: "%{message}"

L'impostazione predefinita è format: %{attribute} %{message}


7

Ecco un altro modo:

Se usi questo modello:

<% if @thing.errors.any? %>
  <ul>
    <% @thing.errors.full_messages.each do |message| %>
      <li><%= message %></li>
    <% end %>
  </ul>
<% end %>

Puoi scrivere il tuo messaggio personalizzato in questo modo:

class Thing < ActiveRecord::Base

  validate :custom_validation_method_with_message

  def custom_validation_method_with_message
    if some_model_attribute.blank?
      errors.add(:_, "My custom message")
    end
  end

In questo modo, a causa del carattere di sottolineatura, il messaggio completo diventa "Il mio messaggio personalizzato", ma lo spazio aggiuntivo all'inizio è impercettibile. Se non vuoi davvero quello spazio aggiuntivo all'inizio, aggiungi semplicemente il .lstripmetodo.

<% if @thing.errors.any? %>
  <ul>
    <% @thing.errors.full_messages.each do |message| %>
      <li><%= message.lstrip %></li>
    <% end %>
  </ul>
<% end %>

Il metodo String.lstrip eliminerà lo spazio aggiuntivo creato da ': _' e lascerà invariati tutti gli altri messaggi di errore.

O ancora meglio, usa la prima parola del tuo messaggio personalizzato come chiave:

  def custom_validation_method_with_message
    if some_model_attribute.blank?
      errors.add(:my, "custom message")
    end
  end

Ora il messaggio completo sarà "Il mio messaggio personalizzato" senza spazio aggiuntivo.

Se vuoi che il messaggio completo inizi con una parola maiuscola come "L'URL non può essere vuoto" non può essere fatto. Prova invece ad aggiungere qualche altra parola come chiave:

  def custom_validation_method_with_message
    if some_model_attribute.blank?
      errors.add(:the, "URL can't be blank")
    end
  end

Ora il messaggio completo sarà "L'URL non può essere vuoto"


ooo, puoi anche fare errors.add(:_, 'foobar')e ottenere "foobar" come messaggio
xxjjnn,

6

Fallo nel modo normale:

validates_presence_of :email, :message => "Email is required."

Ma visualizzalo in questo modo invece

<% if @user.errors.any? %>
  <% @user.errors.messages.each do |message| %>
    <div class="message"><%= message.last.last.html_safe %></div>
  <% end %>
<% end %>

ritorna

"Email is required."

Il metodo di localizzazione è sicuramente il modo "corretto" per farlo, ma se stai facendo un piccolo progetto non globale e vuoi solo andare veloce - questo è sicuramente più semplice del file hopping.

Mi piace per la possibilità di inserire il nome del campo in un punto diverso dall'inizio della stringa:

validates_uniqueness_of :email, :message => "There is already an account with that email."

2

Se vuoi elencarli tutti in una bella lista ma senza usare il nome rozzo non umano, puoi farlo ...

object.errors.each do |attr,message|
  puts "<li>"+message+"</li>"
end

1

Dal tuo punto di vista

object.errors.each do |attr,msg|
  if msg.is_a? String
    if attr == :base
      content_tag :li, msg
    elsif msg[0] == "^"
      content_tag :li, msg[1..-1]
    else
      content_tag :li, "#{object.class.human_attribute_name(attr)} #{msg}"
    end
  end
end

Quando si desidera sovrascrivere il messaggio di errore senza il nome dell'attributo, anteporre semplicemente il messaggio con ^ in questo modo:

validates :last_name,
  uniqueness: {
    scope: [:first_name, :course_id, :user_id],
    case_sensitive: false,
    message: "^This student has already been registered."
  }

non funziona con le rotaie 5.1 / ruby ​​2.4? ottenere il nome del modello in tale ambito
Ben

@Ben Funziona per me su Rails 5.1.2, Ruby 2.4.1p111. Puoi condividere il tuo codice?
luckyruby,

immagino di dover guardare oltre, puoi controllare il codice e la sua risposta lì stackoverflow.com/q/45128434/102133
Ben

0

Ho provato a seguire, ha funzionato per me :)

1 job.rb

class Job < ApplicationRecord
    validates :description, presence: true
    validates :title, 
              :presence => true, 
              :length => { :minimum => 5, :message => "Must be at least 5 characters"}
end

2 jobs_controller.rb

def create
      @job = Job.create(job_params)
      if @job.valid?
        redirect_to jobs_path
      else
        render new_job_path
      end     
    end

3 _form.html.erb

<%= form_for @job do |f| %>
  <% if @job.errors.any? %>
    <h2>Errors</h2>
    <ul>
      <% @job.errors.full_messages.each do |message|%>
        <li><%= message %></li>
      <% end %>  
    </ul>
  <% end %>
  <div>
    <%= f.label :title %>
    <%= f.text_field :title %>
  </div>
  <div>
    <%= f.label :description %>
    <%= f.text_area :description, size: '60x6' %>

  </div>
  <div>
    <%= f.submit %>
  </div>
<% end %> 

0

Ecco il mio codice che può esserti utile nel caso ne avessi ancora bisogno: Il mio modello:

validates :director, acceptance: {message: "^Please confirm that you are a director of the company."}, on: :create, if: :is_director?

Quindi ho creato un aiuto per mostrare i messaggi:

module ErrorHelper
  def error_messages!
    return "" unless error_messages?
    messages = resource.errors.full_messages.map { |msg|
       if msg.present? && !msg.index("^").nil?
         content_tag(:p, msg.slice((msg.index("^")+1)..-1))
       else
         content_tag(:p, msg)
       end
    }.join

    html = <<-HTML
      <div class="general-error alert show">
        #{messages}
      </div>
    HTML

    html.html_safe
  end

  def error_messages?
    !resource.errors.empty?
  end
end

0

Un approccio unico di cui non ho mai visto parlare nessuno!

L'unico modo in cui sono stato in grado di ottenere tutta la personalizzazione che volevo era utilizzare un after_validationcallback per consentirmi di manipolare il messaggio di errore.

  1. Consenti che il messaggio di convalida venga creato normalmente, non è necessario provare a modificarlo nell'helper di convalida.

  2. creare un after_validationcallback che sostituirà quel messaggio di validazione nel back-end prima che arrivi alla vista.

  3. Nel after_validationmetodo puoi fare tutto quello che vuoi con il messaggio di validazione, proprio come una normale stringa! È anche possibile utilizzare valori dinamici e inserirli nel messaggio di convalida.


#this could be any validation
validates_presence_of :song_rep_xyz, :message => "whatever you want - who cares - we will replace you later"

after_validation :replace_validation_message

def replace_validation_message
    custom_value = #any value you would like
    errors.messages[:name_of_the_attribute] = ["^This is the replacement message where 
    you can now add your own dynamic values!!! #{custom_value}"]
end

Il metodo after_validation avrà un ambito di gran lunga maggiore rispetto all'helper di convalida delle rotaie incorporato, quindi sarai in grado di accedere all'oggetto che stai convalidando come stai provando a fare con object.file_name. Che non funziona nell'helper di convalida in cui si sta tentando di chiamarlo.

Nota: usiamo il ^per sbarazzarci del nome dell'attributo all'inizio della validazione come sottolineato da @Rystraum facendo riferimento a questa gemma


0

La risposta di graywh è la migliore se in realtà è diversa per la visualizzazione del nome del campo. Nel caso di un nome di campo dinamico (basato su altri campi da visualizzare), farei qualcosa del genere

<% object.errors.each do |attr, msg| %>
<li>
  <% case attr.to_sym %>
  <% when :song_rep_xyz %>
    <%= #display error how you want here %>
  <% else %>
    <%= object.errors.full_message(attr, msg) %>
  <% end %>
</li>
<% end %>

il metodo full_message sull'altro è quello che le rotaie usano all'interno del metodo full_messages, quindi darà gli errori Rails normali per altri casi (Rails 3.2 e versioni successive)

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.