Karn Tirasoontorn

July 20, 2021

เพิ่ม Loading Spinner ให้กับปุ่ม Submit

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

Screen Shot 2021-07-20 at 14.55.59.png


จากข้างต้นที่นี้เราลองมาใช้ 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