Rotaie 3: il wrapper "campo con errori" modifica l'aspetto della pagina. Come evitarlo?


131

Campo email:

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

Somiglia a questo:

senza errori

Ma, se la convalida dell'email fallisce, diventa:

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

che assomiglia a questo:

with_error

Come posso evitare questo cambiamento di aspetto?


Ciao @ misha-moroshko, provo ad aggiungere la classe di errore a livello genitore come descritto qui . Ho provato ad immergermi nel codice delle rotaie usando byebug ma mi sono perso immediatamente .. Volevo impostare questo comportamento in un modo un po 'intelligente controllando se quei campi hanno un genitore ..
SanjiBukai

Risposte:


235

Dovresti scavalcare ActionView::Base.field_error_proc. Attualmente è definito come questo all'interno di ActionView::Base:

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

Puoi sovrascriverlo inserendo questo nella classe della tua applicazione all'interno config/application.rb:

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

Riavvia il server rails per rendere effettiva questa modifica.


4
Una piccola domanda: perché sia ​​il sia labelil inputsono avvolti? Come Rails decide cosa avvolgere?
Misha Moroshko,

4
Questo è probabilmente fatto in modo che tu possa modellare l'etichetta di un campo anche con errori. Inoltre, le rotaie sanno cosa avvolgere perché dici a quali campi appartengono a quale attributo della risorsa stai creando il modulo: f.label :passworde f.password_field :passwordnella @resource.errorsci sarebbe un [:password]set di errori.
Mosselman,

3
Se stai lavorando con Twitter Bootstrap o vuoi un altro esempio di cosa puoi fare in field_error_proc, chiedi questo fantastico esempio: gist.github.com/1464315
Ryan Sandridge,

2
Perché uno dovrebbe fare "# {html_tag}". Html_safe e non html_tag.html_safe?
Anurag,

3
Anurag: se html_tag risulta essere zero, o qualcosa di diverso da una stringa, un semplice html_tag.html_safe genererebbe un errore. Mettendolo in "# {html_tag}" chiama implicitamente html_tag.to_s, che si spera restituirà una stringa, che sarà quindi in grado di rispondere a html_safe
sockmonk

100

La differenza visiva che stai vedendo sta accadendo perché l' divelemento è un elemento a blocchi. Aggiungi questo stile al tuo file CSS per farlo comportare come un elemento incorporato:

.field_with_errors { display: inline; }

2
Questo è un hack nella migliore delle ipotesi perché annulla qualsiasi display:proprietà utilizzata (e altri stili di layout) sul html_tag.
Ryan,

1
Non lo vedo come un trucco. La displayproprietà utilizzata prima dell'aggiunta di questo CSS blockè la causa della differenza visiva non desiderata. Non nega altri stili di layout sul tag. Tuttavia, la risposta di Ryan Bigg è perfetta se si desidera modificare / rimuovere il tag che avvolge il campo con errori.
Dontangg,

Ho provato questo, tuttavia, se i tuoi campi sono all'interno dei tag <p>, non sembra funzionare (almeno non su Firefox) poiché un <div> all'interno di un <p> rompe le linee, non importa quale. Usando la soluzione Biggs, solo la sostituzione di <div con <span sembra fare il trucco.
jpw,

72

Attualmente uso questa soluzione, inserita in un inizializzatore:

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

Questo mi permette di aggiungere semplicemente un nome di classe al tag appropriato, senza creare elementi aggiuntivi.


2
Questo è fantastico per usare i campi di errore in modo discreto.
Ryan,

1
Funziona su Rails 4.0.3.
Yuki Matsukura,

1
Ha funzionato per me. Ho dovuto riavviare il server Rails per notare le modifiche :)
Jezen Thomas,

1
Ho adorato questa soluzione, ma non funzionerà se hai altri tag all'interno di label.
Caio Tarifa,

Ciao @Phobetron, in effetti questa è una bella soluzione. Cerco un modo per aggiungere quella classe a livello genitore invece come descritto qui . Mi sono immerso nel codice delle rotaie ma mi sono perso immediatamente non potendo seguire il processo di rendering con byebug. Credi che sia effettivamente possibile?
SanjiBukai,

20

Il codice aggiuntivo è stato aggiunto da ActionView::Base.field_error_proc. Se non stai utilizzando field_with_errorslo stile del modulo, puoi sostituirlo in application.rb:

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

In alternativa, puoi cambiarlo in qualcosa adatto alla tua UI:

