Rails 3: wrapper "field-with-errors" mengubah tampilan halaman. Bagaimana cara menghindarinya?

131

Bidang email:

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

terlihat seperti ini:

tanpa error

Tetapi, jika validasi email gagal, itu menjadi:

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

yang terlihat seperti ini:

with_error

Bagaimana saya bisa menghindari perubahan penampilan ini?

Misha Moroshko
sumber
Hai @ misha-moroshko, saya coba tambahkan kelas kesalahan di tingkat induk seperti dijelaskan di sini . Saya mencoba untuk menyelam ke dalam kode rel menggunakan byebug tetapi saya segera hilang .. Saya ingin mengatur perilaku ini dengan cara yang cerdas dengan memeriksa apakah bidang tersebut memiliki orang tua ..
SanjiBukai

Jawaban:

235

Anda harus mengganti ActionView::Base.field_error_proc. Saat ini didefinisikan sebagai ini di dalam ActionView::Base:

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

Anda bisa menimpanya dengan meletakkan ini di kelas aplikasi Anda di dalam config/application.rb:

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

Mulai ulang server rel agar perubahan ini diterapkan.

Ryan Bigg
sumber
4
Satu pertanyaan kecil: Mengapa keduanya labeldan inputyang dibungkus? Bagaimana Rails memutuskan apa yang akan dibungkus?
Misha Moroshko
4
Ini mungkin dilakukan agar Anda dapat menata label bidang dengan kesalahan juga. Juga, rel tahu apa yang harus dibungkus karena Anda memberi tahu bidang mana yang termasuk atribut sumber daya yang Anda buat formulirnya: f.label :passworddan f.password_field :passworddi @resource.errorssana akan ada [:password]set kesalahan.
Mosselman
3
Jika Anda bekerja dengan bootstrap twitter, atau Anda ingin contoh lain dari apa yang dapat Anda lakukan di field_error_proc, lihat intisari yang mengagumkan ini: gist.github.com/1464315
Ryan Sandridge
2
Mengapa orang melakukan "# {html_tag}". Html_safe, dan bukan html_tag.html_safe?
Anurag
3
Anurag: jika html_tag kebetulan nol, atau apa pun selain string, maka html_tag.html_safe biasa akan menimbulkan kesalahan. Meletakkannya di "# {html_tag}" secara implisit memanggil html_tag.to_s, yang diharapkan akan mengembalikan sebuah string, yang kemudian akan dapat menanggapi html_safe
sockmonk
100

Perbedaan visual yang Anda lihat terjadi karena divelemen tersebut adalah elemen blok. Tambahkan gaya ini ke file CSS Anda untuk membuatnya berperilaku seperti elemen inline:

.field_with_errors { display: inline; }
dontangg
sumber
2
Ini adalah retasan terbaik karena meniadakan display:properti apa pun yang digunakan (dan gaya tata letak lainnya) di html_tag.
Ryan
1
Saya tidak melihatnya sebagai retasan. The displayproperti yang digunakan sebelum css ini ditambahkan yaitu blockyang menyebabkan perbedaan visual yang tidak diinginkan. Itu tidak meniadakan gaya tata letak lain pada tag. Namun, jawaban Ryan Bigg sempurna jika Anda ingin mengubah / menghapus tag yang membungkus bidang dengan kesalahan.
dontangg
Saya mencoba ini, namun, jika bidang Anda ada di dalam jika <p> tag, sepertinya tidak berfungsi (setidaknya tidak pada Firefox) karena <div> di dalam <p> memecah baris apa pun yang terjadi. Menggunakan solusi Biggs, hanya mengganti <div dengan <span tampaknya melakukan trik.
jpw
72

Saat ini saya menggunakan solusi ini, ditempatkan di penginisialisasi:

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

Ini memungkinkan saya untuk hanya menambahkan nama kelas ke tag yang sesuai, tanpa membuat elemen tambahan.

Phobetron
sumber
2
Ini luar biasa untuk menggunakan bidang kesalahan dengan cara yang tidak mencolok.
Ryan
1
Bekerja pada Rails 4.0.3.
Yuki Matsukura
1
Bekerja untukku. Harus me-restart rel server untuk melihat perubahan :)
Jezen Thomas
1
Saya menyukai solusi ini, tetapi tidak akan berfungsi jika Anda memiliki tag lain di dalamnya label.
Caio Tarifa
Hai @ Pitobetron, memang ini solusi yang bagus. Saya mencari cara untuk menambahkan kelas itu di tingkat induk alih-alih seperti dijelaskan di sini . Saya menyelam ke dalam kode rails tetapi saya kehilangan diri saya dengan segera tidak dapat mengikuti proses rendering dengan byebug .. Apakah Anda pikir ini sebenarnya mungkin?
SanjiBukai
20

Kode tambahan sedang ditambahkan oleh ActionView::Base.field_error_proc. Jika Anda tidak menggunakan field_with_errorsuntuk membuat gaya formulir Anda, Anda dapat menimpanya di application.rb:

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

Atau, Anda dapat mengubahnya ke sesuatu yang sesuai dengan UI Anda:

