ปัญหาที่เกิดจาก 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 อย่างเดียว
});
❌ ผลลัพธ์แบบเก่า ( Shallow Merge )
Config ที่ได้จะเป็น
persistence: { sort: false } // filter และ page หายไป! เพราะโดนทับทั้งก้อน
✅ ผลลัพธ์แบบใหม่ ( Deep Merge )
Config ที่ได้จะฉลาดขึ้น
persistence: {
filter: true, // ยังอยู่ (จาก Default)
page: true, // ยังอยู่ (จาก Default)
sort: false // ถูกเปลี่ยนเฉพาะตัวนี้ (จาก User)
}
🚀 ประโยชน์เพิ่มเติม
- ความปลอดภัย: คุณสามารถกำหนด Default Config ที่ซับซ้อน ( Nested Objects ) ได้โดยไม่ต้องกลัวว่าการแก้ไขเพียงจุดเดียวจากภายนอกจะทำให้ระบบรวน
- ความยืดหยุ่น: ช่วยให้ Dev คนอื่นในทีมส่ง Config เฉพาะจุดที่ต้องการแก้ไขจริง ๆ เข้ามาได้ง่ายขึ้น
- No Dependencies: ฟังก์ชัน
deepMergeเป็น Pure JavaScript ไม่ต้องติดตั้ง Library ภายนอกอย่าง Lodash เพิ่มเติม ทำให้ไฟล์มีขนาดเล็กและรันได้ทุกที่