ใน Rails จะมีความสามารถที่จะป้องกันไม่ให้เรากด submit ข้อมูลซ้ำไปยัง Server ได้โดยใช้ data-attribute ที่ชื่อว่า `data-disable-with` ดังแสดงในตัวอย่างโค้ดด้านล่าง หรือจะเข้าไปดูรายละเอียดได้ที่ automatic-disabling
ตรงจุดนี้เองเราสามารถเปลี่ยนการแสดงข้อความให้เป็น Loading Spinner แทนได้โดยการใส่ tag ที่เป็นไอคอนหรือภาพเข้าไปแทนได้
# login.html.erb <%= form ... %> <button type="submit" class="..." data-disable-with="<%= spinner_tag %>"> Sign in </button> <% end %>
# application_helper.rb def spinner_tag <<~SVG <svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> </svg> SVG end
ผลลัพธ์ที่ได้ก็จะแสดดังรูปด้านล่าง
สำหรับวิธีนี้ถ้าเราเปิด inspector เข้าไปดูปุ่มของเรา จะพบว่าอาจจะดูรกไปนิดหนึ่ง เพราะ svg ที่แสดง spinner จะเป็น data-attribute ตัวหนึ่งใน button
จากข้างต้นที่นี้เราลองมาใช้ Stimulus ในการแสดง Loading Spinner และ disable ปุ่มเพื่อป้องกันการส่งข้อมูลซ้ำดูโดยเริ่มต้นจากการสร้าง controller ขึ้นมาก่อน
# spinner_controller.rb import { Controller } from "stimulus" export default class extends Controller { initialize() { this.submitButton = this.element.querySelector("[type='submit']") document.addEventListener('submit', event => { this.submitButton.setAttribute('disabled', '') this.submitButton.insertAdjacentHTML('afterbegin', this.spinner) }) } get spinner() { return `<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> </svg>` } }
จากนั้นก็กำหนด controller ตัวนี้ให้กับ form ที่จะใช้
# session.html.erb <%= form_for(resource, as: resource_name, url: session_path(resource_name), data: { controller: "spinner" }, html: { class: "mt-8 space-y-6" }) do |f| %> ... <button type="submit" class="..."> Sign in </button> <% end %>
และเมื่อรันโปรแกรมดู ก็จะได้ผลลัพธ์เช่นเดียวกันกับการใช้ data-disable-with และเราสามารถนำไป reuse ใช้กับฟอร์มอื่นในโปรแกรมได้ทันทีเพียงแค่เพิ่ม controller ตัวนี้เข้าไปในฟอร์มที่จะให้แสดง spinner