TabulatorPlus: Deep MergeTabulatorPlus: Deep Merge

ปัญหาที่เกิดจาก Shallow Merge (...spread) คือถ้าคุณส่ง userConfig ที่มี Object ซ้อนกัน เช่น persistence: { sort: false } มันจะไปเขียนทับ defaultConfig.persistence ทั้งก้อน ทำให้ค่า filter และ page ที่เราอุตส่าห์ตั้งไว้หายไปทันที

เราจะแก้ปัญหานี้ด้วยการสร้างฟังก์ชัน deepMerge เล็ก ๆ ไว้ในไฟล์เดียวกัน เพื่อให้ TabulatorPlus แข็งแกร่งขึ้นครับ


TabulatorPlus ให้รองรับ Deep Merge

/** * TabulatorPlus.js * A wrapper class for Tabulator-tables to provide standardized configurations * and deep-merge capabilities across different frameworks. */
import { TabulatorFull as Tabulator } from 'tabulator-tables'; /** * Helper function to check if an item is a plain object. * @param {any} item - The item to check. * @returns {boolean} */
function isObject (item) { return (item && typeof item === 'object' && !Array.isArray (item)) ;
} /** * Deeply merges two objects. * This ensures nested properties (like 'persistence') aren't overwritten entirely * when only one sub-property is provided in the user configuration. * @param {Object} target - The default configuration object. * @param {Object} source - The user-provided configuration object. * @returns {Object} - The deeply merged configuration. */
function deepMerge (target, source) { let output = Object.assign ({}, target) ; if (isObject (target) && isObject (source)) { Object.keys (source) .forEach (key => { if (isObject (source[key])) { if (! (key in target)) { Object.assign (output, { [key]: source[key] }) ; } else { output[key] = deepMerge (target[key], source[key]) ; } } else { Object.assign (output, { [key]: source[key] }) ; } }) ; } return output;
} export default class TabulatorPlus extends Tabulator { /** * @param {HTMLElement|string} el - The DOM element or selector to attach the table to. * @param {Object} userConfig - Custom Tabulator options provided by the user. * @param {string|null} storageKey - Unique ID for persistence (sorting, filtering, etc.) . */ constructor (el, userConfig = {}, storageKey = null) { // 1. Define organization-wide default settings const defaultConfig = { layout: "fitColumns", movableColumns: true, pagination: "local", paginationSize: 10, paginationSizeSelector: [10, 25, 50, 100], persistence: storageKey ? { filter: true, page: true, sort: true } : false, persistenceID: storageKey, placeholder: "No Data Available", resizableRows: true, responsiveLayout: "collapse" }; // 2. Perform Deep Merge to protect nested default settings const finalConfig = deepMerge (defaultConfig, userConfig) ; // 3. Initialize the parent Tabulator class super (el, finalConfig) ; } /** * Updates the table with a new dataset. * Using this method is preferred over re-initializing the whole class. * @param {Array} newData - The new array of objects to display. * @returns {Promise} - Resolves when data is loaded. */ refreshData (newData) { if (newData && Array.isArray (newData)) { return this.setData (newData) ; } return Promise.reject ("Invalid data format: Expected an Array.") ; } /** * Forces the table to recalculate its dimensions and redraw. * Useful when the table container size changes or after opening a modal. */ forceRedraw () { this.redraw (true) ; } /** * Clears all active filters and header filters from the table. */ resetFilters () { this.clearFilter (true) ; this.clearHeaderFilter () ; }
}

💡 ทำไมแบบนี้ถึงดีกว่า?

สมมติคุณเรียกใช้งานแบบนี้

new TabulatorPlus ("#table", { persistence: { sort: false } // ต้องการปิดแค่ sort อย่างเดียว
}) ;

❌ ผลลัพธ์แบบเก่า

Config ที่ได้จะเป็น

persistence: { sort: false } // filter และ page หายไป! เพราะโดนทับทั้งก้อน

✅ ผลลัพธ์แบบใหม่

Config ที่ได้จะฉลาดขึ้น

persistence: { filter: true, // ยังอยู่ (จาก Default) page: true, // ยังอยู่ (จาก Default) sort: false // ถูกเปลี่ยนเฉพาะตัวนี้ (จาก User) }

🚀 ประโยชน์เพิ่มเติม

  1. ความปลอดภัย: คุณสามารถกำหนด Default Config ที่ซับซ้อน ได้โดยไม่ต้องกลัวว่าการแก้ไขเพียงจุดเดียวจากภายนอกจะทำให้ระบบรวน
  2. ความยืดหยุ่น: ช่วยให้ Dev คนอื่นในทีมส่ง Config เฉพาะจุดที่ต้องการแก้ไขจริง ๆ เข้ามาได้ง่ายขึ้น
  3. No Dependencies: ฟังก์ชัน deepMerge เป็น Pure JavaScript ไม่ต้องติดตั้ง Library ภายนอกอย่าง Lodash เพิ่มเติม ทำให้ไฟล์มีขนาดเล็กและรันได้ทุกที่