Now.js Framework Documentation

Now.js Framework Documentation

AppConfigManager

TH 04 Sep 2026 01:40

AppConfigManager

ภาพรวม

AppConfigManager คือระบบจัดการธีมและ configuration ใน Now.js Framework ดูแลเรื่องต่อไปนี้

  • สลับธีม — โหมด light/dark พร้อมตรวจค่าที่ระบบปฏิบัติการตั้งไว้
  • จัดการ CSS Variables — โหลด CSS custom properties จาก backend มาใช้
  • ผูกข้อมูลกับ Template — ส่ง payload ทั้งก้อนจาก API ให้ TemplateManager (แบบเดียวกับ ApiComponent)
  • ความปลอดภัย — ล้างค่า CSS ก่อนใช้ เพื่อกัน XSS
  • เสริมปุ่มอัตโนมัติ — ปุ่มที่มี data-component="config" กลายเป็นปุ่มสลับธีมเอง
  • เปลี่ยนธีมแบบลื่น — กัน FOUC (Flash of Unstyled Content) ด้วยการ fade

ใช้เมื่อ:

  • ต้องการจัดการธีมจากที่เดียวทั้งแอป
  • ต้องการโหลด CSS variables จาก backend แบบ dynamic
  • ต้องการผูก configuration เข้ากับ template โดยไม่ต้องเขียน JS
  • ต้องการให้การเปลี่ยนธีมลื่นไม่กะพริบ

สิ่งที่ต้องมี

  • Now.js Framework core
  • เบราว์เซอร์ที่รองรับ ES6+
  • ไม่บังคับ: EventManager (สำหรับส่ง event)
  • ไม่บังคับ: TemplateManager (สำหรับผูกข้อมูล)
  • ไม่บังคับ: HttpClient (สำหรับเรียก API)

การใช้งานพื้นฐาน

เริ่มต้นใช้งาน

// Initialize with Now framework
await Now.init({
  config: {
    enabled: true,
    defaultTheme: 'light',
    storageKey: 'app_theme',
    systemPreference: true,
    api: {
      enabled: true,
      configUrl: '/api/index/config/frontend-settings',
      cacheResponse: true
    }
  }
});

ลำดับที่เกิดขึ้น:

  1. AppConfigManager อ่านธีมที่เก็บไว้จาก localStorage
  2. ถ้ายังไม่มีค่าที่เก็บไว้ ใช้ค่าที่ระบบปฏิบัติการตั้งไว้แทน
  3. ใส่ธีมให้ document.documentElement
  4. โหลด configuration จาก API (ถ้าเปิดไว้)
  5. ใส่ CSS variables
  6. ประมวลผลการผูกข้อมูลกับ template
  7. ทำเครื่องหมายว่าหน้าพร้อมแล้ว (เอาสถานะ loading ออก)

เริ่มต้นเอง

// If not using Now framework
await AppConfigManager.init({
  enabled: true,
  defaultTheme: 'dark',
  api: {
    enabled: true,
    configUrl: '/api/config'
  }
});

รูปแบบ Response ของ API

ฝั่ง backend ควรคืน JSON ตามโครงสร้างนี้

{
  "variables": {
    "--color-primary": "#29336b",
    "--color-secondary": "#6366f1",
    "--logo-url": "/images/logo.png",
    "--hero-bg": "/images/hero.jpg"
  },
  "web_title": "My Website",
  "web_description": "Website description",
  "company": {
    "name": "Company Name",
    "phone": "+1-234-567-8900",
    "email": "info@example.com",
    "address": "123 Main St"
  },
  "user": {
    "id": 1,
    "name": "John Doe",
    "email": "john@example.com"
  }
}

คำอธิบายฟิลด์:

  • variables (object, ไม่บังคับ) — CSS custom properties ที่จะนำไปใช้
  • คีย์อื่นทั้งหมด — ใช้ได้ใน context ของ template สำหรับผูกข้อมูล

หมายเหตุ: โครงสร้าง response ยืดหยุ่นได้ AppConfigManager จะส่งข้อมูลทั้งหมดต่อให้ TemplateManager ทำให้ template เข้าถึงคีย์ใดก็ได้ที่ backend ส่งมา

