สร้าง Custom Helper สำหรับใช้ใน Rails Views และ Controller

ท้าวความถึงตอนที่เราเขียน view

เวลาที่ผมจะเขียน view ผมจะเขียนเป็นไฟล์ .html.erb เพราะถนัดมือและคุ้นกับการมอง HTML tag มากกว่า แต่เวลาที่จะต้องเอาข้อมูลมาแสดง หรือรันโค้ด ruby บน view นั้น คนที่เคยใช้ erb อาจจะไม่ค่อยชอบเพราะจะต้องเขียนโค้ด ruby ภายในแท็ก <%= ... %> หรือ ไม่ก็ <% ... %> โดยส่วนตัว ผมพอรับได้ไม่มีปัญหาในการใช้แท็กนี้ แต่ก็มีหลายครั้งเหมือนกันที่จำเป็นต้องเขียนโค้ดยาวๆ ลงในแท็กเพื่อให้มันรีเทิร์นค่าออกมาเป็นข้อความสักข้อความนึ่ง

ตัวอย่างด้านล่าง เป็น view ไฟล์ที่สมมติว่าผมต้องการโชว์รายการของส่วนประกอบ part ย่อยของ product ตัวนึ่ง โดยโชว์เฉพาะรายชื่อของ part ที่มีสีแดง (สมมติอีกเหมือนกันว่าผมไม่สามารถแก้ไขหรือไปเพิ่มเมธอดให้กับคลาส Product ไม่ได้) โค้ดในไฟล์ .html.erb ตรงที่โชว์ข้อมูลของ part ย่อยจะมีหน้าตาประมาณนี้ ซึ่งไม่ค่อยน่าดูเท่าไหร่

<p>Product name: <%= product.name %></p>
<p>Red parts: <%= product.parts.select { |p| p.color == "red" }.map(&:name).join('/') %>

ตรงนี้เป็นที่มาของการนำ custom view helper มาใช้ เพื่อให้โค้ดในไฟล์ view ของเราอ่านง่ายขึ่น โค้ด Ruby จะถูกแยกออกจาก HTML โค้ดอย่างชัดเจนขึ้นทำให้ง่ายต่อการเข้ามาแก้ไขในอนาคตด้วย

Custom View Helper คืออะไร

Custom View Helper คือโมดูลที่ประกอบด้วยเมธอดหลายๆ เมธอด ซึ่งเราสามารถเรียกใช้เมธอดพวกนี้จากภายในแท็ก <%= %> หรืแ <% %> ที่อยู่ในไฟล์ view โดยตรงได้เลย การสร้าง custom view helper ทำได้โดยการสร้างโมดูลที่มีชื่อเดียวกับชื่อของ Controller ที่เป็นของ View ที่เรากำลังทำงานอยู่แล้วตามด้วย Helper เช่น ถ้าเราเขียน view ของ Product ที่มีคอนโทลเลอร์ชื่อ ProductsController อยู่ เราจะทำการสร้าง helper ได้โดยสร้างไฟล์ app/helpers/product_helper.rb และเขียนโมดูล ProductsHelper ได้ดังตัวอย่างด้านล่าง

module ProductsHelper
  def parts_list(parts, color)
    parts.select { |p| p.color == color }.map(&:name).join('/')
  end
end

หลังจากสร้างไฟล์ product_helper.rb แล้วเราจะสามารถเรียกใช้เมธอด red_parts จากภายในไฟล์ view ได้เลย ซึ่งไฟล์ .html.erb จากตัวอย่างที่ผ่านมาจะสามารถเขียนใหม่ได้แบบนี้

<p>Product name: <%= product.name %></p>
<p>Red parts: <%= parts_list(product.parts, "red") %>

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

note: ถ้าเราสร้าง Controller ด้วยคำสั่ง `rails g controller Products` Rails จะสร้างไฟล์ที่ชื่อ products_helper.rb ไว้ในไดเรกทอรี่ app/helpers ให้เราโดยอัตโนมัติ

การตั้งชื่อโมดูลและไฟล์ของ custom helper

จริงๆ แล้วตั้งแต่ Rails เวอร์ชั่น 5 ขึ้นมา เราไม่จำเป็นต้องตั้งชื่อโมดูลของ helper ให้ล้อหรืออสอดคล้องกับชื่อของ controller ก็ได้ เนื่องจาก Rails จะโหลดโมดูล helper ทุกไฟล์ที่อยู่ในโฟลเดอร์ app/helpers/ ขึ้นมาเตรียมให้เราเรียกใช้ได้โดยอัตโนมัติ ไม่ว่าเราจะเรียก helper เมธอดจาก view ไหนก็ตาม

ยกตัวอย่างเช่น เราสามารถสร้างโมดูลชื่อ SuperProducstHelper ในไฟล์ app/helpers/super_products_helper.rb (ชื่อของโมดูลและชื่อของไฟล์ยังคงจำเป็นต้องตั้งให้สอดคล้องกันตามกฏของ Ruby นะ)ซึ่งเราจะสามารถเรียกใช้เมธอดที่ระบุในโมดูลนี้จาก view Product ได้เช่นกัน

เรียกใช่ custom helper จากภายใน Controller ก็ได้

จากที่จั่วหัวไว้ตั้งแต่ต้น เราสามารถเรียกใช้ custom helper เมธอดจากแอคชั่นภายใน controller ได้ด้วย ซึ่งถ้าเราตั้งชื่อโมดูล Helper ให้สอดคล้องหรือล้อกับชื่อของ Controller ไว้แล้ว เราจะสามารถเรียก helper เมธอดที่ต้องการได้เลย ซึ่งจะทำการเรียกใช้ผ่านทางเมธอดตัวกลางชื่อ helpers อีกที

ยกตัวอย่างเช่น helper เมธอด parts_list จากตัวอย่างที่ผ่านมานั้นถูกระบุอยู่ในโมดูล ProductsHelper ซึ่งมีชื่อล้อกับ ProductsController อยู่แล้ว ดังนั้นเราจึงสามารถเรียกใช้ parts_list ภ่ายใน controller ได้เลยแบบนี้

class ProductsController < ApplicationController
  def index
    product = Product.last
    @list = helpers.parts_list(product.parts, "red")
  end
end

ทีนี้ถ้าเผื่อว่าเรามีโมดูล Helper อีกตัวชื่อ MyHelper เราสามารถเอาเมธอดที่อยู่ใน MyHelper มาใช้ใน ProductController ได้ด้วยเหมือนกัน เพียงแต่จะต้องบอก controller นิดนึงว่าฉันจะเอาโมดูล MyHelper มาใช้นะ โดยการระบด้วยเมธอด helper ดังนี้

class ProductController < ApplicationController
  helper MyHelper

  def index
    product = Product.last
    @list = helpers.parts_list(product.parts, "red")
    helpers.my_helper_method
  end
end

แชร์โพสได้จากลิ้งด้านล่าง ขอบคุณครับ