Now.js Framework Documentation

Now.js Framework Documentation

AppConfigManager

TH 11 Feb 2026 07:28

AppConfigManager

ภาพรวม

AppConfigManager คือระบบจัดการ theme และ site configuration ใน Now.js Framework รองรับ:

  • Theme Management: Light/Dark mode switching พร้อม system preference
  • Site Metadata: จัดการข้อมูล title, description, logo, contact, social
  • CSS Variables: โหลดและจัดการ CSS custom properties จาก backend
  • Auto-enhanced components: Toggle buttons (data-component="theme" หรือ data-component="config")
  • data-on-load: Execute script หลังโหลด config เสร็จ
  • Smooth transitions: Anti-FOUC พร้อม fade animations
  • Backend API integration: โหลด config ครั้งเดียวได้ทั้ง theme + site metadata

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

Initialization

await Now.init({
  config: {
    enabled: true,
    defaultTheme: 'light',
    storageKey: 'app_theme',
    systemPreference: true,

    // โหลด theme + site metadata จาก API
    api: {
      enabled: true,
      configUrl: '/api/config',
      cacheResponse: true
    }
  }
});

API Response Format

{
  "variables": {
    "--color-primary": "#29336b",
    "--logo-url": "/images/logo.png"
  },
  "site": {
    "title": "ร้านหนังสือสุวิมล",
    "description": "หนังสือเก่า สะสม และหายาก",
    "logo": "/images/logo.png",
    "favicon": "/images/favicon.ico",
    "lang": "th",
    "contact": {
      "email": "info@example.com",
      "phone": "02-xxx-xxxx",
      "address": "123 ถนนพระราม 4"
    },
    "social": {
      "facebook": "https://facebook.com/...",
      "line": "@example",
      "twitter": "@example"
    }
  }
}

Toggle Theme

AppConfigManager.toggle();               // สลับ light ↔ dark
await AppConfigManager.setTheme('dark'); // ตั้งค่าเฉพาะเจาะจง
const theme = AppConfigManager.getCurrentTheme();

Site Metadata Management

ดึงข้อมูล Site Metadata

// ดึงข้อมูลทั้งหมด
const site = AppConfigManager.getSiteMetadata();
console.log(site.title);        // 'ร้านหนังสือสุวิมล'
console.log(site.description);  // 'หนังสือเก่า สะสม และหายาก'

// ดึงแบบ specific (รองรับ dot notation)
const title = AppConfigManager.getSite('title');
const email = AppConfigManager.getSite('contact.email');
const facebook = AppConfigManager.getSite('social.facebook');

// ใช้ผ่าน window.siteConfig (auto-exported)
console.log(window.siteConfig.title);
console.log(window.siteConfig.contact.email);

อัพเดท Site Metadata

// Apply ข้อมูลใหม่ทั้งหมด
AppConfigManager.applySiteMetadata({
  title: 'ชื่อใหม่',
  description: 'คำอธิบายใหม่',
  logo: '/new-logo.png'
});

// Update แบบ partial (merge กับข้อมูลเดิม)
AppConfigManager.updateSiteMetadata({
  title: 'ชื่อใหม่',
  contact: {
    phone: '02-999-9999'  // อัพเดทแค่ phone
  }
});

ใช้ใน Template

<!-- ใช้ผ่าน window.siteConfig -->
<header>
  <img data-attr="src:siteConfig.logo" alt="Logo">
  <h1 data-text="siteConfig.title"></h1>
  <p data-text="siteConfig.description"></p>
</header>

<footer>
  <p>Email: <span data-text="siteConfig.contact.email"></span></p>
  <p>Tel: <span data-text="siteConfig.contact.phone"></span></p>
  <a data-attr="href:siteConfig.social.facebook">Facebook</a>
  <a data-attr="href:siteConfig.social.line">Line</a>
</footer>

data-on-load Support

Execute script หลัง config โหลดเสร็จ (เหมือน ApiComponent):

<body data-on-load="console.log('Config loaded!', site, theme, config)">
  <!--
    Available variables:
    - site: site metadata object
    - theme: current theme ('light' or 'dark')
    - config: full config { site, theme, variables }
  -->
</body>

ตัวอย่างการใช้งาน:

<body data-on-load="
  document.title = site.title;
  updateHeaderLogo(site.logo);
  initSocialLinks(site.social);
">

Toggle Button

HTML

<!-- Auto-enhanced - ไม่ต้องเขียน JS -->
<button data-component="theme">🌓</button>

<!-- หรือใช้ชื่อใหม่ -->
<button data-component="config">🌓</button>

AppConfigManager จะ enhance ปุ่มอัตโนมัติ:

  • ✅ เพิ่ม click handler
  • ✅ Sync data-theme-state attribute
  • ✅ ทุกปุ่มจะ sync เมื่อ theme เปลี่ยน

CSS สำหรับ Toggle Button

