Now.js Framework Documentation

Now.js Framework Documentation

TemplateManager

TH 15 Dec 2025 08:53

TemplateManager

ภาพรวม

TemplateManager คือระบบ template และ data binding ใน Now.js Framework ประกอบด้วย:

  • Template Loading - โหลด HTML template แบบ dynamic ผ่าน AJAX
  • Data Binding - ผูกข้อมูลกับ DOM elements แบบ reactive
  • JavaScript Initialization - เริ่มต้น JavaScript อัตโนมัติเมื่อ template โหลด
  • Component Lifecycle - จัดการ cleanup เมื่อ template ถูกลบ

ใช้เมื่อ:

  • ต้องการแยก HTML templates ออกจากหน้าหลัก
  • ต้องการ reactive data binding ใน templates
  • ต้องการ initialize JavaScript เมื่อ template โหลด
  • ต้องการโครงสร้างโค้ดที่สะอาดและดูแลง่าย

ทำไมต้องใช้:

  • ✅ 12 data directives สำหรับ binding
  • ✅ โหลด templates แบบ dynamic ผ่าน AJAX
  • ✅ Auto-initialize JavaScript functions
  • ✅ รองรับ cleanup เมื่อ templates ถูกลบ
  • ✅ มีความปลอดภัยในตัว (sanitization, validation)

Data Directives

TemplateManager มี 12 data directives สำหรับผูกข้อมูลกับ elements:

Directive คำอธิบาย เอกสาร
data-text ผูกเนื้อหาข้อความพร้อม formatters data-text.md
data-html ผูก HTML content พร้อม sanitization data-html.md
data-attr ผูก attributes (href, src ฯลฯ) data-attr.md
data-class ผูก CSS class (3 โหมด) data-class.md
data-style ผูก inline style data-style.md
data-if แสดงผลแบบมีเงื่อนไข data-if.md
data-for แสดงผลรายการ data-for.md
data-on จัดการ event (20+ modifiers) data-on.md
data-model Two-way binding ฟอร์ม data-model.md
data-checked ผูก checkbox/radio data-checked.md
data-container โหลด component แบบ dynamic data-container.md
data-script เริ่มต้น JavaScript data-script.md

ดู ดัชนี Template Directives สำหรับการอ้างอิงด่วน

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

Template กับ Data Binding

<!-- profile.html -->
<main class="content" data-script="initProfile">
  <h1 data-text="user.name"></h1>
  <p data-text="user.email"></p>

  <ul data-for="skill in user.skills">
    <template>
      <li data-text="skill"></li>
    </template>
  </ul>

  <form data-form="editprofile">
    <input type="text" name="name" data-model="user.name">
    <button type="submit">บันทึก</button>
  </form>
</main>

Initialization Script

// main.js - Global function
function initProfile(element, data) {
  console.log('โหลด Profile แล้ว!', element);
  console.log('ข้อมูลจาก API:', data);

  // Custom initialization
  const input = element.querySelector('input[name="name"]');
  input.addEventListener('focus', () => {
    console.log('ฟิลด์ชื่อถูก focus');
  });

  // Return cleanup function (ไม่บังคับ)
  return () => {
    console.log('Cleanup Profile');
  };
}

การทำงาน:

  1. RouterManager โหลด profile.html
  2. TemplateManager ประมวลผล data directives ทั้งหมด
  3. ตรวจพบ data-script="initProfile"
  4. เรียก window.initProfile(element, data)
  5. เมื่อ navigate ออก เรียก cleanup function

ฟีเจอร์หลัก

1. Reactive Data Binding

การเปลี่ยนแปลงข้อมูลอัพเดท DOM อัตโนมัติ:

<span data-text="count"></span>
<button data-on="click:increment">+</button>
let count = 0;
function increment() {
  count++;
  // DOM อัพเดทอัตโนมัติ
}

2. Template Expressions

รองรับ expressions ใน bindings:

<!-- Text กับ formatters -->
<span data-text="price | currency:'THB'"></span>

<!-- Computed class -->
<div data-class="count > 0 ? 'has-items' : 'empty'"></div>

<!-- Conditional rendering -->
<div data-if="user.isAdmin && permissions.edit">
  ส่วนควบคุมแอดมิน
</div>

3. Event Handling

Event binding แบบ declarative พร้อม modifiers:

<!-- Basic click -->
<button data-on="click:save">บันทึก</button>

<!-- กับ modifiers -->
<form data-on="submit.prevent:handleSubmit">
<input data-on="keydown.enter:search">
<div data-on="click.self:close">