ความสามารถหลัก

1. จัดการธีม

สลับธีม

// Toggle between light and dark
AppConfigManager.toggle();

// Set specific theme
await AppConfigManager.setTheme('dark');

// Get current theme
const theme = AppConfigManager.getCurrentTheme();
console.log(theme); // 'light' or 'dark'

การจดจำธีม

ธีมถูกบันทึกลง localStorage อัตโนมัติ

// Stored with key from config.storageKey (default: 'app_theme')
// Retrieved automatically on next page load

ตรวจค่าที่ระบบตั้งไว้

// Enable in config
await Now.init({
  config: {
    systemPreference: true  // Detect prefers-color-scheme
  }
});

// AppConfigManager will:
// 1. Check localStorage first
// 2. If no stored theme, use system preference
// 3. Listen for system preference changes

2. CSS Variables

ใส่ค่าตัวแปร

// Apply single variable
AppConfigManager.applyVariables({
  '--color-primary': '#6366f1'
});

// Apply multiple variables
AppConfigManager.applyVariables({
  '--color-primary': '#6366f1',
  '--color-secondary': '#8b5cf6',
  '--font-size-base': '16px',
  '--spacing-unit': '8px'
});

// Apply image URLs (auto-wrapped with url())
AppConfigManager.applyVariables({
  '--logo-url': '/images/logo.png',
  '--hero-bg': './images/hero.jpg'
});

ผลลัพธ์:

:root {
  --color-primary: #6366f1;
  --color-secondary: #8b5cf6;
  --logo-url: url('/images/logo.png');
  --hero-bg: url('./images/hero.jpg');
}

ล้างค่าตัวแปร

// Remove all applied variables
AppConfigManager.clearVariables();

อ่านค่าตัวแปรที่ใส่ไว้

// Get object of currently applied variables
const vars = AppConfigManager.getAppliedVariables();
console.log(vars);
// { '--color-primary': '#6366f1', ... }

3. ผูกข้อมูลกับ Template

AppConfigManager ส่ง response ทั้งก้อนจาก API ต่อให้ TemplateManager ทำให้ template เข้าถึงข้อมูลจาก backend ได้

<!-- Header with dynamic data -->
<header>
  <h1 data-text="web_title"></h1>
  <p data-text="web_description"></p>
</header>

<!-- Footer with company info -->
<footer>
  <div data-text="company.name"></div>
  <a data-attr="href:'tel:' + company.phone" data-text="company.phone"></a>
  <a data-attr="href:'mailto:' + company.email" data-text="company.email"></a>
</footer>

<!-- Conditional rendering based on user -->
<nav>
  <li data-if="user === null">
    <a href="/login">Login</a>
  </li>
  <li data-if="user !== null">
    <a href="/profile" data-text="user.name"></a>
  </li>
</nav>

ทำงานอย่างไร:

  1. AppConfigManager โหลด config จาก API
  2. สร้าง context เป็น { state: config, data: config, computed: {} }
  3. เรียก TemplateManager.processDataDirectives(container, context)
  4. Template เข้าถึงข้อมูลด้วยจุด เช่น company.name

4. เสริมปุ่มสลับธีมอัตโนมัติ

HTML

<!-- Automatically enhanced -->
<button data-component="config">🌓 Toggle Theme</button>

<!-- Multiple toggles sync automatically -->
<button data-component="config" class="nav-toggle">Theme</button>
<button data-component="config" class="footer-toggle">🌙</button>

ลำดับที่เกิดขึ้น:

  1. ComponentManager เรียก AppConfigManager.enhance(element)
  2. ผูก click handler ให้สลับธีม
  3. เพิ่ม attribute data-theme-state เช่น data-theme-state="light"
  4. ปุ่มสลับธีมทุกตัวอัปเดตพร้อมกันเมื่อธีมเปลี่ยน

CSS สำหรับปุ่มสลับธีม

/* Style based on current theme */
[data-component="config"][data-theme-state="light"]::before {
  content: "🌙"; /* Show moon in light mode */
}

