Rails 3: обгортка "поле з помилками" змінює зовнішній вигляд сторінки. Як цього уникнути?


131

Поле електронної пошти:

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

виглядає так:

без_помилки

Але якщо перевірка електронної пошти не вдалася, вона стає:

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

що виглядає приблизно так:

with_error

Як я міг уникнути цієї зміни зовнішності?


Привіт @ misha-moroshko, я намагаюся додати клас помилок на батьківському рівні, як описано тут . Я спробував зануритися в код рейки за допомогою byebug, але я негайно загубився. Я хотів налаштувати цю поведінку трохи розумно, перевіривши, чи є у цього поля батько ..
SanjiBukai

Відповіді:


235

Вам слід перекрити ActionView::Base.field_error_proc. Наразі це визначено у межах ActionView::Base:

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

Ви можете змінити його, помістивши це в клас вашої програми всередині config/application.rb:

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

Перезапустіть сервер рейки, щоб ця зміна набула чинності.


4
Одне маленьке запитання: Чому labelі inputзагорнуті, і ці ? Як Рейки вирішують, що обгортати?
Міша Морошко

4
Це, мабуть, зроблено для того, щоб ви могли також стилізувати мітку поля з помилками. Крім того , рейки знає , що загорнути , тому що ви вказати, які поля належать до того , що атрибут ресурсу ваші роблять форму для: f.label :passwordі f.password_field :passwordв НЕ @resource.errorsбуло б [:password]безліч помилок.
Моссельман

3
Якщо ви працюєте з завантажувальним твіттером або хочете ще один приклад того, що ви можете зробити у field_error_proc, виберіть цей дивовижний суть: gist.github.com/1464315
Ryan

2
Чому можна зробити "# {html_tag}". Html_safe, а не html_tag.html_safe?
Анураг

3
Anurag: якщо html_tag не дорівнює нулю, або щось інше, ніж рядок, то звичайна html_tag.html_safe може призвести до помилки. Помістивши його в "# {html_tag}" неявно викликає html_tag.to_s, який, сподіваємось, поверне рядок, який зможе відповісти на html_safe
sockmonk

100

Візуальна різниця, яку ви бачите, відбувається тому, що divелемент - це блок-елемент. Додайте цей стиль у свій CSS-файл, щоб він поводився як вбудований елемент:

.field_with_errors { display: inline; }

2
Це хакер у кращому випадку, оскільки він заперечує будь-яку display:властивість (та інші стилі компонування) на html_tag.
Райан

1
Я не вважаю це хакером. displayВластивість використовується перед цим КСС додається , blockяка викликає візуальне відмінність, не бажано. Це не скасовує жодних інших стилів компонування тегу. Однак відповідь Райана Бігга ідеальна, якщо ви хочете змінити / вилучити тег, який обертає поле помилками.
dontangg

Я спробував це, однак, якщо ваші поля знаходяться всередині тегів <p>, схоже, це не працює (принаймні, не на Firefox), оскільки <div> в межах <p> розбиває рядки незалежно від того. Використовуючи рішення Біггса, лише заміна <div на <span, здається, виконає трюк.
jpw

72

Зараз я використовую це рішення, розміщене в ініціалізаторі:

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

Це дозволяє мені просто додати ім'я класу до відповідного тегу, не створюючи додаткових елементів.


2
Це приголомшливо для використання полів помилок ненав’язливим чином.
Райан

1
Працює на рейках 4.0.3.
Юкі Мацукура

1
Працювали для мене. Довелося перезапустити сервер
рейлів,

1
Мені подобалося це рішення, але воно не спрацює, якщо у вас є інший тег label.
Caio Tarifa

Привіт @Phobetron, справді це приємне рішення. Я шукаю спосіб додати цей клас на батьківському рівні замість описаного тут . Я занурююся в код рейки, але я втратив себе негайно, не маючи змоги стежити за процесом візуалізації за допомогою байбака. Ви розумієте, що це насправді можливо?
SanjiBukai

20

Додатковий код додається користувачем ActionView::Base.field_error_proc. Якщо ви не використовуєте field_with_errorsстиль для форми, ви можете змінити її в application.rb:

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

