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