config.action_view.field_error_proc = Proc.new { |html_tag, instance| "<span class='field_with_errors'>#{html_tag}</span>".html_safe }

Questo funziona bene per me; sembra essere la soluzione più elegante da utilizzare con Twitter Bootstrap
Avishai

5

Sto lavorando con Rails 5 e Materialise-Sass e sto riscontrando alcuni problemi con il comportamento predefinito di Rails per trattare le convalide dei campi non riuscite come nell'immagine seguente ed è stato a causa dell'aggiunta divaggiuntiva ai campi di input in cui la convalida non è riuscita.

inserisci qui la descrizione dell'immagine

Lavorare con la risposta di @Phobetron e modificare anche la risposta di Hugo Demiglio. Ho apportato alcune modifiche a quei blocchi di codice e ottengo qualcosa che funzioni bene nei seguenti casi:

  • Se entrambi inpute labelhanno il loro classattributo ovunque
    • <input type="my-field" class="control">
    • <label class="active" for="...">My field</label>
  • Se il tag inputo labelnon ha un classattributo
    • <input type="my-field">
    • <label for="...">My field</label>
  • se il labeltag ha un altro tag all'interno conclass attribute
    • <label for="..."><i class="icon-name"></i>My field</label>

In tutti questi casi, la errorclasse verrà aggiunta alle classi esistenti classnell'attributo se esiste o verrà creata se non è presente nell'etichetta o nei tag di input .

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

Spero possa essere utile per qualcuno con le stesse condizioni come me.


4

Oltre alla risposta @phobetron, che non funziona quando hai altri tag con attributo di classe, come <label for="..."><i class="icon my-icon"></i>My field</label>.

Ho apportato alcune modifiche alla sua soluzione:

# 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

Ma questo non funzionerà se il tuo campo non ha un attributo class
mizurnix

2

Se per qualche motivo stai ancora lavorando su Rails 2 (come me), dai un'occhiata al post SO qui .

Offre uno script da inserire negli inizializzatori.


2

Una cosa da tenere a mente (come ho scoperto lavorando oggi) è che se si galleggiano l'etichetta o i campi di input (sto galleggiando tutti i campi di input a destra), i CSS si interrompono anche se si esegue l'override di ActionView :: Base.field_error_proc.

Un'alternativa è abbassare un livello più profondo nella formattazione CSS in questo modo:

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

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

2

Ho fatto un'opzione per disabilitare questa cosa terribile per alcuni oggetti

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

Quindi puoi usarlo in questo modo:

@user.skip_field_error_wrapper = true
form_for(@user) do |f|
  ...
end

1

Questa è la mia soluzione basata sulla risposta di @ Phobetron. Inserendo questo codice application.rb, i tag your <p>e <span>generati dalle form.error :pchiamate corrispondenti riceveranno il fields_with_errorstag css. Il resto riceverà la errorclasse 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
}

Ho trovato in questo modo il più flessibile e discreto di tutti i precedenti per modellare la risposta attraverso le mie forme.


1

Se è solo per scopi di stile (non ti dispiace div), puoi semplicemente aggiungere questo al tuo CSS:

div.field_with_errors {
 display: inline;
}

Si divcomporterà come un spane non interferirà con il tuo progetto (poiché divè un elemento a blocchi - display: block;- per impostazione predefinita, causerà una nuova riga dopo la chiusura; spanè inline, quindi non lo fa).


1

Se desideri solo disattivare gli errori per determinati elementi, ad esempio caselle di controllo , puoi farlo:

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

0

Se si tratta solo di problemi di stile, possiamo sovrascrivere "field_with_errors". Ma poiché ciò potrebbe influire su altri moduli nella nostra applicazione, è meglio sovrascrivere la classe "field_with_errors" solo con quel modulo.

Considerando che 'parent_class' è una delle classi principali per il campo di errore del modulo (o la classe del modulo o la classe di uno qualsiasi degli elementi padre per il campo dell'errore), allora

  .parent_class .field_with_errors {
    display: inline;
  }

Risolverà il problema e non disturberà altre forme nella nostra applicazione.

O

Se abbiamo bisogno di sovrascrivere lo stile di "field_with_errors" per l'intera applicazione, allora come diceva @dontangg,

.field_with_errors { display: inline; } 

farà la correzione. Spero che sia d'aiuto :)


0

Se non si desidera cambiare field_error_procper l'intera applicazione, di jQuery decifratura in grado di fornire una soluzione più mirato per specifiche aree problematiche, ad esempio,

$('FORM .field_with_errors > INPUT[type="checkbox"]').unwrap();
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.