Ви також можете змінити його на щось, що відповідає вашому інтерфейсу користувача:

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

Це добре працює для мене; здається, найелегантніше рішення для використання у Twitter Bootstrap
Avishai

5

Я працюю з Rails 5 та Materialize-Sass, і я отримую деякі проблеми з поведінкою за замовчуванням від Rails для лікування невдалої перевірки поля, як на зображенні нижче, і це було через додаткові divдодані до полів вводу, де перевірка не вдалася.

введіть тут опис зображення

Робота з відповіддю @Phobetron і модифікація відповіді Уго Демільо. Я вніс деякі корективи в ці блоки коду, і я можу щось добре працювати в таких випадках:

  • Якщо і те, inputі інше labelє свій classатрибут
    • <input type="my-field" class="control">
    • <label class="active" for="...">My field</label>
  • Якщо inputабо або labelтеги не мають classатрибута
    • <input type="my-field">
    • <label for="...">My field</label>
  • якщо в labelтезі є інший тег всерединіclass attribute
    • <label for="..."><i class="icon-name"></i>My field</label>

У всіх цих випадках errorклас буде додано до існуючих класів в classатрибуті при наявності або він буде створений , якщо він не присутній в етикетці або введення тегів.

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

Я сподіваюся, що це може бути корисним для тих, хто має такі самі умови, як я.


4

На додаток до відповіді @phobetron, яка не працює, якщо у вас є інший тег з атрибутом класу, наприклад <label for="..."><i class="icon my-icon"></i>My field</label>.

Я змінив його рішення:

# 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

Але це не спрацює, якщо у вашому полі немає атрибута класу
mizurnix

2

Якщо з якоїсь причини ви все ще працюєте над Rails 2 (як і я), ознайомтеся з повідомленням SO тут .

Він пропонує сценарій для розміщення в ініціалізаторах.


2

Одне, що потрібно пам’ятати (як я виявив, що зараз це працює), це те, що якщо ви будете плавати або міткою, або полями введення (я плаваю всі поля вводу правильно), css зламається, навіть якщо ви перекриєте ActionView :: Base.field_error_proc.

Альтернатива - опустити рівень глибше у форматі CSS так:

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

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

2

Я зробив можливість відключити цю жахливу річ для деяких об’єктів

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

Тож можна використовувати його так:

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

1

Це моє рішення на основі відповіді @ Phobetron. Розмістивши цей код application.rb, ваші <p>та <span>теги, згенеровані відповідними form.error :pдзвінками, отримають fields_with_errorsтег css. Решта отримає errorклас 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
}

Я вважав цей спосіб найбільш гнучким і ненав'язливим з усіх попередніх, щоб стилізувати відповіді в моїх формах.


1

Якщо це лише для цілей стилізації (ви не заперечуєте проти div), ви можете просто додати це до свого css:

div.field_with_errors {
 display: inline;
}

divПоводитиметься як spanі він не буде заважати вашій конструкції (так як divце блоковий елемент - display: block;- за замовчуванням, це викличе нову лінію після того, як вона закривається, spanце inline, так що це робить).


1

Якщо ви просто хочете вимкнути помилки для певних елементів, наприклад, прапорці , ви можете зробити це:

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

Якщо мова йде лише про стильові проблеми, ми можемо замінити "field_with_errors". Але як це може вплинути на інші форми в нашому додатку, краще замінити клас "field_with_errors" лише в цій формі.

Враховуючи, що "parent_class" є одним із батьківського класу для поля помилки форми (або класу форми, або класу будь-якого з батьківських елементів для поля помилки), то

  .parent_class .field_with_errors {
    display: inline;
  }

Це також виправить цю проблему, а також не завадить будь-яким іншим формам у нашій програмі.

АБО

Якщо нам потрібно перекрити стиль "field_with_errors" для всього додатка, то, як сказав @dontangg,

.field_with_errors { display: inline; } 

зробимо виправлення. Сподіваюся, це допомагає :)


0

Якщо ви не хочете змінювати field_error_procвсю програму, розгортання jQuery може запропонувати більш цільове рішення для конкретних проблемних областей, наприклад,

$('FORM .field_with_errors > INPUT[type="checkbox"]').unwrap();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.