วิธีใช้ Rails Helpers: การสาธิต Bootstrap Carousel

เผยแพร่แล้ว: 2022-03-11

โครงสร้างในตัว Rails ที่ถูกใช้ในทางที่ผิด เข้าใจผิด และถูกละเลยมากที่สุดอย่างหนึ่งคือตัว ช่วยการดู อยู่ในไดเรกทอรี app/helpers ของคุณ และสร้างขึ้นโดยค่าเริ่มต้นกับโครงการ Rails ใหม่ทุกโครงการ ผู้ช่วยเหลือมักจะได้รับชื่อเสียงที่ไม่ดีว่าเป็นแหล่งทิ้งขยะสำหรับวิธีการแบบครั้งเดียวที่ใช้กับเลเยอร์การดูของแอปพลิเคชันทั้งหมด น่าเสียดายที่ Rails เองสนับสนุนการขาดโครงสร้างและองค์กรที่ไม่ดีโดยการรวมผู้ช่วยทั้งหมดไว้ในทุกมุมมองโดยค่าเริ่มต้น สร้างเนมสเปซส่วนกลางที่มีมลพิษ

แต่ถ้าผู้ช่วยของคุณมีความหมายมากขึ้น จัดระเบียบได้ดีขึ้น และสามารถใช้ซ้ำได้ในโครงการต่างๆ จะเกิดอะไรขึ้นหากสิ่งเหล่านี้เป็นมากกว่าฟังก์ชันแบบใช้ครั้งเดียวที่กระจายไปทั่วมุมมอง แต่วิธีการอันทรงพลังที่สร้างมาร์กอัปที่ซับซ้อนได้อย่างง่ายดาย โดยปล่อยให้มุมมองของคุณไม่มีตรรกะแบบมีเงื่อนไขและโค้ด

มาดูวิธีการทำสิ่งนี้เมื่อสร้างภาพหมุนด้วยเฟรมเวิร์ก Twitter Bootstrap ที่คุ้นเคยและการเขียนโปรแกรมเชิงวัตถุที่ล้าสมัย

เมื่อใดควรใช้ตัวช่วย Rails

มีรูปแบบการออกแบบที่แตกต่างกันมากมายที่สามารถใช้ในเลเยอร์มุมมองของ Rails: ผู้นำเสนอ นักตกแต่ง บางส่วน และผู้ช่วย เป็นต้น หลักการง่ายๆ ของฉันคือผู้ช่วยทำงานได้ดีเมื่อคุณต้องการสร้างมาร์กอัป HTML ที่ต้องมีโครงสร้างบางอย่าง คลาส CSS เฉพาะ ตรรกะตามเงื่อนไข หรือนำมาใช้ซ้ำในหน้าต่างๆ

ตัวอย่างที่ดีที่สุดของพลังของตัวช่วย Rails นั้นแสดงโดย FormBuilder พร้อมวิธีการที่เกี่ยวข้องทั้งหมดสำหรับการสร้างฟิลด์อินพุต เลือกแท็ก ป้ายกำกับ และโครงสร้าง HTML อื่นๆ วิธีการที่เป็นประโยชน์เหล่านี้จะสร้างมาร์กอัปให้กับคุณด้วยการตั้งค่าแอตทริบิวต์ที่เกี่ยวข้องทั้งหมดอย่างเหมาะสม ความสะดวกสบายแบบนี้ทำให้พวกเราทุกคนหลงรัก Rails ตั้งแต่แรก

ประโยชน์ของการใช้ตัวช่วยที่ออกแบบมาอย่างดีก็เหมือนกับโค้ดที่เขียนมาอย่างดีและสะอาด: การห่อหุ้ม การลดโค้ดซ้ำ (DRY) และการเก็บตรรกะให้พ้นสายตา

กายวิภาคของ Twitter Bootstrap Carousel

Twitter Bootstrap เป็นเฟรมเวิร์กส่วนหน้าที่ใช้กันอย่างแพร่หลาย ซึ่งมาพร้อมกับการรองรับในตัวสำหรับส่วนประกอบทั่วไป เช่น โมดอล แท็บ และภาพหมุน คอมโพเนนต์ Bootstrap เหล่านี้เป็นกรณีการใช้งานที่ยอดเยี่ยมสำหรับผู้ช่วยเหลือที่กำหนดเอง เนื่องจากมาร์กอัปมีโครงสร้างสูง ต้องมีคลาส ID และแอตทริบิวต์ข้อมูลบางประเภทที่ต้องตั้งค่าอย่างถูกต้องเพื่อให้ JavaScript ทำงานได้ และการตั้งค่าแอตทริบิวต์เหล่านั้นต้องใช้ตรรกะแบบมีเงื่อนไขเล็กน้อย

