Now.js Framework Documentation
ModalDataBinder - Declarative Modal Data Binding
ModalDataBinder - Declarative Modal Data Binding
เอกสารฉบับนี้อธิบาย ModalDataBinder ซึ่งเป็น helper สำหรับใช้งาน modal แบบ declarative ผ่าน HTML attributes พร้อมระบบ data binding, template evaluation และ gallery navigation
📋 สารบัญ
- ภาพรวม
- การติดตั้งและนำเข้า
- การใช้งานพื้นฐาน
- HTML Attributes
- Template Context Evaluation
- Gallery Mode
- การทำงานกับ Modal Elements
- JavaScript API
- ตัวอย่างการใช้งาน
- API Reference
- แนวทางปฏิบัติที่ดี
ภาพรวม
ModalDataBinder เป็น helper ที่ช่วยให้ใช้งาน modal แบบ declarative ผ่าน HTML attributes โดยไม่ต้องเขียน JavaScript เพิ่มเติม รองรับการผูกข้อมูลจาก template loops และ gallery navigation
ฟีเจอร์หลัก
- ✅ Declarative API: ใช้งานผ่าน HTML attributes ได้ทันที
- ✅ Data Binding: ผูกข้อมูลจาก template context อัตโนมัติ
- ✅ Template Evaluation: ประเมินค่า expressions จาก template loops
- ✅ Gallery Navigation: รองรับการนำทางแบบ gallery (prev/next)
- ✅ Dual Mode: รองรับทั้ง pre-existing HTML modals และ Modal instances
- ✅ Auto-initialization: เริ่มทำงานอัตโนมัติเมื่อหน้าโหลด
- ✅ Event Delegation: ใช้ event delegation สำหรับ performance
เมื่อไหร่ควรใช้ ModalDataBinder
✅ ใช้ ModalDataBinder เมื่อ:
- มี modal elements อยู่ใน HTML แล้ว
- ต้องการผูกข้อมูลจาก template loops
- ต้องการ gallery navigation (prev/next)
- ต้องการใช้งานแบบ declarative ไม่ต้องเขียน JavaScript
❌ ไม่ควรใช้ ModalDataBinder เมื่อ:
- ต้องการสร้าง modal แบบ programmatic (ใช้
Modalclass แทน) - ต้องการควบคุมทุกรายละเอียดของ modal
- ไม่มีข้อมูลที่ต้องผูก
การติดตั้งและนำเข้า
ModalDataBinder โหลดมาพร้อมกับ Now.js Framework และเริ่มทำงานอัตโนมัติ:
// ไม่ต้อง import - เริ่มทำงานอัตโนมัติ
console.log(window.ModalDataBinder); // ModalDataBinder objectสิ่งที่ต้องพึ่งพา
- Modal – สำหรับสร้าง Modal instances (สำหรับ dynamic modals)
- BackdropManager – สำหรับจัดการ backdrop
- TemplateManager – สำหรับ template context evaluation (ทางเลือก)
การใช้งานพื้นฐาน
1. Modal แบบง่าย
<!-- Modal Element -->
<div class="modal" id="simple-modal">
<div class="modal-content">
<button class="modal-close">×</button>
<div class="modal-body">
<h3 data-modal-target="title"></h3>
<p data-modal-target="message"></p>
</div>
</div>
</div>
<!-- Trigger Button -->
<button data-modal="simple-modal"
data-modal-bind="title:'สวัสดี', message:'ยินดีต้อนรับ'">
เปิด Modal
</button>2. Modal กับ Template Loop
<!-- Gallery Items -->
<div class="gallery">
<div class="gallery-item"
data-modal="image-modal"
data-modal-bind="image:item.image, title:item.title, desc:item.desc"
data-image="photo1.jpg"
data-title="รูปที่ 1"
data-desc="รายละเอียดรูปที่ 1">
<img src="photo1.jpg">
</div>
<div class="gallery-item"
data-modal="image-modal"
data-modal-bind="image:item.image, title:item.title, desc:item.desc"
data-image="photo2.jpg"
data-title="รูปที่ 2"
data-desc="รายละเอียดรูปที่ 2">
<img src="photo2.jpg">
</div>
</div>
<!-- Modal -->
<div class="modal" id="image-modal">
<div class="modal-content">
<button class="modal-close">×</button>
<div class="modal-body">
<img data-modal-target="image">
<h3 data-modal-target="title"></h3>
<p data-modal-target="desc"></p>
</div>
</div>
</div>3. Gallery Mode
<!-- Gallery Items -->
<div class="gallery">
<div class="gallery-item"
data-modal="gallery-modal"
data-modal-bind="image:item.image, title:item.title"
data-modal-gallery="true"
data-image="photo1.jpg"
data-title="รูปที่ 1">
<img src="photo1.jpg">
</div>
<!-- More items... -->
</div>
<!-- Gallery Modal -->
<div class="modal" id="gallery-modal">
<div class="modal-content">
<button class="modal-close">×</button>
<div class="modal-body">
<img data-modal-target="image">
<h3 data-modal-target="title"></h3>
</div>
<div class="modal-controls">
<button data-modal-nav="prev">‹ ก่อนหน้า</button>
<button data-modal-nav="next">ถัดไป ›</button>
</div>
</div>
</div>HTML Attributes
Trigger Attributes
| Attribute | Type | Required | Description |
|---|---|---|---|
data-modal |
string | ✅ | ID ของ modal ที่ต้องการเปิด |
data-modal-bind |
string | ❌ | Data binding expressions (คั่นด้วย comma) |
data-modal-gallery |
boolean | ❌ | เปิดใช้งาน gallery mode |
data-modal-gallery-loop |
boolean | ❌ | วนกลับเมื่อถึงท้าย (default: true) |
Modal Target Attributes
| Attribute | Type | Description |
|---|---|---|
data-modal-target |
string | ชื่อของ data key ที่จะผูก |
data-modal-html |
boolean | ใช้ innerHTML แทน textContent |
Navigation Attributes
| Attribute | Type | Description |
|---|---|---|
data-modal-nav |
string | ทิศทางการนำทาง: "prev" หรือ "next" |
ตัวอย่าง Data Binding Syntax
<!-- Simple values -->
<button data-modal="my-modal"
data-modal-bind="name:'John', age:30">
Open
</button>
<!-- From data attributes -->
<div data-modal="my-modal"
data-modal-bind="title:item.title, desc:item.desc"
data-title="My Title"
data-desc="My Description">
Click me
</div>
<!-- Mixed -->
<div data-modal="my-modal"
data-modal-bind="name:item.name, status:'active', count:$index"
data-name="John">
Item
</div>Template Context Evaluation
ModalDataBinder สามารถประเมินค่า expressions จาก template context:
Special Variables
| Variable | Description | Example |
|---|---|---|
$index |
Index ของ element ใน parent | count:$index |
$item |
Dataset ทั้งหมดของ element | data:$item |
item.property |
Property จาก dataset | name:item.title |
Expression Evaluation
<!-- Template Loop Example -->
<div data-for="product of products">
<div class="product-card"
data-modal="product-modal"
data-modal-bind="
id:item.id,
name:item.name,
price:item.price,
index:$index
"
data-id="123"
data-name="Product Name"
data-price="999">
<h3>{product.name}</h3>
</div>
</div>Data Attribute Mapping
<!-- Element with data attributes -->
<div data-title="My Title"
data-description="My Description"
data-image-url="/image.jpg"
data-modal="my-modal"
data-modal-bind="
title:item.title,
desc:item.description,
image:item.imageUrl
">
Click
</div>Mapping Rules:
item.title→data-titleitem.description→data-descriptionitem.imageUrl→data-image-url(camelCase → kebab-case)
Gallery Mode
Gallery mode ช่วยให้สามารถนำทางระหว่าง items ได้:
เปิดใช้งาน Gallery
<!-- Gallery Items -->
<div class="gallery">
<div data-modal="gallery-modal"
data-modal-gallery="true"
data-modal-bind="image:item.image, title:item.title"
data-image="photo1.jpg"
data-title="Photo 1">
<img src="photo1.jpg">
</div>
<div data-modal="gallery-modal"
data-modal-gallery="true"
data-modal-bind="image:item.image, title:item.title"
data-image="photo2.jpg"
data-title="Photo 2">
<img src="photo2.jpg">
</div>
<div data-modal="gallery-modal"
data-modal-gallery="true"
data-modal-bind="image:item.image, title:item.title"
data-image="photo3.jpg"
data-title="Photo 3">
<img src="photo3.jpg">
</div>
</div>
<!-- Gallery Modal -->
<div class="modal" id="gallery-modal">
<div class="modal-content">
<button class="modal-close">×</button>
<img data-modal-target="image" class="gallery-image">
<h3 data-modal-target="title"></h3>
<!-- Navigation Buttons -->
<button class="modal-prev" data-modal-nav="prev">‹</button>
<button class="modal-next" data-modal-nav="next">›</button>
</div>
</div>Gallery Options
<!-- ไม่วนกลับเมื่อถึงท้าย -->
<div data-modal="gallery-modal"
data-modal-gallery="true"
data-modal-gallery-loop="false"
data-modal-bind="...">
Item
</div>Gallery Navigation
<!-- Previous Button -->
<button data-modal-nav="prev">ก่อนหน้า</button>
<!-- Next Button -->
<button data-modal-nav="next">ถัดไป</button>
<!-- Custom Styling -->
<button class="btn-prev" data-modal-nav="prev">
<i class="icon-arrow-left"></i> ก่อนหน้า
</button>การทำงานกับ Modal Elements
ModalDataBinder รองรับ 2 โหมดการทำงาน:
1. Pre-existing HTML Modals
สำหรับ modal elements ที่มีอยู่แล้วใน HTML:
<!-- Modal ที่มีอยู่แล้วใน HTML -->
<div class="modal" id="my-modal">
<div class="modal-content">
<button class="modal-close">×</button>
<div class="modal-body">
<h3 data-modal-target="title"></h3>
<p data-modal-target="message"></p>
</div>
</div>
</div>
<!-- Trigger -->
<button data-modal="my-modal"
data-modal-bind="title:'Hello', message:'World'">
Open Modal
</button>คุณสมบัติ:
- ใช้ modal element ที่มีอยู่แล้ว
- แสดง/ซ่อนด้วย CSS class
.show - จัดการ backdrop ผ่าน BackdropManager
- ไม่มี animation (ใช้ CSS transitions)
2. Dynamic Modal Instances
สำหรับ modal ที่ไม่มี .modal class (จะสร้าง Modal instance):
<!-- Element ที่ไม่ใช่ modal -->
<div id="dynamic-modal">
<h3 data-modal-target="title"></h3>
<p data-modal-target="message"></p>
</div>
<!-- Trigger -->
<button data-modal="dynamic-modal"
data-modal-bind="title:'Dynamic', message:'Modal'">
Open Dynamic Modal
</button>คุณสมบัติ:
- สร้าง Modal instance อัตโนมัติ
- มี animation support
- มี accessibility features ครบถ้วน
- มี lifecycle callbacks
JavaScript API
getInstance(modalId)
อ่าน modal instance หรือ reference
const modalRef = ModalDataBinder.state.modalInstances.get('my-modal');
if (modalRef.isPreExisting) {
// Pre-existing modal
console.log(modalRef.element);
} else {
// Modal instance
console.log(modalRef); // Modal instance
}openModalWithData(modalId, data, options)
เปิด modal พร้อมข้อมูล
ModalDataBinder.openModalWithData('my-modal', {
title: 'Hello',
message: 'World'
}, {
gallery: false
});Parameters:
modalId(string) - ID ของ modaldata(Object) - ข้อมูลที่ต้องการผูกoptions(Object) - ตัวเลือกเพิ่มเติม
updateModalData(modalId, data)
อัพเดทข้อมูลของ modal ที่เปิดอยู่
ModalDataBinder.updateModalData('my-modal', {
message: 'Updated message'
});Parameters:
modalId(string) - ID ของ modaldata(Object) - ข้อมูลที่ต้องการอัพเดท
closeModal(modalId)
ปิด modal
ModalDataBinder.closeModal('my-modal');Parameters:
modalId(string) - ID ของ modal
navigateGallery(modalId, direction)
นำทาง gallery
// ไปรูปถัดไป
ModalDataBinder.navigateGallery('gallery-modal', 'next');
// ไปรูปก่อนหน้า
ModalDataBinder.navigateGallery('gallery-modal', 'prev');Parameters:
modalId(string) - ID ของ modaldirection(string) -'prev'หรือ'next'
getModalData(modalId)
อ่านข้อมูลของ modal
const data = ModalDataBinder.getModalData('my-modal');
console.log(data);Parameters:
modalId(string) - ID ของ modal
Returns: Object | null
ตัวอย่างการใช้งาน
1. Product Gallery
<!-- Product Grid -->
<div class="product-grid">
<div class="product-card"
data-modal="product-modal"
data-modal-gallery="true"
data-modal-bind="
image:item.image,
name:item.name,
price:item.price,
desc:item.description
"
data-image="/products/product1.jpg"
data-name="สินค้า 1"
data-price="999"
data-description="รายละเอียดสินค้า 1">
<img src="/products/product1.jpg">
<h3>สินค้า 1</h3>
<p class="price">฿999</p>
</div>
<div class="product-card"
data-modal="product-modal"
data-modal-gallery="true"
data-modal-bind="
image:item.image,
name:item.name,
price:item.price,
desc:item.description
"
data-image="/products/product2.jpg"
data-name="สินค้า 2"
data-price="1,299"
data-description="รายละเอียดสินค้า 2">
<img src="/products/product2.jpg">
<h3>สินค้า 2</h3>
<p class="price">฿1,299</p>
</div>
</div>
<!-- Product Modal -->
<div class="modal" id="product-modal">
<div class="modal-content">
<button class="modal-close">×</button>
<div class="modal-body">
<img data-modal-target="image" class="product-image">
<h2 data-modal-target="name"></h2>
<p class="price" data-modal-target="price"></p>
<p data-modal-target="desc"></p>
<button class="btn-add-cart">เพิ่มในตะกร้า</button>
</div>
<div class="modal-nav">
<button data-modal-nav="prev">‹ ก่อนหน้า</button>
<button data-modal-nav="next">ถัดไป ›</button>
</div>
</div>
</div>2. User Profile Cards
<!-- User Cards -->
<div class="user-grid">
<div class="user-card"
data-modal="user-modal"
data-modal-bind="
avatar:item.avatar,
name:item.name,
email:item.email,
role:item.role,
bio:item.bio
"
data-avatar="/avatars/user1.jpg"
data-name="สมชาย ใจดี"
data-email="somchai@example.com"
data-role="Developer"
data-bio="Full-stack developer">
<img src="/avatars/user1.jpg" class="avatar">
<h3>สมชาย ใจดี</h3>
<p>Developer</p>
</div>
<!-- More users... -->
</div>
<!-- User Modal -->
<div class="modal" id="user-modal">
<div class="modal-content">
<button class="modal-close">×</button>
<div class="modal-body">
<div class="user-profile">
<img data-modal-target="avatar" class="avatar-large">
<h2 data-modal-target="name"></h2>
<p class="role" data-modal-target="role"></p>
<p class="email" data-modal-target="email"></p>
<p class="bio" data-modal-target="bio"></p>
</div>
</div>
</div>
</div>3. Image Lightbox
<!-- Image Grid -->
<div class="image-grid">
<div class="image-item"
data-modal="lightbox"
data-modal-gallery="true"
data-modal-bind="image:item.src, caption:item.caption"
data-src="/images/photo1.jpg"
data-caption="ภาพที่ 1">
<img src="/images/thumb1.jpg">
</div>
<div class="image-item"
data-modal="lightbox"
data-modal-gallery="true"
data-modal-bind="image:item.src, caption:item.caption"
data-src="/images/photo2.jpg"
data-caption="ภาพที่ 2">
<img src="/images/thumb2.jpg">
</div>
<!-- More images... -->
</div>
<!-- Lightbox Modal -->
<div class="modal modal-lightbox" id="lightbox">
<div class="modal-content">
<button class="modal-close">×</button>
<img data-modal-target="image" class="lightbox-image">
<p class="caption" data-modal-target="caption"></p>
<button class="lightbox-prev" data-modal-nav="prev">‹</button>
<button class="lightbox-next" data-modal-nav="next">›</button>
</div>
</div>
<style>
.modal-lightbox {
background: rgba(0,0,0,0.9);
}
.lightbox-image {
max-width: 90vw;
max-height: 80vh;
object-fit: contain;
}
.lightbox-prev,
.lightbox-next {
position: absolute;
top: 50%;
transform: translateY(-50%);
font-size: 48px;
color: white;
background: none;
border: none;
cursor: pointer;
}
.lightbox-prev { left: 20px; }
.lightbox-next { right: 20px; }
</style>4. Notification Details
<!-- Notification List -->
<div class="notification-list">
<div class="notification-item"
data-modal="notification-modal"
data-modal-bind="
title:item.title,
message:item.message,
time:item.time,
type:item.type
"
data-title="การแจ้งเตือนใหม่"
data-message="คุณมีข้อความใหม่จาก Admin"
data-time="5 นาทีที่แล้ว"
data-type="info">
<span class="icon">🔔</span>
<div class="content">
<h4>การแจ้งเตือนใหม่</h4>
<p>คุณมีข้อความใหม่...</p>
</div>
</div>
<!-- More notifications... -->
</div>
<!-- Notification Modal -->
<div class="modal" id="notification-modal">
<div class="modal-content">
<button class="modal-close">×</button>
<div class="modal-body">
<h2 data-modal-target="title"></h2>
<p class="time" data-modal-target="time"></p>
<p class="message" data-modal-target="message"></p>
<span class="badge" data-modal-target="type"></span>
</div>
</div>
</div>5. Dynamic Content with Template
<!-- Template Loop -->
<div data-for="article of articles">
<article class="article-card"
data-modal="article-modal"
data-modal-bind="
title:item.title,
author:item.author,
date:item.date,
content:item.content,
tags:item.tags
"
data-title="{article.title}"
data-author="{article.author}"
data-date="{article.date}"
data-content="{article.content}"
data-tags="{article.tags}">
<h3>{article.title}</h3>
<p class="meta">โดย {article.author} • {article.date}</p>
</article>
</div>
<!-- Article Modal -->
<div class="modal" id="article-modal">
<div class="modal-content">
<button class="modal-close">×</button>
<div class="modal-body">
<h1 data-modal-target="title"></h1>
<p class="meta">
โดย <span data-modal-target="author"></span>
• <span data-modal-target="date"></span>
</p>
<div data-modal-target="content" data-modal-html></div>
<div class="tags" data-modal-target="tags"></div>
</div>
</div>
</div>API Reference
Configuration
ModalDataBinder.config = {
debug: true, // เปิด debug logging
galleryClass: 'modal-gallery', // CSS class สำหรับ gallery
bindAttribute: 'data-modal-bind',
targetAttribute: 'data-modal-target',
sanitize: true // เปิด sanitization
};State
ModalDataBinder.state = {
initialized: false,
modalInstances: new Map(), // modalId => Modal instance or {element, isPreExisting}
galleryData: new Map() // modalId => {items, currentIndex, loop}
};Methods
Public Methods
init()- เริ่มต้น ModalDataBinderopenModalWithData(modalId, data, options)- เปิด modal พร้อมข้อมูลupdateModalData(modalId, data)- อัพเดทข้อมูลcloseModal(modalId)- ปิด modalnavigateGallery(modalId, direction)- นำทาง gallerygetModalData(modalId)- อ่านข้อมูลdestroy()- ทำลาย instances ทั้งหมด
Private Methods
bindModalTriggers()- ผูก click eventsbindGalleryNavigation()- ผูก navigation eventshandleModalTrigger(trigger)- จัดการ trigger clickparseBindData(bindStr)- แปลง bind string เป็น objectevaluateBindData(bindData, trigger)- ประเมินค่า expressionsfindTemplateContext(element)- หา template contextevaluateExpression(expr, context, trigger)- ประเมินค่า expressionsetupGallery(modalId, trigger, data)- ตั้งค่า gallery_bindDataToElement(modalElement, data)- ผูกข้อมูลกับ element_setElementContent(element, value, key)- ตั้งค่า content_showPreExistingModal(modalElement)- แสดง pre-existing modal_bindPreExistingModalEvents(modalElement)- ผูก eventslog(...args)- Debug logging
แนวทางปฏิบัติที่ดี
✅ ควรทำ
1. ใช้ Data Attributes สำหรับข้อมูล
<!-- ✅ ดี - ใช้ data attributes -->
<div data-modal="my-modal"
data-modal-bind="name:item.name, email:item.email"
data-name="John"
data-email="john@example.com">
Click
</div>
<!-- ❌ ไม่ดี - hardcode ค่า -->
<div data-modal="my-modal"
data-modal-bind="name:'John', email:'john@example.com'">
Click
</div>2. ใช้ Gallery Mode สำหรับ Collections
<!-- ✅ ดี - gallery mode -->
<div data-modal="gallery"
data-modal-gallery="true"
data-modal-bind="...">
Item
</div>
<!-- ❌ ไม่ดี - แยก modal แต่ละ item -->
<div data-modal="item1-modal">Item 1</div>
<div data-modal="item2-modal">Item 2</div>3. ใช้ Semantic HTML
<!-- ✅ ดี - semantic -->
<button data-modal="confirm-modal">Confirm</button>
<!-- ❌ ไม่ดี - div as button -->
<div data-modal="confirm-modal">Confirm</div>❌ ไม่ควรทำ
1. อย่าใส่ HTML ใน Bind Expressions
<!-- ❌ อันตราย - XSS -->
<div data-modal="my-modal"
data-modal-bind="content:'<script>alert(1)</script>'">
Click
</div>
<!-- ✅ ปลอดภัย - ใช้ data attributes -->
<div data-modal="my-modal"
data-modal-bind="content:item.content"
data-content="Safe content">
Click
</div>2. อย่าใช้ Modal ID ซ้ำ
<!-- ❌ ไม่ดี - ID ซ้ำ -->
<div class="modal" id="my-modal">...</div>
<div class="modal" id="my-modal">...</div>
<!-- ✅ ดี - ID ไม่ซ้ำ -->
<div class="modal" id="modal-1">...</div>
<div class="modal" id="modal-2">...</div>3. อย่าลืมปุ่มปิด
<!-- ❌ ไม่ดี - ไม่มีปุ่มปิด -->
<div class="modal" id="my-modal">
<div class="modal-content">
<p>Content</p>
</div>
</div>
<!-- ✅ ดี - มีปุ่มปิด -->
<div class="modal" id="my-modal">
<div class="modal-content">
<button class="modal-close">×</button>
<p>Content</p>
</div>
</div>💡 Tips
1. ใช้ Debug Mode
// เปิด debug logging
ModalDataBinder.config.debug = true;
// ดู logs ใน console
// [ModalDataBinder] Using pre-existing modal element: my-modal
// [ModalDataBinder] Opened modal my-modal with data {...}2. ตรวจสอบ Modal State
// ดู modal instances
console.log(ModalDataBinder.state.modalInstances);
// ดู gallery data
console.log(ModalDataBinder.state.galleryData);3. ใช้ CSS สำหรับ Animation
.modal {
opacity: 0;
transition: opacity 0.3s;
}
.modal.show {
opacity: 1;
}เพิ่มเติม
- Modal - Modal class สำหรับสร้าง modal แบบ programmatic
- BackdropManager - จัดการ backdrop
- TemplateManager - Template rendering system