4. Auto-Initialization

Templates ที่มี data-script จะถูก initialize อัตโนมัติ:

<div class="dashboard" data-script="initDashboard">
  <div id="chart"></div>
</div>
function initDashboard(element, data) {
  const chart = new Chart(element.querySelector('#chart'), {
    type: 'bar',
    data: data.chartData
  });

  return () => chart.destroy();
}

5. Cleanup Functions

Return function เพื่อ cleanup เมื่อ template ถูกลบ:

function initTimer(element, data) {
  const timer = setInterval(() => {
    element.querySelector('#time').textContent = new Date().toLocaleTimeString();
  }, 1000);

  // ถูกเรียกเมื่อ navigate ออก
  return () => clearInterval(timer);
}

การใช้งานขั้นสูง

รับข้อมูลจาก API

เมื่อใช้กับ RouterManager ที่มี data-load-api:

// Router configuration
routes: {
  '/dashboard': {
    template: 'dashboard.html',
    title: 'Dashboard',
    'data-load-api': 'api/dashboard/stats'
  }
}
function initDashboard(element, data) {
  // data มี API response
  console.log(data); // { totalUsers: 150, activeUsers: 89, ... }
  renderStats(data);
}

หลาย Scripts

Templates สามารถมีหลาย data-script attributes:

<div class="page">
  <main data-script="initMainContent">...</main>
  <aside data-script="initSidebar">...</aside>
  <footer data-script="initFooter">...</footer>
</div>

Functions ถูกเรียกตามลำดับ DOM (บนลงล่าง)

Nested Templates

Templates สามารถมี templates อื่นๆ ได้:

<div data-container="'components/' + currentComponent + '.html'"></div>

ดู data-container.md สำหรับรายละเอียด

ข้อควรระวัง

⚠️ 1. Function ต้องเป็น Global

// ❌ ไม่ทำงาน - Function อยู่ใน module scope
document.addEventListener('DOMContentLoaded', () => {
  function initProfile(element, data) { }
});

// ✅ ถูก - Global function
function initProfile(element, data) { }

// ✅ ถูก - กำหนด global ชัดเจน
window.initProfile = function(element, data) { };

⚠️ 2. Memory Leaks

// ❌ ไม่ดี - ไม่มี cleanup
function initTimer(element, data) {
  setInterval(() => { /* ทำงานตลอดไป */ }, 1000);
}

// ✅ ดี - มี cleanup
function initTimer(element, data) {
  const timer = setInterval(() => { /* ... */ }, 1000);
  return () => clearInterval(timer);
}

⚠️ 3. ตรวจสอบ Element ก่อน

// ❌ อาจ error
function initForm(element, data) {
  element.querySelector('#input').value = data.value;
}

// ✅ ตรวจสอบก่อน
function initForm(element, data) {
  const input = element.querySelector('#input');
  if (input) input.value = data.value || '';
}

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

1. ชื่อ Function ที่ชัดเจน

// ✅ ดี
function initUserProfileForm(element, data) { }
function initSalesChart(element, data) { }
function initProductGallery(element, data) { }

2. แยก Helper Functions

function initDashboard(element, data) {
  const cleanup1 = initStats(element, data.stats);
  const cleanup2 = initChart(element, data.chart);

  return () => {
    cleanup1?.();
    cleanup2?.();
  };
}

3. Event Delegation

function initList(element, data) {
  const list = element.querySelector('#list');

  const handleClick = (e) => {
    if (e.target.matches('.btn-delete')) {
      handleDelete(e.target.dataset.id);
    }
  };

  list.addEventListener('click', handleClick);
  return () => list.removeEventListener('click', handleClick);
}

4. Error Handling

function initDataTable(element, data) {
  try {
    const table = element.querySelector('#table');
    if (!table) throw new Error('ไม่พบ Table');

    // Initialize...
  } catch (error) {
    console.error('Init ล้มเหลว:', error);
    element.innerHTML = '<p class="error">โหลดล้มเหลว</p>';
  }
}

การผสานระบบ

กับ FormManager

<form data-form="editprofile" data-script="initProfileForm" data-load-api="api/users/get">
  <!-- FormManager จัดการฟอร์ม, data-script เพิ่ม logic เอง -->
</form>

กับ ComponentManager

<div data-component="sidebar" data-script="initSidebar"></div>

กับ ApiComponent

<div data-api-component="userList" data-script="initUserList" data-api-url="api/users/list">
</div>

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

เอกสาร Directive

ระบบอื่นๆ