config.action_view.field_error_proc = Proc.new { |html_tag, instance| "<span class='field_with_errors'>#{html_tag}</span>".html_safe }
Dan Cheail
sumber
Ini bekerja dengan baik untuk saya; tampaknya menjadi solusi paling elegan untuk digunakan dengan Twitter Bootstrap
Avishai
5

Saya bekerja dengan Rails 5 dan Materialize-Sass dan saya mendapatkan beberapa masalah dengan perilaku default dari Rails untuk menangani validasi bidang yang gagal seperti pada gambar di bawah ini dan itu karena tambahan yang divditambahkan ke bidang input di mana validasi gagal.

masukkan deskripsi gambar di sini

Bekerja dengan @Phobetron menjawab dan memodifikasi jawaban Hugo Demiglio juga. Saya membuat beberapa penyesuaian pada blok kode tersebut dan saya mendapatkan sesuatu yang berfungsi dengan baik dalam kasus-kasus berikut:

  • Jika keduanya inputdan labelmemiliki classatribut sendiri di mana saja
    • <input type="my-field" class="control">
    • <label class="active" for="...">My field</label>
  • Jika tag inputatau labeltidak memiliki classatribut
    • <input type="my-field">
    • <label for="...">My field</label>
  • jika labeltag memiliki tag lain di dalam denganclass attribute
    • <label for="..."><i class="icon-name"></i>My field</label>

Dalam semua kasus tersebut, errorkelas akan ditambahkan ke kelas yang ada di classatribut jika ada atau akan dibuat jika tidak ada dalam label atau tag 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

Saya berharap ini bisa bermanfaat bagi seseorang dengan kondisi yang sama seperti saya.

alexventuraio
sumber
4

Selain jawaban @ phobetron, yang tidak berfungsi saat Anda memiliki tag lain dengan atribut kelas, seperti <label for="..."><i class="icon my-icon"></i>My field</label>.

Saya melakukan beberapa perubahan pada solusinya:

# 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
sumber
Tetapi ini tidak akan berfungsi jika bidang Anda tidak memiliki atribut kelas
mizurnix
2

Jika karena alasan tertentu Anda masih mengerjakan Rails 2 (seperti saya), periksa pos SO di sini .

Ia menawarkan skrip untuk dimasukkan ke inisialisasi.

ScottJShea
sumber
2

Satu hal yang perlu diingat (seperti yang saya temukan saat mengerjakan ini hari ini) adalah bahwa jika Anda mengapungkan bidang label atau input (saya mengapungkan semua bidang input dengan benar), css akan pecah bahkan jika Anda menimpa ActionView :: Base.field_error_proc.

Alternatifnya adalah dengan menurunkan level lebih dalam pada format CSS seperti:

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

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

Saya membuat pilihan untuk menonaktifkan hal mengerikan ini untuk beberapa objek

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

Jadi bisa menggunakannya seperti ini:

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

Ini adalah solusi saya membangun di atas jawaban @ Phobetron. Menempatkan kode ini di application.rb, tag Anda <p>dan yang <span>dihasilkan oleh form.error :ppanggilan yang sesuai akan menerima fields_with_errorstag css. Sisanya akan menerima errorkelas 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
}

Saya menemukan cara ini yang paling fleksibel dan tidak mencolok dari semua sebelumnya untuk gaya respons di formulir saya.

dgilperez
sumber
1

Jika itu hanya untuk keperluan penataan (Anda tidak keberatan div), Anda bisa menambahkan ini ke css Anda:

div.field_with_errors {
 display: inline;
}

The divakan bertindak seperti spandan itu tidak akan mengganggu desain Anda (karena divmerupakan elemen blok - display: block;- secara default, maka akan menyebabkan baris baru setelah itu menutup, spanadalah inline, sehingga tidak).

pengguna2985898
sumber
1

Jika Anda hanya ingin mematikan kesalahan untuk elemen tertentu, misalnya kotak centang , Anda dapat melakukan ini:

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
Tintin81
sumber
0

Jika hanya tentang masalah gaya, kita dapat menimpa "field_with_errors". Tapi karena itu mungkin mempengaruhi formulir lain di aplikasi kita, lebih baik menimpa kelas "field_with_errors" dengan hanya dalam bentuk itu.

Mempertimbangkan 'parent_class' adalah salah satu kelas induk untuk bidang kesalahan formulir (baik kelas formulir atau kelas elemen induk mana pun untuk bidang kesalahan), lalu

  .parent_class .field_with_errors {
    display: inline;
  }

Itu akan memperbaiki masalah juga, itu tidak akan mengganggu bentuk lain dalam aplikasi kami juga.

ATAU

Jika kita perlu mengganti gaya "field_with_errors" untuk seluruh aplikasi, maka seperti yang dikatakan @dontangg,

.field_with_errors { display: inline; } 

akan melakukan perbaikan. Semoga bermanfaat :)

Prem
sumber
0

Jika Anda tidak ingin mengubah field_error_procseluruh aplikasi Anda, buka jQuery dapat memberikan solusi yang lebih bertarget untuk area masalah tertentu, misalnya,

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