[data-component="config"][data-theme-state="dark"]::before {
  content: "☀️"; /* Show sun in dark mode */
}

/* With icon fonts */
[data-component="config"]::before {
  font-family: 'icomoon' !important;
}

[data-component="config"][data-theme-state="light"]::before {
  content: "\e929"; /* icon-moon */
}

[data-component="config"][data-theme-state="dark"]::before {
  content: "\e9d4"; /* icon-sun */
}

5. เปลี่ยนธีมแบบลื่น (กัน FOUC)

CSS ที่ต้องมี

body {
  opacity: 0;
  transition: opacity 0.3s ease;
}

body.theme-ready {
  opacity: 1;
}

body.theme-transitioning {
  opacity: 0;
}

body.theme-loading {
  opacity: 0;
}

ทำงานอย่างไร:

  1. หน้าเว็บโหลดมาพร้อม body { opacity: 0 }
  2. AppConfigManager เริ่มทำงานและโหลดธีม
  3. เพิ่มคลาส theme-ready แล้วหน้าค่อย ๆ ปรากฏ
  4. เมื่อสลับธีม
    • เพิ่มคลาส theme-transitioning แล้วจางหาย
    • ใส่ธีมใหม่
    • เพิ่มคลาส theme-ready แล้วจางกลับมา

ตั้งค่าการเปลี่ยนผ่าน

await Now.init({
  config: {
    transition: {
      enabled: true,
      duration: 300,              // Transition duration (ms)
      hideOnSwitch: true,         // Fade out during switch
      loadingClass: 'theme-loading',
      readyClass: 'theme-ready',
      transitionClass: 'theme-transitioning'
    }
  }
});

ตัวอย่างขั้นสูง

ตัวอย่างที่ 1: เชื่อมต่อ API ของตัวเอง

// Custom endpoint with headers
await AppConfigManager.init({
  enabled: true,
  api: {
    enabled: true,
    configUrl: '/api/v2/config',
    headers: {
      'X-API-Key': 'your-api-key',
      'Accept': 'application/json'
    },
    timeout: 10000,
    cacheResponse: true
  }
});

// Manual load with custom URL
try {
  const config = await AppConfigManager.loadFromAPI('/api/custom-config');
  console.log('Loaded:', config);
} catch (error) {
  console.error('Failed to load config:', error);
}

// Refresh (clears cache)
const freshConfig = await AppConfigManager.refreshFromAPI();

ตัวอย่างที่ 2: ควบคุมธีมจากโค้ด

// Listen for theme changes
EventManager.on('theme:changed', ({ theme }) => {
  console.log('Theme changed to:', theme);

  // Update analytics
  gtag('event', 'theme_change', { theme });

  // Update external service
  updateThemePreference(theme);
});

// Set theme based on user action
async function applyUserTheme(userPreference) {
  if (userPreference === 'system') {
    // Use system preference
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    await AppConfigManager.setTheme(prefersDark ? 'dark' : 'light');
  } else {
    await AppConfigManager.setTheme(userPreference);
  }
}

// Toggle with custom transition
await AppConfigManager.setTheme('dark', {
  transition: true  // Force transition even if disabled
});

// Set without transition
await AppConfigManager.setTheme('light', {
  transition: false  // Skip transition
});

ตัวอย่างที่ 3: อัปเดตตัวแปรตามธีม

// Update variables based on theme
EventManager.on('theme:changed', ({ theme }) => {
  if (theme === 'dark') {
    AppConfigManager.applyVariables({
      '--logo-url': '/images/logo-dark.png',
      '--hero-bg': '/images/hero-dark.jpg'
    });
  } else {
    AppConfigManager.applyVariables({
      '--logo-url': '/images/logo-light.png',
      '--hero-bg': '/images/hero-light.jpg'
    });
  }
});

// Load theme-specific stylesheet
EventManager.on('theme:changed', ({ theme }) => {
  const linkId = 'theme-specific-styles';
  let link = document.getElementById(linkId);

  if (!link) {
    link = document.createElement('link');
    link.id = linkId;
    link.rel = 'stylesheet';
    document.head.appendChild(link);
  }

  link.href = `/css/themes/${theme}.css`;
});