ม้าหมุน Bootstrap 3 มีมาร์กอัปต่อไปนี้:

 <div class="carousel slide" data-ride="carousel"> <!-- Indicators --> <ol class="carousel-indicators"> <li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li> <li data-target="#carousel-example-generic" data-slide-to="1"></li> <li data-target="#carousel-example-generic" data-slide-to="2"></li> </ol> <!-- Wrapper for slides --> <div class="carousel-inner"> <div class="item active"> <img src="..." alt="..."> </div> <div class="item"> <img src="..." alt="..."> </div> ... </div> <!-- Controls --> <a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"> <span class="glyphicon glyphicon-chevron-left"></span> </a> <a class="right carousel-control" href="#carousel-example-generic" data-slide="next"> <span class="glyphicon glyphicon-chevron-right"></span> </a> </div>

อย่างที่คุณเห็น มีโครงสร้างหลักสามแบบ: (1) ตัวบ่งชี้ (2) สไลด์รูปภาพ (3) ตัวควบคุมสไลด์

พิมพ์เขียวแสดงสี่เหลี่ยมผืนผ้าอัตราส่วนไวด์สกรีนที่อยู่ตรงกลาง (สไลด์) โดยมีวงกลมเล็กๆ สามวงอยู่ใกล้ด้านล่าง (ตัวบ่งชี้) ขนาบข้างด้วยสี่เหลี่ยมบางๆ สองอันที่มีลูกศรซ้ายและขวาตามลำดับ (ตัวควบคุม)
ชิ้นส่วนของ Bootstrap carousel

เป้าหมายคือสามารถสร้างวิธีการตัวช่วยเดียวที่รวบรวมรูปภาพและแสดงองค์ประกอบภาพหมุนทั้งหมด เพื่อให้มั่นใจว่าข้อมูล id แอตทริบิวต์ href และคลาส CSS ได้รับการตั้งค่าอย่างเหมาะสม

ผู้ช่วย

เริ่มต้นด้วยโครงร่างพื้นฐานของตัวช่วย:

 # app/helpers/carousel_helper.rb module CarouselHelper def carousel_for(images) Carousel.new(self, images).html end class Carousel def initialize(view, images) @view, @images = view, images end def html # TO FILL IN end private attr_accessor :view, :images end end

เมธอดตัวช่วย carousel_for จะคืนค่ามาร์กอัปภาพหมุนที่สมบูรณ์สำหรับ URL รูปภาพที่ระบุ แทนที่จะสร้างชุดของวิธีการแต่ละชุดเพื่อแสดงผลแต่ละส่วนของภาพหมุน (ซึ่งจะทำให้เราต้องส่งคอลเล็กชันรูปภาพและข้อมูลสถานะอื่น ๆ ไปยังแต่ละวิธี) เราจะสร้างคลาส Ruby แบบเก่าใหม่ที่เรียกว่า Carousel เพื่อ แสดงถึงข้อมูลวงล้อ คลาสนี้จะแสดงเมธอด html ซึ่งส่งคืนมาร์กอัปที่แสดงผลอย่างสมบูรณ์ เราเริ่มต้นด้วยการรวบรวมรูปภาพ URL ของ images และมุมมองบริบทของ view

โปรดทราบว่าพารามิเตอร์ view เป็นอินสแตนซ์ของ ActionView ซึ่งตัวช่วย Rails ทั้งหมดถูกผสมเข้าด้วยกัน เราส่งต่อไปยังอินสแตนซ์อ็อบเจกต์ของเราเพื่อเข้าถึงวิธีการช่วยเหลือในตัวของ Rails เช่น link_to , content_tag , image_tag และ safe_join ซึ่งเราจะใช้สร้างมาร์กอัปภายในคลาส นอกจากนี้เรายังจะเพิ่มมาโคร delegate เพื่อให้เราสามารถเรียกวิธีการเหล่านั้นได้โดยตรง โดยไม่ต้องอ้างถึง view :

 def html content = view.safe_join([indicators, slides, controls]) view.content_tag(:div, content, class: 'carousel slide') end private attr_accessor :view, :images delegate :link_to, :content_tag, :image_tag, :safe_join, to: :view def indicators # TO FILL IN end def slides # TO FILL IN end def controls # TO FILL IN end

