Rails 3: wrapper“ field-with-error” เปลี่ยนลักษณะของเพจ จะหลีกเลี่ยงสิ่งนี้ได้อย่างไร?


132

ฟิลด์อีเมล:

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

มีลักษณะดังนี้:

without_error

แต่หากการตรวจสอบอีเมลล้มเหลวจะกลายเป็น:

<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ห่อ? Rails ตัดสินใจว่าจะห่ออย่างไร?
Misha Moroshko

4
ซึ่งอาจทำได้เพื่อให้คุณสามารถจัดรูปแบบป้ายกำกับของฟิลด์ที่มีข้อผิดพลาดได้เช่นกัน นอกจากนี้รางยังรู้ว่าต้องห่ออะไรเพราะคุณบอกว่าช่องใดเป็นของแอตทริบิวต์ของทรัพยากรที่คุณกำลังสร้างแบบฟอร์มสำหรับ: f.label :passwordและf.password_field :passwordใน@resource.errorsนั้นจะมี[:password]ชุดข้อผิดพลาด
Mosselman

3
หากคุณกำลังทำงานกับ twitter bootstrap หรือคุณต้องการตัวอย่างอื่น ๆ ของสิ่งที่คุณสามารถทำได้ใน field_error_proc ให้ตรวจสอบส่วนสำคัญที่ยอดเยี่ยมนี้: gist.github.com/1464315
Ryan Sandridge

2
ทำไมจึงมี "# {html_tag}". html_safe ไม่ใช่ html_tag.html_safe
Anurag

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

1
ฉันไม่เห็นว่ามันเป็นการแฮ็ก displayคุณสมบัติถูกนำมาใช้ก่อน CSS นี้จะมีการเพิ่มมีที่blockซึ่งจะก่อให้เกิดความแตกต่างของภาพที่ไม่ต้องการ ไม่ลบล้างรูปแบบการจัดวางอื่น ๆ บนแท็ก อย่างไรก็ตามคำตอบของ Ryan Bigg นั้นสมบูรณ์แบบหากคุณต้องการเปลี่ยน / ลบแท็กที่ปิดฟิลด์ด้วยข้อผิดพลาด
dontangg

ฉันได้ลองแล้วอย่างไรก็ตามหากฟิลด์ของคุณอยู่ภายในหาก <p> แท็กดูเหมือนว่าจะใช้งานไม่ได้ (อย่างน้อยก็ไม่ใช่บน Firefox) เนื่องจาก <div> ภายใน <p> ขีด จำกัด ไม่ว่าจะเกิดอะไรขึ้นก็ตาม การใช้โซลูชัน Biggs การแทนที่ <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
สิ่งนี้ยอดเยี่ยมสำหรับการใช้ช่องข้อผิดพลาดในลักษณะที่ไม่เป็นการรบกวน
Ryan

1
ทำงานบน Rails 4.0.3
Yuki Matsukura

1
ทำงานให้ฉัน ต้องรีสตาร์ทเซิร์ฟเวอร์รางเพื่อสังเกตการเปลี่ยนแปลงแม้ว่า :)
Jezen Thomas

1
ฉันชอบโซลูชันนี้ แต่จะใช้ไม่ได้หากคุณมีแท็กอื่นในไฟล์label.
Caio Tarifa

สวัสดี @Phobetron นี่เป็นทางออกที่ดีจริงๆ ฉันค้นหาวิธีที่จะเพิ่มระดับในระดับที่ผู้ปกครองแทนเช่นอธิบายไว้ที่นี่ ฉันเจาะเข้าไปในรหัสราง แต่ฉันเสียตัวทันทีที่ไม่สามารถทำตามขั้นตอนการเรนเดอร์ด้วย byebug .. คุณคิดว่านี่เป็นไปได้จริงหรือ?
SanjiBukai

21

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 }

หรือคุณสามารถเปลี่ยนเป็นสิ่งที่เหมาะกับ UI ของคุณ:

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 และแก้ไขคำตอบของ Hugo Demiglio ด้วย ฉันได้ทำการปรับเปลี่ยนบล็อกโค้ดเหล่านั้นและพบว่าบางอย่างทำงานได้ดีในกรณีต่อไปนี้:

  • หากทั้งสอง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

หากมีเหตุผลบางอย่างที่คุณยังคงทำงานบนรางที่ 2 (เหมือนผม) ตรวจสอบการโพสต์ SO ที่นี่

มีสคริปต์ที่จะใส่ใน initializers


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ของคุณที่สร้างโดยการเรียกที่เกี่ยวข้องจะได้รับแท็ก css ส่วนที่เหลือจะได้รับคลาส CSS<p><span>form.error :pfields_with_errorserror

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นี้จะทำหน้าที่เหมือน a 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();

0

คุณสามารถปิดการใช้งาน<div class="field_with_errors">div พิเศษได้อย่างง่ายดายหากคุณไม่ต้องการเลยสำหรับองค์ประกอบรูปแบบเฉพาะ เช่นถ้าคุณไม่ต้องการมัน<label>'s ใช้กำหนดเองFormBuilder

เช่น:

class MyFormBuilder < ActionView::Helpers::FormBuilder
  # Strip the containing div for labels associated with invalid fields:
  def label(method, text = nil, options = {}, &block)
    super(method, text, options, &block).gsub(%r{<div.*?>|<\/div>}, '').html_safe
  end
end

และจากนั้นทั้งเพิ่ม, builder: MyFormBuilderที่คุณform_with/ form_forในมุมมองหรือเพิ่มdefault_form_builder MyFormBuilderไปยังตัวควบคุมของคุณ (หรือควบคุมฐานถ้าคุณอยากให้มันเป็นพฤติกรรมที่ทั่วโลก)

คุณสามารถทำเช่นเดียวกันสำหรับอินพุตและองค์ประกอบรูปแบบอื่น ๆ ได้เช่นกัน

เครดิตJack Caseyสำหรับคำตอบนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.