ตัวอย่างที่ 4: Configuration แบบหลายผู้เช่า

// Load tenant-specific config
const tenantId = getCurrentTenantId();

await AppConfigManager.init({
  enabled: true,
  api: {
    enabled: true,
    configUrl: `/api/tenants/${tenantId}/config`,
    cacheResponse: false  // Don't cache (tenant-specific)
  }
});

// Each tenant gets:
// - Custom CSS variables (colors, logos, fonts)
// - Custom company info
// - Custom theme preferences

API Reference

ตัวเลือกการตั้งค่า

{
  enabled: boolean,           // Enable AppConfigManager (default: false)
  defaultTheme: string,       // Default theme: 'light' or 'dark' (default: 'light')
  storageKey: string,         // localStorage key (default: 'app_theme')
  systemPreference: boolean,  // Use system color scheme (default: false)

  api: {
    enabled: boolean,         // Load config from API (default: false)
    configUrl: string|null,   // API endpoint URL (default: null)
    headers: object,          // Custom request headers (default: {})
    timeout: number,          // Request timeout in ms (default: 5000)
    cacheResponse: boolean    // Cache API responses (default: true)
  },

  transition: {
    enabled: boolean,         // Enable transitions (default: true)
    duration: number,         // Transition duration in ms (default: 300)
    hideOnSwitch: boolean,    // Fade out during switch (default: true)
    loadingClass: string,     // Loading state class (default: 'theme-loading')
    readyClass: string,       // Ready state class (default: 'theme-ready')
    transitionClass: string   // Transitioning class (default: 'theme-transitioning')
  }
}

เมธอด

init(options)

เริ่มต้นใช้งาน AppConfigManager

พารามิเตอร์:

  • options (object) — ตัวเลือกการตั้งค่า

คืนค่า: Promise

ตัวอย่าง:

await AppConfigManager.init({
  enabled: true,
  defaultTheme: 'dark'
});

toggle()

สลับระหว่างธีม light กับ dark

คืนค่า: void

ตัวอย่าง:

AppConfigManager.toggle();

setTheme(theme, options)

ตั้งธีมที่ต้องการ

พารามิเตอร์:

  • theme (string) — ชื่อธีม: 'light' หรือ 'dark'
  • options (object, ไม่บังคับ) — ตัวเลือก
    • transition (boolean) — ให้มี animation หรือไม่ (ค่าเริ่มต้น: true)

คืนค่า: Promise

ตัวอย่าง:

await AppConfigManager.setTheme('dark');
await AppConfigManager.setTheme('light', { transition: false });

getCurrentTheme()

อ่านธีมที่ใช้อยู่

คืนค่า: string — ชื่อธีมปัจจุบัน

ตัวอย่าง:

const theme = AppConfigManager.getCurrentTheme();
console.log(theme); // 'light' or 'dark'

applyVariables(variables)

ใส่ CSS custom properties

พารามิเตอร์:

  • variables (object) — คู่ชื่อและค่าของ CSS variable

คืนค่า: object — ตัวแปรที่ใส่จริงหลังผ่านการล้างค่า

ตัวอย่าง:

const applied = AppConfigManager.applyVariables({
  '--color-primary': '#6366f1',
  '--logo-url': '/images/logo.png'
});

ความปลอดภัย: รับเฉพาะ CSS custom properties (--*) เท่านั้น รูปแบบที่อันตรายถูกตัดออก ส่วน URL ของรูปถูกตรวจแล้วครอบด้วย url()

clearVariables()

ลบ CSS variables ที่ใส่ไว้ทั้งหมด

คืนค่า: void

ตัวอย่าง:

AppConfigManager.clearVariables();

getAppliedVariables()

อ่าน CSS variables ที่ใส่ไว้อยู่

คืนค่า: object — อ็อบเจ็กต์ของตัวแปรที่ใส่ไว้

ตัวอย่าง:

const vars = AppConfigManager.getAppliedVariables();

loadFromAPI(url)

โหลด configuration จาก backend API

พารามิเตอร์:

  • url (string, ไม่บังคับ) — URL ของ API (ถ้าไม่ระบุจะใช้ config.api.configUrl)

