Carriles 3: el contenedor "campo con errores" cambia la apariencia de la página. ¿Cómo evitar esto?

131

Campo de correo electrónico:

<label for="job_client_email">Email: </label> 
<input type="email" name="job[client_email]" id="job_client_email">

Se ve como esto:

sin error

Pero, si la validación del correo electrónico falla, se convierte en:

<div class="field_with_errors">
  <label for="job_client_email">Email: </label>
</div> 
<div class="field_with_errors">
  <input type="email" value="wrong email" name="job[client_email]" id="job_client_email">
</div>

que se ve así:

con_error

¿Cómo podría evitar este cambio de apariencia?

Misha Moroshko
fuente
Hola @ misha-moroshko, intento agregar la clase de error en el nivel primario como se describe aquí . Traté de buceo en los rieles del código utilizando byebug pero se perdió de inmediato .. quería configurar este comportamiento en un poco de manera inteligente comprobando si los campo tiene un padre ..
SanjiBukai

Respuestas:

235

Deberías anularlo ActionView::Base.field_error_proc. Actualmente se define como esto dentro de ActionView::Base:

 @@field_error_proc = Proc.new{ |html_tag, instance| 
   "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe
 }

Puede anularlo colocando esto en la clase de su aplicación dentro de config/application.rb:

config.action_view.field_error_proc = Proc.new { |html_tag, instance| 
  html_tag
}

Reinicie el servidor de rails para que este cambio surta efecto.

Ryan Bigg
fuente
44
Una pequeña pregunta: ¿Por qué tanto el labelcomo el inputestán envueltos? ¿Cómo decide Rails qué envolver?
Misha Moroshko
44
Probablemente esto se haga para que también pueda aplicar estilo a la etiqueta de un campo con errores. Además, rails sabe qué envolver porque usted le dice qué campos pertenecen a qué atributo del recurso para el que está haciendo el formulario: f.label :passwordy f.password_field :passworden el @resource.errorshabría un [:password]conjunto de errores.
Mosselman
3
Si estás trabajando con Twitter Bootstrap, o si quieres otro ejemplo de lo que puedes hacer en field_error_proc, mira esta increíble esencia: gist.github.com/1464315
Ryan Sandridge
2
¿Por qué uno haría "# {html_tag}". Html_safe, y no html_tag.html_safe?
Anurag
3
Anurag: si html_tag resulta ser nulo, o cualquier otra cosa que no sea una cadena, entonces un html_tag.html_safe simple generaría un error. Ponerlo en "# {html_tag}" implícitamente llama a html_tag.to_s, que con suerte devolverá una cadena, que luego podrá responder a html_safe
sockmonk
100

La diferencia visual que está viendo está sucediendo porque el divelemento es un elemento de bloque. Agregue este estilo a su archivo CSS para que se comporte como un elemento en línea:

.field_with_errors { display: inline; }
dontangg
fuente
2
Este es un truco en el mejor de los casos porque niega cualquier display:propiedad utilizada (y otros estilos de diseño) en el html_tag.
Ryan
1
No lo veo como un truco. La displaypropiedad que se usa antes de agregar este CSS es la blockque está causando la diferencia visual que no se desea. No niega ningún otro estilo de diseño en la etiqueta. Sin embargo, la respuesta de Ryan Bigg es perfecta si desea cambiar / eliminar la etiqueta que envuelve el campo con errores.
dontangg
Intenté esto, sin embargo, si sus campos están dentro de las etiquetas <p>, no parece funcionar (al menos no en Firefox) ya que un <div> dentro de un <p> rompe líneas sin importar qué. Usando la solución Biggs, solo reemplazar <div con <span parece ser el truco.
jpw
72

Actualmente uso esta solución, colocada en un inicializador:

ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
  class_attr_index = html_tag.index 'class="'

  if class_attr_index
    html_tag.insert class_attr_index+7, 'error '
  else
    html_tag.insert html_tag.index('>'), ' class="error"'
  end