เรารู้ว่าภาพหมุนประกอบด้วยสามองค์ประกอบที่แยกจากกัน ดังนั้นเรามาเริ่มวิธีที่จะให้มาร์กอัปสำหรับแต่ละรายการในท้ายที่สุด จากนั้นให้เมธอด html รวมเข้ากับแท็ก div ของคอนเทนเนอร์ โดยใช้คลาส Bootstrap ที่จำเป็นสำหรับตัวหมุนเอง

safe_join เป็นวิธีการที่มีอยู่แล้วภายในที่สะดวก ซึ่งจะเชื่อมชุดของสตริงเข้าด้วยกันและเรียก html_safe กับผลลัพธ์ โปรดจำไว้ว่า เรามีสิทธิ์เข้าถึงวิธีการเหล่านั้นผ่านพารามิเตอร์การ view ซึ่งเราส่งผ่านเมื่อเราสร้างอินสแตนซ์

เราจะสร้างตัวชี้วัดก่อน:

 def indicators items = images.count.times.map { |index| indicator_tag(index) } content_tag(:ol, safe_join(items), class: 'carousel-indicators') end def indicator_tag(index) options = { class: (index.zero? ? 'active' : ''), data: { target: uid, slide_to: index } } content_tag(:li, '', options) end

อินดิเคเตอร์เป็นรายการเรียงลำดับอย่างง่าย ol ที่มีองค์ประกอบรายการ li สำหรับแต่ละภาพในคอลเลกชัน ตัวบ่งชี้รูปภาพที่ทำงานอยู่ในปัจจุบันต้องการคลาส CSS ที่ active ดังนั้นเราจะตรวจสอบให้แน่ใจว่าได้ตั้งค่าตัวบ่งชี้นี้สำหรับตัวบ่งชี้แรกที่เราสร้างขึ้น นี่เป็นตัวอย่างที่ดีของตรรกะ ซึ่งปกติแล้วจะต้องอยู่ในมุมมองด้วยตัวมันเอง

โปรดสังเกตว่าตัวบ่งชี้จำเป็นต้องอ้างอิง id เฉพาะขององค์ประกอบภาพหมุนที่มี (ในกรณีที่มีภาพหมุนมากกว่าหนึ่งหน้า) เราสามารถสร้าง id นี้ได้อย่างง่ายดายใน initializer และใช้งานได้ทั่วทั้งคลาส (โดยเฉพาะภายใน indicator และส่วนควบคุม) การทำเช่นนี้โดยทางโปรแกรมภายในเมธอดตัวช่วยช่วยให้มั่นใจว่า id จะสอดคล้องกันในองค์ประกอบภาพหมุน มีหลายครั้งที่การพิมพ์ผิดเพียงเล็กน้อยหรือเปลี่ยน id ในที่เดียวแต่ไม่ใช่อย่างอื่นจะทำให้ภาพหมุนแตก ที่จะไม่เกิดขึ้นที่นี่เพราะองค์ประกอบทั้งหมดอ้างอิง id เดียวกันโดยอัตโนมัติ

 def initialize(view, images) # ... @uid = SecureRandom.hex(6) end attr_accessor :uid

ถัดไปเป็นสไลด์รูปภาพ:

 def slides items = images.map.with_index { |image, index| slide_tag(image, index.zero?) } content_tag(:div, safe_join(items), class: 'carousel-inner') end def slide_tag(image, is_active) options = { class: (is_active ? 'item active' : 'item'), } content_tag(:div, image_tag(image), options) end

เราเพียงแค่วนซ้ำรูปภาพแต่ละภาพที่เราส่งไปยังอินสแตนซ์ Carousel และสร้างมาร์กอัปที่เหมาะสม: แท็กรูปภาพที่ห่อใน div ด้วยคลาส CSS ของ item ตรวจสอบให้แน่ใจว่าได้เพิ่มคลาสที่ active ไปยังอันแรกที่เราสร้างขึ้น

สุดท้ายนี้ เราต้องการตัวควบคุมก่อนหน้า/ถัดไป:

 def controls safe_join([control_tag('left'), control_tag('right')]) end def control_tag(direction) options = { class: "#{direction} carousel-control", data: { slide: direction == 'left' ? 'prev' : 'next' } } icon = content_tag(:i, nil, class: "glyphicon glyphicon-chevron-#{direction}") control = link_to(icon, "##{uid}", options) end