คืนค่า: Promise — อ็อบเจ็กต์ configuration จาก API

โยนข้อผิดพลาด: เมื่อเรียกไม่สำเร็จหรือ response ไม่ถูกต้อง

ตัวอย่าง:

try {
  const config = await AppConfigManager.loadFromAPI();
  console.log('Config loaded:', config);
} catch (error) {
  console.error('Load failed:', error);
}

refreshFromAPI()

โหลด configuration จาก API ใหม่ (ล้าง cache ก่อน)

คืนค่า: Promise — configuration ชุดใหม่

ตัวอย่าง:

const config = await AppConfigManager.refreshFromAPI();

enhance(element)

เสริมความสามารถให้ปุ่มสลับธีม (ComponentManager เป็นผู้เรียก)

พารามิเตอร์:

  • element (HTMLElement) — อีลิเมนต์ที่จะเสริม

คืนค่า: HTMLElement — อีลิเมนต์ที่เสริมแล้ว

ตัวอย่าง:

const button = document.querySelector('.theme-toggle');
AppConfigManager.enhance(button);

destroy()

ทำลาย AppConfigManager และคืนทรัพยากรทั้งหมด

คืนค่า: void

ตัวอย่าง:

// On app unmount
AppConfigManager.destroy();

reset()

คืน AppConfigManager สู่สถานะเริ่มต้นโดยไม่ทำลายทิ้ง

คืนค่า: void

ตัวอย่าง:

AppConfigManager.reset();
await AppConfigManager.init(newConfig);

เหตุการณ์

theme:initialized

ส่งเมื่อ AppConfigManager เริ่มต้นเสร็จ

Payload:

{
  theme: string  // Current theme
}

ตัวอย่าง:

EventManager.on('theme:initialized', ({ theme }) => {
  console.log('Initialized with theme:', theme);
});

theme:changed

ส่งเมื่อธีมเปลี่ยน

Payload:

{
  theme: string  // New theme
}

ตัวอย่าง:

EventManager.on('theme:changed', ({ theme }) => {
  console.log('Theme changed to:', theme);
  // Update UI, analytics, etc.
});

theme:ready

ส่งเมื่อธีมพร้อมแสดงผล (กัน FOUC)

Payload: ไม่มี

ตัวอย่าง:

EventManager.on('theme:ready', () => {
  console.log('Theme ready - page visible');
});

theme:variables-applied

ส่งเมื่อใส่ CSS variables เรียบร้อย

Payload:

{
  variables: object  // Applied variables
}

ตัวอย่าง:

EventManager.on('theme:variables-applied', ({ variables }) => {
  console.log('Variables applied:', variables);
});

theme:variables-cleared

ส่งเมื่อล้าง CSS variables

Payload: ไม่มี

ตัวอย่าง:

EventManager.on('theme:variables-cleared', () => {
  console.log('Variables cleared');
});

theme:api-loaded

ส่งเมื่อโหลด configuration จาก API สำเร็จ

Payload:

{
  config: object,      // Configuration data
  fromCache: boolean   // Whether loaded from cache
}

ตัวอย่าง:

EventManager.on('theme:api-loaded', ({ config, fromCache }) => {
  console.log('Config loaded:', config);
  console.log('From cache:', fromCache);
});

theme:api-error

ส่งเมื่อเรียก API ไม่สำเร็จ

Payload:

{
  error: string,  // Error message
  url: string     // API URL
}

ตัวอย่าง:

EventManager.on('theme:api-error', ({ error, url }) => {
  console.error('API error:', error);
  console.error('URL:', url);
});

theme:destroyed

ส่งเมื่อ AppConfigManager ถูกทำลาย

Payload: ไม่มี

ตัวอย่าง:

EventManager.on('theme:destroyed', () => {
  console.log('AppConfigManager destroyed');
});

theme:reset

ส่งเมื่อ AppConfigManager ถูก reset

Payload: ไม่มี

ตัวอย่าง:

EventManager.on('theme:reset', () => {
  console.log('AppConfigManager reset');
});

ความปลอดภัย