/* Icon font setup */
[data-component="theme"]:before,
[data-component="config"]:before {
  font-family: 'icomoon' !important;
}

/* แสดง icon ตาม theme */
[data-component="theme"][data-theme-state="light"]:before,
[data-component="config"][data-theme-state="light"]:before {
  content: "\e929";  /* icon-night */
}

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

หรือใช้ CSS classes:

[data-theme-state="light"] .icon-light { display: none; }
[data-theme-state="dark"] .icon-dark { display: none; }
  • ✅ ปุ่มทั้งหมดจะซิงค์เมื่อมีการเปลี่ยนธีม

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

/* Icon font setup */
[data-component="theme"]:before {
  font-family: 'icomoon' !important;
}

/* Show icon based on theme */
[data-component="theme"][data-theme-state="light"]:before {
  content: "\e929";  /* icon-night */
}

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

หรือจะใช้คลาสของ CSS แทน:

[data-theme-state="light"] .icon-light { display: none; }
[data-theme-state="dark"] .icon-dark { display: none; }

การตั้งค่าเต็มรูปแบบ

await Now.init({
  config: {
    enabled: true,
    defaultTheme: 'light',
    storageKey: 'app_theme',
    systemPreference: true,

    // Smooth Transitions
    transition: {
      enabled: true,
      duration: 300,
      hideOnSwitch: true,
      loadingClass: 'theme-loading',
      readyClass: 'theme-ready',
      transitionClass: 'theme-transitioning'
    },

    // Backend API - โหลด theme + site metadata
    api: {
      enabled: true,
      configUrl: '/api/config',
      headers: {},
      timeout: 5000,
      cacheResponse: true
    }
  }
});

Backend API Integration

โหลด Config จาก API

// โหลดอัตโนมัติตอน init (ถ้า api.enabled = true)
await Now.init({ config: { api: { enabled: true, configUrl: '/api/config' } } });

// หรือโหลดเอง
await AppConfigManager.loadFromAPI('/api/config');

// Refresh (ล้าง cache + โหลดใหม่)
await AppConfigManager.refreshFromAPI();

API Response Format

{
  "variables": {
    "--color-primary": "#6366f1",
    "--color-background": "#0f172a",
    "--logo-url": "/images/logo.png",
    "--hero-bg": "/images/hero.jpg"
  },
  "site": {
    "title": "My Site",
    "description": "Site description",
    "logo": "/images/logo.png",
    "favicon": "/images/favicon.ico",
    "lang": "th",
    "contact": {
      "email": "info@example.com",
      "phone": "02-xxx-xxxx"
    },
    "social": {
      "facebook": "https://facebook.com/...",
      "line": "@example"
    }
  }
}

หมายเหตุ:

  • variables - CSS custom properties (optional)
  • site - Site metadata (optional)
  • API ไม่ควบคุม light/dark mode - ผู้ใช้ควบคุมเอง

CSS Variables

Apply Variables

AppConfigManager.applyVariables({
  '--color-primary': '#6366f1',
  '--color-background': '#0f172a',
  '--hero-bg': '/images/hero.jpg'  // Auto-wrap เป็น url()
});

Security

  • อนุญาตเฉพาะ CSS Custom Properties (--*)
  • ลบ patterns อันตราย (javascript:, expression(), <script>)
  • URLs: รองรับ local paths หรือ absolute URLs จากโดเมนเดียวกัน
  • โดเมนภายนอกถูกบล็อกโดย default
  • Extensions ต้องเป็นรูปภาพ (ไม่มี SVG)

Smooth Transitions

CSS ที่ต้องใช้

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

body.theme-ready {
  opacity: 1;
}

body.theme-transitioning {
  opacity: 0;
}

วิธีทำงาน

  1. body เริ่มต้นที่ opacity: 0
  2. AppConfigManager โหลด theme
  3. เพิ่ม class theme-ready → fade in
  4. เมื่อ switch → fade out → apply → fade in

API Reference

Theme Methods

Method คำอธิบาย
toggle() สลับ theme (light ↔ dark)
setTheme(theme) ตั้งค่า theme ('light' หรือ 'dark')
getCurrentTheme() รับ theme ปัจจุบัน

Site Metadata Methods

Method คำอธิบาย
getSiteMetadata() รับ site metadata ทั้งหมด
getSite(key) รับค่าเดียว (รองรับ dot notation)
applySiteMetadata(data) Apply metadata ใหม่
updateSiteMetadata(updates) Update แบบ partial (merge)
exportToWindow() Export to window.siteConfig

CSS Variables Methods

Method คำอธิบาย
applyVariables(vars) Apply CSS Variables
clearVariables() ล้าง CSS Variables
getAppliedVariables() ดึง variables ที่ใช้

API Methods

Method คำอธิบาย
loadFromAPI(url?) โหลดจาก API
refreshFromAPI() โหลดใหม่ (ล้าง cache)