เราสร้างลิงก์ที่ควบคุมการเคลื่อนไหวของภาพหมุนไปมาระหว่างภาพ สังเกตการใช้ uid อีกครั้ง ไม่ต้องกังวลว่าจะไม่ได้ใช้ ID ที่ถูกต้องในทุกที่ภายในโครงสร้างภาพหมุน ซึ่งจะสอดคล้องกันโดยอัตโนมัติและไม่ซ้ำกัน

ผลิตภัณฑ์สำเร็จรูป:

ด้วยเหตุนี้ ตัวช่วยหมุนของเราจึงสมบูรณ์ นี่คือทั้งหมด:

 # app/helpers/carousel_helper.rb module CarouselHelper def carousel_for(images) Carousel.new(self, images).html end class Carousel def initialize(view, images) @view, @images = view, images @uid = SecureRandom.hex(6) end def html content = safe_join([indicators, slides, controls]) content_tag(:div, content, id: uid, class: 'carousel slide') end private attr_accessor :view, :images, :uid delegate :link_to, :content_tag, :image_tag, :safe_join, to: :view def indicators items = images.count.times.map { |index| indicator_tag(index) } content_tag(:ol, safe_join(items), class: 'carousel-indicators') end def indicator_tag(index) options = { class: (index.zero? ? 'active' : ''), data: { target: uid, slide_to: index } } content_tag(:li, '', options) end def slides items = images.map.with_index { |image, index| slide_tag(image, index.zero?) } content_tag(:div, safe_join(items), class: 'carousel-inner') end def slide_tag(image, is_active) options = { class: (is_active ? 'item active' : 'item'), } content_tag(:div, image_tag(image), options) end def controls safe_join([control_tag('left'), control_tag('right')]) end def control_tag(direction) options = { class: "#{direction} carousel-control", data: { slide: direction == 'left' ? 'prev' : 'next' } } icon = content_tag(:i, '', class: "glyphicon glyphicon-chevron-#{direction}") control = link_to(icon, "##{uid}", options) end end end

ผู้ช่วยในการดำเนินการ:

สุดท้ายนี้ ในการขับเคลื่อนประเด็นนี้ เรามาดูตัวอย่างสั้นๆ กันว่าผู้ช่วยคนนี้จะทำให้ชีวิตเราง่ายขึ้นได้อย่างไร สมมติว่าเรากำลังสร้างเว็บไซต์สำหรับรายการเช่าอพาร์ตเมนต์ แต่ละอ็อบเจ็กต์ Apartment มีรายการ URL ของรูปภาพ:

 class Apartment def image_urls # ... end end

ด้วยตัวช่วยหมุนของเรา เราสามารถแสดง Bootstrap carousel ทั้งหมดด้วยการเรียก carousel_for เพียงครั้งเดียว ลบตรรกะที่ค่อนข้างซับซ้อนออกจากมุมมอง:

 <% apartment = Apartment.new %> # ... <%= carousel_for(apartment.image_urls) %> 

ไม่แน่ใจว่าจะใช้ตัวช่วยดู Rails เมื่อใด นี่คือการสาธิต

ทวีต

บทสรุป

ด้วยการใช้เทคนิคที่เรียบง่าย แต่ทรงพลังนี้ เราได้ย้ายสิ่งที่จะเป็นมาร์กอัปและตรรกะจำนวนมากออกจากเลเยอร์การดู และไปเป็นฟังก์ชันตัวช่วยที่สามารถใช้เพื่อแสดงองค์ประกอบภาพหมุนได้ทุกที่ด้วยการเรียก carousel_for(some_images) . ตัวช่วยทั่วไปนี้สามารถใช้ได้กับโปรเจ็กต์ Rails ทั้งหมดของคุณทุกครั้งที่คุณใช้ Twitter Bootstrap ที่สำคัญที่สุด ตอนนี้คุณมีเครื่องมือใหม่ในชุดเครื่องมือของคุณแล้ว ซึ่งคุณสามารถใช้สำหรับส่วนประกอบเฉพาะของโปรเจ็กต์ได้เช่นกัน

ดังนั้น ครั้งต่อไปที่คุณพบว่าตัวเองพิมพ์และพิมพ์มาร์กอัปประเภทเดียวกันอีกครั้งและฝังตรรกะแบบมีเงื่อนไขลงในมุมมองของคุณ ให้ดูว่าฟังก์ชันตัวช่วยกำลังรอให้เขียนอยู่หรือไม่เพื่อทำให้ชีวิตของคุณง่ายขึ้น