การตรวจสอบ CSS Variable

AppConfigManager ตรวจค่าอย่างเข้มงวดเพื่อกัน XSS

ตรวจชื่อ property

// ✅ Allowed - CSS custom properties only
AppConfigManager.applyVariables({
  '--color-primary': '#6366f1',
  '--font-size': '16px'
});

// ❌ Rejected - Not a CSS custom property
AppConfigManager.applyVariables({
  'color': 'red',          // Rejected
  'background': 'blue'     // Rejected
});

ล้างค่าที่อันตราย

รูปแบบที่อันตรายถูกตัดออกอัตโนมัติ

// ❌ Dangerous patterns (automatically removed)
'javascript:alert(1)'      // javascript: protocol
'expression(alert(1))'     // IE expression()
'<script>alert(1)</script>' // script tags
'url(javascript:alert(1))' // javascript in url()

ตรวจสอบ URL

URL ของรูปถูกตรวจและล้างก่อนใช้

// ✅ Allowed URLs
'/images/logo.png'              // Local absolute path
'./images/hero.jpg'             // Local relative path
'https://example.com/image.png' // Same-origin absolute (if allowed)

// ❌ Rejected URLs
'../../../etc/passwd'           // Path traversal
'http://evil.com/xss.jpg'       // External domain (default policy)
'/images/file.svg'              // SVG not allowed (default)
'/images/file.php'              // Non-image extension

ตั้งค่าความปลอดภัย

AppConfigManager.security = {
  allowedPropertyPattern: /^--[\w-]+$/,  // CSS custom properties only
  maxValueLength: 500,                    // Prevent DoS

  dangerousPatterns: [
    // Patterns to remove from values
  ],

  url: {
    enabled: true,
    allowedExtensions: ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.avif', '.ico'],
    blockPathTraversal: true,
    stripQueryString: true,
    allowedOrigins: [window.location.origin]
  }
};

ข้อผิดพลาดที่พบบ่อย

❌ ข้อที่ 1: ไม่ได้เปิดใช้ AppConfigManager

// ❌ Wrong - AppConfigManager not enabled
AppConfigManager.toggle(); // Does nothing

// ✅ Correct - Enable first
await Now.init({
  config: { enabled: true }
});
AppConfigManager.toggle();

❌ ข้อที่ 2: ลืม await เมธอดที่เป็น async

// ❌ Wrong - Not awaiting
AppConfigManager.setTheme('dark');
console.log(AppConfigManager.getCurrentTheme()); // May still be 'light'

// ✅ Correct - Await theme change
await AppConfigManager.setTheme('dark');
console.log(AppConfigManager.getCurrentTheme()); // 'dark'

❌ ข้อที่ 3: ใช้ property ที่ไม่ใช่ CSS custom property

// ❌ Wrong - Regular CSS properties rejected
AppConfigManager.applyVariables({
  'color': 'red',
  'font-size': '16px'
});

// ✅ Correct - Use CSS custom properties
AppConfigManager.applyVariables({
  '--color-primary': 'red',
  '--font-size-base': '16px'
});

❌ ข้อที่ 4: ไม่ได้ตั้ง CSS สำหรับการเปลี่ยนผ่าน

// ❌ Wrong - No CSS for anti-FOUC
// Page will flash unstyled content

// ✅ Correct - Add required CSS
body {
  opacity: 0;
  transition: opacity 0.3s ease;
}
body.theme-ready {
  opacity: 1;
}

❌ ข้อที่ 5: คิดว่าตัวแปรมีผลทันที

// ❌ Wrong - Variables not applied yet
AppConfigManager.applyVariables({ '--color': 'red' });
const computed = getComputedStyle(document.documentElement)
  .getPropertyValue('--color'); // May be empty

// ✅ Correct - Variables applied synchronously to DOM
AppConfigManager.applyVariables({ '--color': 'red' });
// But CSS paint may be deferred by browser
requestAnimationFrame(() => {
  const computed = getComputedStyle(document.documentElement)
    .getPropertyValue('--color'); // 'red'
});

แนวปฏิบัติที่ดี