Lifecycle Methods

Method คำอธิบาย
init(options) Initialize manager
destroy() Cleanup ทั้งหมด
reset() Reset state

Events

Theme Events

Event เมื่อเกิด Payload
theme:initialized Init เสร็จ { theme }
theme:changed Theme เปลี่ยน { theme }
theme:ready พร้อมแสดงผล -

Config/Site Events

Event เมื่อเกิด Payload
config:site-loaded โหลด site metadata สำเร็จ { site }
config:site-updated Update metadata สำเร็จ { site, updates }
config:exported Export to window สำเร็จ { siteConfig }
config:on-load-executed data-on-load executed { body, script }
config:on-load-error data-on-load error { error, script }

Variables Events

Event เมื่อเกิด Payload
theme:variables-applied Apply variables เสร็จ { variables }
theme:variables-cleared ล้าง variables เสร็จ -

API Events

Event เมื่อเกิด Payload
theme:api-loaded โหลดจาก API สำเร็จ { config, fromCache }
theme:api-error API error { error, url }

Lifecycle Events

Event เมื่อเกิด
theme:destroyed Cleanup เสร็จ
theme:reset Reset เสร็จ
// ตัวอย่างการใช้งาน
EventManager.on('theme:changed', ({ theme }) => {
  console.log('เปลี่ยนเป็น:', theme);
});

EventManager.on('config:site-loaded', ({ site }) => {
  console.log('Site loaded:', site.title);
  document.title = site.title;
});

EventManager.on('config:site-updated', ({ site, updates }) => {
  console.log('Updated:', updates);
});

Cleanup

// Cleanup สมบูรณ์ (unmount app)
AppConfigManager.destroy();

// Reset เบาๆ (re-init)
AppConfigManager.reset();
await AppConfigManager.init({ ... });

ข้อควรระวัง

⚠️ 1. Enable ก่อนใช้

// ❌ ยังไม่ enable
AppConfigManager.toggle();

// ✅ Enable ก่อน
await Now.init({ config: { enabled: true } });
AppConfigManager.toggle();

⚠️ 2. ใช้ CSS Variables

/* ❌ ใช้ค่าคงที่ */
body { background: white; }

/* ✅ ใช้ variables */
body { background: var(--color-background); }

⚠️ 3. API Response Format

// ✅ ถูกต้อง - site เป็น object
{
  "variables": {...},
  "site": { "title": "...", "contact": {...} }
}

// ❌ ผิด - site เป็น array
{
  "site": [...]
}

⚠️ 4. Global vs Page-specific Data

// ✅ Global data → AppConfigManager
// - Site title, logo, description
// - Contact info, social links
// - Theme CSS variables

// ✅ Page-specific data → apiComponent
// - Product list/detail
// - User profile
// - Order history

⚠️ 5. เปิดผ่าน HTTP/HTTPS

// ❌ file:// protocol ใช้ API ไม่ได้
file:///path/to/index.html

// ✅ http(s):// protocol
http://localhost/...
https://example.com/...

⚠️ 6. Backward Compatibility

// ✅ ทั้งสองแบบใช้ได้
window.AppConfigManager
window.ThemeManager  // Alias

Now.init({ config: {...} })  // แนะนำ
Now.init({ theme: {...} })   // ยังใช้ได้

Use Cases

1. Multi-language Site

const lang = AppConfigManager.getSite('lang');  // 'th' or 'en'

// เปลี่ยนภาษา
AppConfigManager.updateSiteMetadata({
  lang: 'en',
  title: 'Bookstore',
  description: 'Old & Rare Books'
});

2. Dynamic SEO

EventManager.on('config:site-loaded', ({ site }) => {
  // Auto-update เมื่อโหลด
  document.title = site.title;

  const metaDesc = document.querySelector('meta[name="description"]');
  if (metaDesc) metaDesc.content = site.description;
});

3. White-label / Multi-tenant

// แต่ละ tenant มี config ต่างกัน
await AppConfigManager.init({
  api: {
    enabled: true,
    configUrl: `/api/config?tenant=${tenantId}`
  }
});

// tenant A: logo A, colors A, contact A
// tenant B: logo B, colors B, contact B

4. Theme-based Branding

EventManager.on('theme:changed', ({ theme }) => {
  const logo = theme === 'dark'
    ? '/images/logo-dark.png'
    : '/images/logo-light.png';

  AppConfigManager.updateSiteMetadata({ logo });
});

Performance Benefits

ก่อน (แยก API)

/api/theme → 150ms (CSS variables)
/api/site/info → 120ms (header)
/api/site/info → 120ms (footer, cached)
────────────────────────────────
Total: 270ms + 2-3 HTTP requests

หลัง (Combined API)

/api/config → 180ms (theme + site)
────────────────────────────────
Total: 180ms + 1 HTTP request
Savings: 90ms + 1-2 requests ⚡

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