end

Esto me permite simplemente agregar un nombre de clase a la etiqueta apropiada, sin crear elementos adicionales.

Phobetron
fuente
2
Esto es increíble para usar los campos de error de una manera discreta.
Ryan
1
Funciona en Rails 4.0.3.
Yuki Matsukura
1
Trabajó para mi. Tuve que reiniciar el servidor de rails para notar cambios :) :)
Jezen Thomas
1
Me encantó esta solución, pero no funcionará si tienes otra etiqueta dentro de label.
Caio Tarifa
Hola @Phobetron, de hecho, esta es una buena solución. Busco una forma de agregar esa clase en el nivel primario como se describe aquí . Me sumergí en el código de los rieles pero me perdí de inmediato al no poder seguir el proceso de renderizado con byebug. ¿Crees que esto es realmente posible?
SanjiBukai
20

El código adicional está siendo agregado por ActionView::Base.field_error_proc. Si no está utilizando field_with_errorspara diseñar su formulario, puede anularlo en application.rb:

config.action_view.field_error_proc = Proc.new { |html_tag, instance| html_tag.html_safe }

Alternativamente, puede cambiarlo a algo que se adapte a su IU:

config.action_view.field_error_proc = Proc.new { |html_tag, instance| "<span class='field_with_errors'>#{html_tag}</span>".html_safe }
Dan Cheail
fuente
Esto está funcionando bien para mí; parece ser la solución más elegante para usar con Twitter Bootstrap
Avishai
5

Estoy trabajando con Rails 5 y Materialize-Sass y estoy teniendo algunos problemas con el comportamiento predeterminado de Rails para tratar validaciones de campo fallidas como en la imagen a continuación y fue debido a la divadición adicional a los campos de entrada donde falló la validación.

ingrese la descripción de la imagen aquí

Trabajando con @Phobetron answer y modificando la respuesta de Hugo Demiglio también. Hice algunos ajustes a esos bloques de código y consigo que algo funcione bien en los siguientes casos:

  • Si ambos inputy labeltiene su propio classatributo en cualquier lugar
    • <input type="my-field" class="control">
    • <label class="active" for="...">My field</label>
  • Si las etiquetas inputo labelno tienen un classatributo
    • <input type="my-field">
    • <label for="...">My field</label>
  • si la labeletiqueta tiene otra etiqueta dentro con elclass attribute
    • <label for="..."><i class="icon-name"></i>My field</label>

En todos esos casos, la errorclase se agregará a las clases existentes en el classatributo si existe o se creará si no está presente en la etiqueta o en las etiquetas de entrada .

ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
    class_attr_index = html_tag.index('class="')
    first_tag_end_index = html_tag.index('>')

    # Just to inspect variables in the console
    puts '😎 ' * 50
    pp(html_tag)
    pp(class_attr_index)
    pp(first_tag_end_index)

    if class_attr_index.nil? || class_attr_index > first_tag_end_index
        html_tag.insert(first_tag_end_index, ' class="error"')
    else
        html_tag.insert(class_attr_index + 7, 'error ')
    end

    # Just to see resulting tag in the console
    pp(html_tag)
end

Espero que pueda ser útil para alguien con las mismas condiciones como yo.

alexventuraio
fuente
4

Además de la respuesta @phobetron, que no funciona cuando tiene otra etiqueta con atributo de clase, como <label for="..."><i class="icon my-icon"></i>My field</label>.

Hice algunos cambios en su solución:

# config/initializers/field_with_error.rb

ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
  class_attr_index = html_tag.index('class="')
  first_tag_end_index = html_tag.index('>')

  if class_attr_index.nil? || first_tag_end_index > class_attr_index
    html_tag.insert(class_attr_index + 7, 'error ')
  else
    html_tag.insert(first_tag_end_index, ' class="error"')
  end
end
Hugo Demiglio
fuente
Pero esto no funcionará si su campo no tiene un atributo de clase
mizurnix
2