✅ ควรทำ

  1. เปิดใช้ AppConfigManager ก่อนเรียกใช้

    await Now.init({ config: { enabled: true } });
  2. ใช้ CSS custom properties ในการทำธีม

    .button {
     background: var(--color-primary);
     color: var(--color-text);
    }
  3. ตั้ง CSS กัน FOUC

    body {
     opacity: 0;
     transition: opacity 0.3s ease;
    }
    body.theme-ready {
     opacity: 1;
    }
  4. รับมือ error จาก API ให้เรียบร้อย

    EventManager.on('theme:api-error', ({ error }) => {
     console.error('Config load failed:', error);
     // Fallback to defaults
    });
  5. คืนทรัพยากรเมื่อเลิกใช้แอป

    window.addEventListener('unload', () => {
     AppConfigManager.destroy();
    });

❌ ไม่ควรทำ

  1. อย่าสลับธีมถี่เกินไป

    // ❌ Bad - Causes flickering
    setInterval(() => AppConfigManager.toggle(), 100);
  2. อย่าแก้ state ตรง ๆ

    // ❌ Bad
    AppConfigManager.state.current = 'dark';
    
    // ✅ Good
    await AppConfigManager.setTheme('dark');
  3. อย่าใส่ตัวแปรทีละตัวจำนวนมาก

    // ❌ Bad - Performance impact
    for (let i = 0; i < 1000; i++) {
     AppConfigManager.applyVariables({ [`--var-${i}`]: 'value' });
    }
    
    // ✅ Good - Batch apply
    const vars = {};
    for (let i = 0; i < 100; i++) {
     vars[`--var-${i}`] = 'value';
    }
    AppConfigManager.applyVariables(vars);
  4. อย่าข้ามการตรวจสอบความปลอดภัย

    // ❌ Bad - Trying to bypass validation
    document.documentElement.style.setProperty('color', 'red');
    
    // ✅ Good - Use AppConfigManager API
    AppConfigManager.applyVariables({ '--color': 'red' });

แก้ปัญหา

ธีมไม่ถูกจดจำ

อาการ: ธีมกลับเป็นค่าเดิมทุกครั้งที่โหลดหน้าใหม่

วิธีแก้:

// Check storageKey is set
await Now.init({
  config: {
    storageKey: 'app_theme'  // Must be set
  }
});

// Check localStorage is available
try {
  localStorage.setItem('test', '1');
  localStorage.removeItem('test');
} catch (e) {
  console.error('localStorage blocked:', e);
}

API ไม่โหลด

อาการ: configuration ไม่ถูกโหลดจาก API

วิธีแก้:

// Check API is enabled
await Now.init({
  config: {
    api: {
      enabled: true,  // Must be true
      configUrl: '/api/config'  // Must be set
    }
  }
});

// Check URL security
// Only HTTPS same-origin or HTTP same-origin allowed by default
// External domains are blocked

// Listen for errors
EventManager.on('theme:api-error', ({ error, url }) => {
  console.error('API error:', error, url);
});

ตัวแปรไม่มีผล

อาการ: CSS variables ไม่ปรากฏใน style

วิธีแก้:

// 1. Check variables are valid CSS custom properties
AppConfigManager.applyVariables({
  '--color': 'red'  // ✅ Good
  // 'color': 'red'  // ❌ Bad
});

// 2. Check variables in DevTools
console.log(AppConfigManager.getAppliedVariables());

// 3. Check computed styles
const computed = getComputedStyle(document.documentElement);
console.log(computed.getPropertyValue('--color'));

// 4. Verify CSS is using variables
// body { color: var(--color); }

หน้ากะพริบตอนเปลี่ยนธีม

อาการ: หน้าเว็บกะพริบระหว่างเปลี่ยนธีม

วิธีแก้:

/* Ensure CSS is set up correctly */
body {
  opacity: 0;
  transition: opacity 0.3s ease;
}

body.theme-ready {
  opacity: 1;
}

body.theme-transitioning {
  opacity: 0;
}
// Adjust transition duration if needed
await Now.init({
  config: {
    transition: {
      duration: 500  // Slower transition
    }
  }
});

เอกสารที่เกี่ยวข้อง