2011-10-24 14 views
32

Estoy tratando de entender cómo puedo lograr esto. ¿Alguien puede aconsejarme o señalarme en la dirección correcta?Cómo mostrar Ruby on Rails mensajes de error de validación uno a la vez

Esto permite mostrar 1 error de cada campo a la vez. Es casi lo que quiero hacer, pero no exactamente. Quiero mostrar 1 mensaje de error completo a la vez. P.EJ. el primer nombre no puede estar en blanco. Una vez que se ha resuelto, pasa al siguiente error. Por lo tanto, si el usuario agregara números a su apellido, ya no estaría en blanco, pero mostraría otro error indicando que solo se permitieron las letras, etc. Cuando se corrigió ese error, iría al último error o tal vez al correo electrónico si el campo del usuario su apellido correctamente.

<% @user.errors.each do |attr, msg| %> 
<%= "#{attr} #{msg}" if @user.errors[attr].first == msg %> 
<% end %> 

Respuesta

26

Después de experimentar durante unas horas lo descubrí.

<% if @user.errors.full_messages.any? %> 
    <% @user.errors.full_messages.each do |error_message| %> 
    <%= error_message if @user.errors.full_messages.first == error_message %> <br /> 
    <% end %> 
<% end %> 

Aún mejor:

<%= @user.errors.full_messages.first if @user.errors.any? %> 
+1

Mejor aún: '<% = f.object.errors.full_messages.join (", ") if f.object.errors.any? %> '- esto es agnóstico de longitud de objeto y error. 'f' es la variable de forma. –

48

ActiveRecord errores de validación tiendas en una matriz llamada errors. Si usted tiene un modelo User continuación, usted podrá acceder a los errores de validación en un caso dado, así:

@user = User.create[params[:user]] # create will automatically call validators 

if @user.errors.any? # If there are errors, do something 

    # You can iterate through all messages by attribute type and validation message 
    # This will be something like: 
    # attribute = 'name' 
    # message = 'cannot be left blank' 
    @user.errors.each do |attribute, message| 
    # do stuff for each error 
    end 

    # Or if you prefer, you can get the full message in single string, like so: 
    # message = 'Name cannot be left blank' 
    @users.errors.full_messages.each do |message| 
    # do stuff for each error 
    end 

    # To get all errors associated with a single attribute, do the following: 
    if @user.errors.include?(:name) 
    name_errors = @user.errors.on(:name) 

    if name_errors.kind_of?(Array) 
     name_errors.each do |error| 
     # do stuff for each error on the name attribute 
     end 
    else 
     error = name_errors 
     # do stuff for the one error on the name attribute. 
    end 
    end 
end 

Por supuesto, también se puede hacer nada de esto en los puntos de vista en lugar del controlador, en caso de que desee mostrar sólo el primer error para el usuario o algo así.

+2

Estoy utilizando Rails 4.2.0 y su código no funcionó para mí. En cambio, tuve que cambiar de '@ user.errors.invalid? (: Name)' a '@ user.errors.include? (: Name)' – anka

+0

Esta es la respuesta más detallada que he visto en el desbordamiento de la pila hasta el momento.Excelente. –

+0

No puedo encontrar ningún documento en su método usado '.on()'. No aparece en el documento "Hash" de la clase ruby? – Douglas

5

Una mejor idea,

si usted quiere poner el mensaje de error justo debajo del campo de texto, se puede hacer como esto

.row.spacer20top 
    .col-sm-6.form-group 
    = f.label :first_name, "*Your First Name:" 
    = f.text_field :first_name, :required => true, class: "form-control" 
    = f.error_message_for(:first_name) 

¿Qué es 0??
-> Pues bien, este es un hermoso truco para hacer algunas cosas interesantes

# Author Shiva Bhusal 
# Aug 2016 
# in config/initializers/modify_rails_form_builder.rb 
# This will add a new method in the `f` object available in Rails forms 
class ActionView::Helpers::FormBuilder 
    def error_message_for(field_name) 
    if self.object.errors[field_name].present? 
     model_name    = self.object.class.name.downcase 
     id_of_element   = "error_#{model_name}_#{field_name}" 
     target_elem_id   = "#{model_name}_#{field_name}" 
     class_name    = 'signup-error alert alert-danger' 
     error_declaration_class = 'has-signup-error' 

     "<div id=\"#{id_of_element}\" for=\"#{target_elem_id}\" class=\"#{class_name}\">"\ 
     "#{self.object.errors[field_name].join(', ')}"\ 
     "</div>"\ 
     "<!-- Later JavaScript to add class to the parent element -->"\ 
     "<script>"\ 
      "document.onreadystatechange = function(){"\ 
      "$('##{id_of_element}').parent()"\ 
      ".addClass('#{error_declaration_class}');"\ 
      "}"\ 
     "</script>".html_safe 
    end 
    rescue 
    nil 
    end 
end 

Resultado enter image description here

marcado generado después de un error

<div id="error_user_email" for="user_email" class="signup-error alert alert-danger">has already been taken</div> 
<script>document.onreadystatechange = function(){$('#error_user_email').parent().addClass('has-signup-error');}</script> 

correspondiente SCSS

.has-signup-error{ 
    .signup-error{ 
     background: transparent; 
     color: $brand-danger; 
     border: none; 
    } 

    input, select{ 
     background-color: $bg-danger; 
     border-color: $brand-danger; 
     color: $gray-base; 
     font-weight: 500; 
    } 

    &.checkbox{ 
     label{ 
     &:before{ 
      background-color: $bg-danger; 
      border-color: $brand-danger; 
     } 
     } 
    } 

Nota: Bootstrap variables utilizadas aquí

1

resolví así:

<% @user.errors.each do |attr, msg| %> 
    <li> 
    <%= @user.errors.full_messages_for(attr).first if @user.errors[attr].first == msg %> 
    </li> 
<% end %> 

De esta manera usted está utilizando los locales para los mensajes de error.

Cuestiones relacionadas