Si por alguna razón todavía estás trabajando en Rails 2 (como yo), mira la publicación SO aquí .

Ofrece un script para poner en inicializadores.

ScottJShea
fuente
2

Una cosa a tener en cuenta (como descubrí trabajando en esto hoy) es que si flota los campos de etiqueta o de entrada (estoy flotando todos los campos de entrada a la derecha), el CSS se interrumpirá incluso si anula ActionView :: Base.field_error_proc.

Una alternativa es dejar caer un nivel más profundo en el formato CSS de esta manera:

.field_with_errors label {
  padding: 2px;
  background-color: red;
}

.field_with_errors input[type="text"] {
  padding: 3px 2px;
  border: 2px solid red;
}
Kevin Reeth
fuente
2

Hice una opción para deshabilitar esta cosa terrible para algunos objetos

# config/initializers/field_error_proc.rb

module ActiveModel::Conversion
  attr_accessor :skip_field_error_wrapper
end

ActionView::Base.field_error_proc = Proc.new {|html_tag, instance|
  if instance.object && instance.object.skip_field_error_wrapper
    html_tag.html_safe
  else
    "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe
  end
}

Entonces puede usarlo así:

@user.skip_field_error_wrapper = true
form_for(@user) do |f|
  ...
end
Pavel Evstigneev
fuente
1

Esta es mi solución basada en la respuesta de @ Phobetron. Al colocar este código application.rb, sus etiquetas <p>y <span>generadas por las form.error :pllamadas correspondientes recibirán la fields_with_errorsetiqueta css. El resto recibirá la errorclase CSS.

config.action_view.field_error_proc = Proc.new { |html_tag, instance|
  class_attr_index = html_tag.index 'class="'

  if class_attr_index
    # target only p's and span's with class error already there
    error_class = if html_tag =~ /^<(p|span).*error/
      'field_with_errors '
    else
      'error '
    end

    html_tag.insert class_attr_index + 7, error_class
  else
    html_tag.insert html_tag.index('>'), ' class="error"'
  end
}

De esta manera, encontré el más flexible y discreto de todos los anteriores para diseñar la respuesta en mis formularios.

dgilperez
fuente
1

Si es solo para fines de estilo (no le importa div), puede agregar esto a su CSS:

div.field_with_errors {
 display: inline;
}

La divactuará como una spany que no interfiera con su diseño (ya que dives un elemento de bloque - display: block;- por defecto, se hará una nueva línea después de que se cierra; spanes inline, por lo que no lo hace).

usuario2985898
fuente
1

Si solo desea desactivar los errores de ciertos elementos, por ejemplo , casillas de verificación , puede hacer esto:

ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
  doc = Nokogiri::HTML::Document.parse(html_tag)
  if doc.xpath("//*[@type='checkbox']").any?
    html_tag
  else
    "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe
  end
end
Tintín81
fuente
0

Si solo se trata de problemas de estilo, podemos sobrescribir "field_with_errors". Pero como eso podría afectar a otros formularios en nuestra aplicación, es mejor sobrescribir la clase "field_with_errors" solo en ese formulario.

Teniendo en cuenta 'parent_class' es una de las clases principales para el campo de error del formulario (ya sea la clase del formulario o la clase de cualquiera de los elementos principales para el campo de error), entonces

  .parent_class .field_with_errors {
    display: inline;
  }

Solucionará el problema y no perturbará ninguna otra forma en nuestra aplicación.

O

Si necesitamos anular el estilo de "field_with_errors" para toda la aplicación, entonces como dijo @dontangg,

.field_with_errors { display: inline; } 

Hará la solución. Espero eso ayude :)

Prem
fuente
0

Si no desea cambiar field_error_procpara toda su aplicación, jQuery's wraprap puede proporcionar una solución más específica para áreas problemáticas específicas, por ejemplo,

$('FORM .field_with_errors > INPUT[type="checkbox"]').unwrap();
Steve
fuente