Now.js Framework Documentation
ResponseHandler
ResponseHandler
ภาพรวม
ResponseHandler เป็นระบบจัดการ API Response แบบรวมศูนย์ที่ช่วยลดความซับซ้อนของโค้ด JavaScript โดยการให้เซิร์ฟเวอร์ส่ง "คำสั่ง" (actions) มาพร้อมกับ response แทนที่จะต้องเขียนโค้ดจัดการแต่ละ case ซ้ำๆ ในฝั่ง client
จุดเด่น:
- ลดโค้ดซ้ำซ้อน - API ควบคุมการแสดงผลและพฤติกรรมได้โดยตรง
- รองรับ action หลากหลายประเภท (notification, modal, redirect, DOM manipulation)
- Hybrid Modal Approach - Frontend กำหนด template, Backend ส่งแค่ data (แยก concerns)
- Template-Driven Modals - โหลดและ render template อัตโนมัติพร้อม data binding
- สามารถสร้าง custom action handler ได้
- รองรับการทำงานแบบ async/await
- มีระบบ condition เพื่อควบคุมการทำงานของ action
- มี Priority System สำหรับ modal configuration (API force > API suggest > Frontend default)
- Security-first: ปิด eval โดย default
การติดตั้งและนำเข้า
ResponseHandler โหลดมาพร้อมกับ Now.js Framework และพร้อมใช้งานทันทีผ่าน window object:
// ไม่ต้อง import - พร้อมใช้งานทันที
console.log(window.ResponseHandler); // อ็อบเจ็กต์ ResponseHandler ที่ผูกไว้กับ windowการกำหนดค่า
ตัวเลือก Configuration
| ตัวเลือก | ประเภท | ค่าเริ่มต้น | คำอธิบาย |
|---|---|---|---|
debug |
Boolean | false |
เปิด/ปิด debug logging |
defaultRedirectDelay |
Number | 1000 |
ระยะเวลาหน่วงการ redirect (ms) |
defaultNotificationDuration |
Number | 5000 |
ระยะเวลาแสดง notification (ms) |
allowEval |
Boolean | false |
อนุญาตให้ใช้ eval action (ไม่แนะนำ) |
autoHandleFormResponses |
Boolean | true |
จัดการ form response อัตโนมัติ |
การ Initialize
// Auto-initialize (ใช้ค่า default)
ResponseHandler.init();
// Initialize พร้อม custom config
ResponseHandler.init({
debug: true,
defaultRedirectDelay: 2000,
defaultNotificationDuration: 3000
});โครงสร้าง API Response
รูปแบบพื้นฐาน
{
"success": true,
"message": "บันทึกข้อมูลสำเร็จ",
"data": {...},
"actions": [
{
"type": "notification",
"level": "success",
"message": "บันทึกข้อมูลเรียบร้อย"
},
{
"type": "redirect",
"url": "/dashboard",
"delay": 1000
}
]
}รูปแบบ Action เดี่ยว
{
"success": true,
"type": "alert",
"message": "ทำรายการสำเร็จ"
}Action พร้อม Condition
{
"type": "notification",
"message": "มีข้อความใหม่",
"condition": true
}Methods
init(options)
เริ่มต้นการทำงานของ ResponseHandler
Parameters:
options(Object, optional): ตัวเลือก configuration
Returns: ResponseHandler
ตัวอย่าง:
ResponseHandler.init({
debug: true,
defaultRedirectDelay: 1500
});process(response, context)
ประมวลผล API response และทำงานตาม actions ที่กำหนด
Parameters:
response(Object): API response objectcontext(Object, optional): Context object สำหรับส่งข้อมูลเพิ่มเติมให้ handlers
Returns: Promise<Object> - response object
ตัวอย่าง:
// ใช้กับ fetch API
const response = await fetch('/api/save', {
method: 'POST',
body: JSON.stringify(data)
});
const json = await response.json();
await ResponseHandler.process(json);
// ใช้พร้อม context
await ResponseHandler.process(json, {
tableId: 'users-table',
formId: 'user-form'
});executeAction(action, context)
ทำงาน action เดี่ยว
Parameters:
action(Object): Action objectcontext(Object, optional): Context object
Returns: Promise<void>
ตัวอย่าง:
await ResponseHandler.executeAction({
type: 'notification',
level: 'success',
message: 'บันทึกสำเร็จ'
});registerHandler(type, handler)
ลงทะเบียน custom action handler
Parameters:
type(String): ชื่อ action typehandler(Function): Function สำหรับจัดการ action(action, context) => {}
Returns: void
ตัวอย่าง:
// สร้าง custom action
ResponseHandler.registerHandler('playSound', (action) => {
const audio = new Audio(action.soundUrl);
audio.volume = action.volume || 1.0;
audio.play();
});
// ใช้งาน
await ResponseHandler.executeAction({
type: 'playSound',
soundUrl: '/sounds/success.mp3',
volume: 0.5
});executeCallback(fn, args)
เรียก callback function
Parameters:
fn(Function|String): Function หรือชื่อ function ใน window scopeargs(Array, optional): Arguments สำหรับส่งให้ function
Returns: void
ตัวอย่าง:
// เรียก function โดยตรง
ResponseHandler.executeCallback(() => {
console.log('Done!');
});
// เรียก function จากชื่อ
window.myCallback = (msg) => alert(msg);
ResponseHandler.executeCallback('myCallback', ['Hello!']);emit(eventName, data)
ส่ง custom event
Parameters:
eventName(String): ชื่อ eventdata(Any): ข้อมูลที่จะส่ง
Returns: void
ตัวอย่าง:
ResponseHandler.emit('data:updated', {
id: 123,
type: 'user'
});Built-in Action Types
1. Notification
แสดง notification message
{
"type": "notification",
"level": "success",
"message": "บันทึกข้อมูลสำเร็จ",
"duration": 3000
}Properties:
level: "success" | "info" | "warning" | "error"message: ข้อความที่จะแสดงduration: ระยะเวลาแสดง (ms)
2. Alert
แสดง browser alert dialog
{
"type": "alert",
"message": "กรุณากรอกข้อมูลให้ครบถ้วน"
}3. Confirm
แสดง confirm dialog และเรียก callback เมื่อกด OK
{
"type": "confirm",
"message": "ต้องการลบข้อมูลใช่หรือไม่?",
"onConfirm": "deleteRecord"
}Properties:
message: ข้อความถามonConfirm: Function หรือชื่อ function ที่จะเรียกเมื่อ confirm
4. Modal (Hybrid Approach)
เปิด/ปิด modal dialog พร้อมรองรับ template loading และ data binding
Hybrid Modal Approach:
ResponseHandler รองรับ 3 แนวทางในการแสดง modal:
- Frontend-Driven (แนะนำ 90%) - Frontend กำหนด template ใน HTML
- API Suggestion - API แนะนำ template (ถ้า frontend ไม่ได้กำหนด)
- API Force Override - API บังคับใช้ template (กรณีพิเศษ 10%)
Priority System:
API Force (force: true) [สูงสุด]
↓
API Suggest [กลาง]
↓
Frontend Config [ต่ำ - Default]แนวทาง 1: Frontend-Driven (แนะนำ) ⭐
HTML Configuration:
<table data-row-actions='{
"edit": {
"element": "button",
"className": "btn btn-success icon-edit",
"modal": {
"template": "editprofile",
"title": "Edit User Profile",
"className": "large-modal"
}
}
}'>API Response (Simple):
{
"success": true,
"data": {
"id": 123,
"name": "John Doe",
"email": "john@example.com"
},
"options": {
"provinces": [...],
"genders": [...]
}
}ข้อดี:
- ✅ Backend ไม่ต้องรู้จัก UI template
- ✅ Frontend ควบคุม UX ได้เต็มที่
- ✅ API เดียว ใช้ได้หลาย template
- ✅ แก้ไข template ไม่ต้องแก้ backend
แนวทาง 2: API Suggestion
API แนะนำ template (ใช้ถ้า frontend ไม่ได้กำหนด):
{
"success": true,
"data": {...},
"actions": [{
"type": "modal",
"template": "editprofile",
"title": "Edit Profile"
}]
}แนวทาง 3: API Force Override
API บังคับใช้ template (กรณี role-based UI, security):
{
"success": true,
"data": {...},
"actions": [{
"type": "modal",
"template": "editprofile_admin",
"force": true,
"title": "Admin: Edit User"
}]
}Template Loading Modes:
// Mode 1: Template name (resolve จาก templates path ที่ตั้งค่าไว้)
{
"type": "modal",
"template": "editprofile"
}
// Mode 2: Template URL
{
"type": "modal",
"templateUrl": "/templates/custom/form.html"
}
// Mode 3: HTML content (ready to use)
{
"type": "modal",
"html": "<form>...</form>"
}
// Mode 4: Simple content (backward compatible)
{
"type": "modal",
"content": "<h2>Hello</h2>"
}Actions:
show/open: เปิด modalclose/hide: ปิด modal
Properties:
template(string): ชื่อ template ที่ resolve ด้วยNow.resolvePath(name, 'templates')templateUrl(string): URL ของ templatehtml(string): HTML สำเร็จรูปcontent(string): HTML content (เหมือน html)dataKey(string): Path ไปยัง data ใน response (เช่น "data", "data.user")optionsKey(string): Path ไปยัง optionstitle(string): ชื่อ modalclassName(string): CSS classwidth/maxWidth/height/maxHeight: ขนาด modalcloseButton(boolean): แสดงปุ่มปิด (default: true)backdrop(boolean): แสดง backdrop (default: true)keyboard(boolean): ปิดด้วย ESC (default: true)buttons(array): ปุ่มใน footerforce(boolean): บังคับใช้ config นี้ (override frontend)onShown/onHide(function): Callbacks
Auto-extraction:
data- ดึงจากresponse.data.dataอัตโนมัติoptions- ดึงจากresponse.data.optionsอัตโนมัติ
State Management:
- สร้าง
modalstate module ใน StateManager อัตโนมัติ - เข้าถึงได้ผ่าน
StateManager.state.modal - มี
formData,options,loading,errors
5. Redirect
เปลี่ยนเส้นทาง (URL)
{
"type": "redirect",
"url": "/dashboard",
"delay": 1000,
"target": "_self"
}Special URLs:
"reload": โหลด view ปัจจุบันใหม่ หรือรีโหลดตาราง/คอมโพเนนต์/ฟอร์มที่กำลังทำงานอยู่เมื่อ context รองรับ (ดู รูปแบบ Redirect สำหรับ Reload)"refresh": โหลดหน้าใหม่"back": ย้อนกลับประวัติ
Properties:
url: URL ปลายทางdelay: หน่วงเวลา (ms)target: "_self" | "_blank" | "_parent" | "_top"
6. Update
อัปเดตเนื้อหาของ element
{
"type": "update",
"target": "#result",
"content": "<p>ข้อมูลใหม่</p>",
"method": "html"
}Methods:
html: ตั้งค่า innerHTMLtext: ตั้งค่า textContentvalue: ตั้งค่า value (สำหรับ input)append: เพิ่มต่อท้ายprepend: เพิ่มไว้ข้างหน้าattr: ตั้งค่า attribute (ใช้คู่กับ propertyattr)
Properties เพิ่มเติมสำหรับ attr method:
attr/attribute: ชื่อ attribute ที่ต้องการตั้งค่า
ตัวอย่าง - อัปเดท Attribute:
{
"type": "update",
"target": "tr[data-id=\"123\"] button[data-action=\"active\"]",
"method": "attr",
"attr": "data-value",
"content": "1"
}ตัวอย่าง PHP - Toggle สถานะปุ่ม:
<?php
// Toggle สถานะ active และอัปเดทปุ่มในตาราง
return $this->successResponse([
'id' => $id,
'active' => $newActive,
'actions' => [
[
'type' => 'update',
'target' => 'tr[data-id="'.$id.'"] button[data-action="active"]',
'method' => 'attr',
'attr' => 'data-value',
'content' => (string) $newActive
]
]
], $newActive == 1 ? 'User enabled' : 'User disabled');ตัวอย่าง - อัปเดทหลาย Attributes (attrs method):
ใช้ attrs method สำหรับอัปเดทหลาย attributes/properties ในครั้งเดียว:
{
"type": "update",
"target": "tr[data-id=\"123\"] button[data-action=\"active\"]",
"method": "attrs",
"attrs": {
"data-value": "1",
"title": "คลิกเพื่อปิดใช้งาน",
"class": "btn-success active"
}
}ตัวอย่าง PHP - Toggle พร้อมอัปเดทหลายค่า:
<?php
return $this->successResponse([
'id' => $id,
'active' => $newActive,
'actions' => [
[
'type' => 'update',
'target' => 'tr[data-id="'.$id.'"] button[data-action="active"]',
'method' => 'attrs',
'attrs' => [
'data-value' => (string) $newActive,
'title' => $newActive == 1 ? 'คลิกเพื่อปิดใช้งาน' : 'คลิกเพื่อเปิดใช้งาน',
'class' => $newActive == 1 ? 'btn-success' : 'btn-danger'
]
]
]
], $newActive == 1 ? 'User enabled' : 'User disabled');การจัดการ Properties พิเศษ:
| Property | การจัดการ |
|---|---|
checked |
ตั้งค่า el.checked = true/false |
disabled |
ตั้งค่า el.disabled = true/false |
selected |
ตั้งค่า el.selected = true/false |
value |
ตั้งค่า el.value |
class / className |
ตั้งค่า el.className |
text / textContent |
ตั้งค่า el.textContent |
html / innerHTML |
ตั้งค่า el.innerHTML |
| อื่นๆ | ใช้ el.setAttribute(name, value) |
ตัวอย่าง Checkbox:
{
"type": "update",
"target": "#user-123-checkbox",
"method": "attrs",
"attrs": {
"checked": true,
"data-value": "1"
}
}7. Remove
ลบ element
{
"type": "remove",
"target": ".temp-message",
"animate": true
}Properties:
target: Selector หรือ elementanimate: แสดง animation ก่อนลบ
8. Class
จัดการ CSS class
{
"type": "class",
"target": "#myDiv",
"class": "active highlight",
"method": "add"
}Methods:
add: เพิ่ม classremove: ลบ classtoggle: สลับ class
9. Attribute
ตั้งค่า/ลบ attribute
{
"type": "attribute",
"target": "#myInput",
"name": "disabled",
"value": true
}Properties:
name: ชื่อ attributevalue: ค่า (ใช้nullเพื่อลบ attribute)
10. Event
ส่ง custom event
{
"type": "event",
"event": "data:updated",
"target": "#dataTable",
"data": {"id": 123},
"bubbles": true
}11. Callback
เรียก callback function
{
"type": "callback",
"function": "window.refreshTable",
"args": [123, "user"]
}12. Download
ดาวน์โหลดไฟล์
{
"type": "download",
"url": "/export/data.xlsx",
"filename": "report-2024.xlsx"
}13. Clipboard
คัดลอกข้อความไปยัง clipboard
{
"type": "clipboard",
"text": "https://example.com/share/123",
"notify": true,
"message": "คัดลอก URL แล้ว"
}14. Scroll
เลื่อนไปยัง element
{
"type": "scroll",
"target": "#top",
"behavior": "smooth",
"block": "center"
}15. Focus
โฟกัสที่ element
{
"type": "focus",
"target": "#emailInput",
"select": true
}Properties:
select: เลือกข้อความทั้งหมด (สำหรับ input)
16. รูปแบบ Redirect สำหรับ Reload
ใช้รีโหลด view ปัจจุบัน หรือตาราง/คอมโพเนนต์/ฟอร์มที่กำลังทำงานอยู่เมื่อ context ปัจจุบันมีตัวช่วยสำหรับ reload
{
"type": "redirect",
"url": "reload",
"target": "table"
}ค่าของ target:
target |
สิ่งที่ถูกรีโหลด |
|---|---|
"table" / "component" (หรือ scope) |
ตารางหรือคอมโพเนนต์ที่ response นี้เป็นของมัน |
| id ของตาราง | ตารางนั้น |
"form" (หรือ scope: "form") |
ฟอร์มที่ response นี้เป็นของมัน โดยโหลดจาก data-load-api ของฟอร์ม |
id ใน data-form |
ฟอร์มนั้น โดยโหลดจาก data-load-api ของฟอร์ม |
"page" / "window" |
ทั้งหน้า |
ถ้ารีโหลดเป้าหมายไม่ได้ (ไม่พบตารางหรือฟอร์มนั้น หรือฟอร์มไม่มี data-load-api) จะโหลดทั้งหน้าใหม่แทน
รีโหลดฟอร์มหลังบันทึก: ตารางที่ไม่มี data-source ของตัวเองได้แถวมาจากข้อมูลของฟอร์ม จึงมีแต่ฟอร์มที่ดึงแถวใหม่ได้ การรีโหลดฟอร์มจะเรียก FormManager.reloadFormData() ซึ่งดึง data-load-api ใหม่โดยไม่ใช้ cache แล้วเติมค่าลงฟิลด์ ตัวเลือก และทุกตารางที่ผูกกับฟอร์ม ล้างช่องเลือกไฟล์ และใช้ค่าใหม่นี้เป็นค่าตั้งต้นที่ reset จะย้อนกลับไป
// redirectResponse($url, $message, $code, $delay, $target) เพิ่ม notification ให้ด้วย
return $this->redirectResponse('reload', 'Saved successfully', 200, 0, 'form');17. Form Errors
แสดง error บนฟิลด์ฟอร์ม พร้อม highlight และแสดง notification รวม
{
"type": "formErrors",
"errors": {
"username": "ชื่อผู้ใช้นี้มีอยู่แล้ว",
"email": "อีเมลไม่ถูกต้อง"
},
"notification": true,
"focusFirst": true,
"level": "error"
}Properties:
| Property | Type | Default | คำอธิบาย |
|---|---|---|---|
errors |
Object | {} |
Object ของ fieldId: errorMessage |
notification |
Boolean หรือ "fallback" |
true |
true แสดง errors ทั้งหมด, false ปิด notification, "fallback" แสดงเฉพาะ errors ที่ไม่มี inline result |
focusFirst |
Boolean | true |
โฟกัสไปที่ฟิลด์แรกที่มี error |
level |
String | "error" |
ระดับ notification (error, warning, info) |
การแสดงผล:
- Highlight: เพิ่ม class
invalidให้ทุกฟิลด์ที่มี error - Notification: toast เดียว บรรทัดละหนึ่ง error เขียนเป็น
Label: messageเมื่อฟิลด์มี label - Fallback: ตั้ง
notification: "fallback"เพื่อ toast เฉพาะ field ที่ไม่พบในฟอร์มหรือไม่มี#result_{field}/data-result
ตัวอย่าง PHP:
<?php
// ใช้ errors โดยตรงได้ทั้ง FormManager และปุ่ม requestApi ที่อยู่ในฟอร์ม
return $this->formErrorResponse([
'editprofile_username' => 'มีอยู่แล้ว',
'editprofile_email' => 'รูปแบบไม่ถูกต้อง'
], 400);
// วิธีที่ 2: ใช้ actions (ควบคุมได้มากกว่า)
echo json_encode([
'success' => false,
'actions' => [
[
'type' => 'formErrors',
'errors' => [
'editprofile_username' => 'มีอยู่แล้ว'
]
]
]
]);หมายเหตุ: ชื่อ field ใน
errorsต้องตรงกับidหรือnameของ input element ใน HTML
requestApiส่ง form context ให้ResponseHandlerจึง highlight field จาก top-levelerrorsได้เมื่อหา field พบ และรวม errors ทั้งหมดแสดงใน toast เสมอ โดย field ที่พบใช้รูปแบบชื่อฟิลด์: ข้อความ; field ที่ไม่มีในฟอร์มจะแสดงเฉพาะข้อความ error
18. Form Patch
อัปเดตค่าฟิลด์และ option source ของอีกฟอร์มหนึ่งได้ทันที โดยไม่ต้องเขียน JavaScript เฉพาะหน้าจอเพิ่ม
{
"type": "form",
"form": "tro-job",
"action": "patch",
"fields": {
"customer_id": "15",
"vehicle_id": "0"
},
"options": {
"customers": {
"mode": "merge",
"items": [
{ "value": "15", "text": "ลูกค้าใหม่ (0812345678)" }
]
},
"vehicles": {
"mode": "replace",
"items": [
{ "value": "0", "text": "กรุณาเลือก" }
]
}
},
"focus": "vehicle_id"
}Properties:
| Property | Type | Default | คำอธิบาย |
|---|---|---|---|
form / formId |
String | ฟอร์มปัจจุบัน | data-form ของฟอร์มเป้าหมาย หรือ CSS selector |
action |
String | "patch" |
ปัจจุบันรองรับ patch / update |
fields |
Object | {} |
ค่าฟิลด์ที่อ้างด้วย name หรือ id |
options |
Object | {} |
ชุดตัวเลือกที่อ้างด้วย data-options-key |
options.<key>.mode |
String | "replace" |
replace, merge, append, clear |
focus |
String | null |
ชื่อฟิลด์/id ที่ต้องการโฟกัสหลังอัปเดต |
trigger |
Array | [] |
รายชื่อฟิลด์ที่จะ dispatch change หลังอัปเดต |
เหมาะกับกรณีเปิด modal เพื่อเพิ่มข้อมูลที่เกี่ยวข้อง เช่น customer หรือ vehicle แล้วต้องนำค่ากลับมาเติมในฟอร์มหลักทันที
flow แบบ zero-JavaScript ที่ใช้บ่อย:
- ปุ่มใช้
data-action="click.prevent:requestApi"เพื่อเรียก API - API แรกตอบกลับด้วย
modalaction เพื่อเปิดฟอร์มของข้อมูลที่เกี่ยวข้อง - หลัง submit endpoint สำหรับบันทึกจะตอบกลับด้วย
notification,formและmodal
ตัวอย่าง response หลังบันทึก:
<?php
return $this->successResponse([
'actions' => [
[
'type' => 'notification',
'level' => 'success',
'message' => 'Saved successfully'
],
[
'type' => 'form',
'form' => 'tro-job',
'action' => 'patch',
'fields' => [
'customer_id' => (string) $id,
'vehicle_id' => '0'
],
'options' => [
'customers' => [
'mode' => 'merge',
'items' => [$customerOption]
],
'vehicles' => [
'mode' => 'replace',
'items' => \Tro\Job\Model::getVehicleOptions($id)
]
],
'focus' => 'vehicle_id'
],
[
'type' => 'modal',
'action' => 'close'
]
]
], 'Saved successfully');pattern นี้ช่วยตัด callback JavaScript เฉพาะหน้าออกจากงานประเภท “เพิ่มข้อมูลใน modal แล้ว patch ฟอร์มแม่” ได้
19. Eval (ไม่แนะนำ)
รันโค้ด JavaScript (ต้องเปิดใช้ allowEval)
{
"type": "eval",
"code": "console.log('Hello');"
}⚠️ คำเตือน: ใช้เฉพาะเมื่อจำเป็นจริงๆ และควบคุม input อย่างเข้มงวด
ตัวอย่างการใช้งาน
1. การบันทึกข้อมูลพื้นฐาน
API Response (PHP):
<?php
echo json_encode([
'success' => true,
'message' => 'บันทึกข้อมูลสำเร็จ',
'data' => ['id' => 123],
'actions' => [
[
'type' => 'notification',
'level' => 'success',
'message' => 'บันทึกข้อมูลผู้ใช้เรียบร้อย'
],
[
'type' => 'redirect',
'url' => '/users',
'delay' => 1500
]
]
]);JavaScript:
document.querySelector('#saveBtn').addEventListener('click', async () => {
const formData = new FormData(document.querySelector('#userForm'));
const response = await fetch('/api/users/save', {
method: 'POST',
body: formData
});
const json = await response.json();
await ResponseHandler.process(json);
// จะแสดง notification และ redirect อัตโนมัติ
});2. Form Validation
API Response:
{
"success": false,
"message": "ข้อมูลไม่ถูกต้อง",
"actions": [
{
"type": "notification",
"level": "error",
"message": "กรุณากรอกข้อมูลให้ครบถ้วน"
},
{
"type": "class",
"target": "#emailInput",
"class": "error",
"method": "add"
},
{
"type": "focus",
"target": "#emailInput",
"select": true
}
]
}3. การลบข้อมูลพร้อม Confirm
JavaScript:
async function deleteUser(userId) {
const response = await fetch(`/api/users/${userId}`, {
method: 'DELETE'
});
const json = await response.json();
await ResponseHandler.process(json);
}API Response:
{
"success": true,
"actions": [
{
"type": "notification",
"level": "success",
"message": "ลบข้อมูลแล้ว"
},
{
"type": "remove",
"target": "#user-row-123",
"animate": true
},
{
"type": "event",
"event": "user:deleted",
"data": {"id": 123}
}
]
}4. Hybrid Modal - Frontend-Driven (แนะนำ)
ขั้นตอนที่ 1: กำหนด Modal Config ใน HTML
<table data-table="users"
data-source="api/v1/users"
data-row-actions='{
"edit": {
"element": "button",
"className": "btn btn-success icon-edit",
"title": "Edit",
"modal": {
"template": "editprofile",
"title": "Edit User Profile",
"className": "large-modal"
}
},
"view": {
"element": "button",
"className": "btn icon-published1",
"modal": {
"template": "viewprofile",
"className": "readonly-modal"
}
}
}'>
</table>ขั้นตอนที่ 2: สร้าง Template (/templates/modals/editprofile.html)
<div class="modal-form-container">
<form id="editProfileForm" class="form">
<input type="hidden" name="id" data-model="id">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name"
data-model="name" data-text="name" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email"
data-model="email" data-text="email">
</div>
<div class="form-group">
<label for="province">Province</label>
<select id="province" name="provinceID" data-model="provinceID">
<option value="">Select Province</option>
<template data-for="province in options.provinces">
<option data-attr="value:province.id"
data-text="province.name"
data-attr="selected:provinceID==province.id">
</option>
</template>
</select>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Save</button>
<button type="button" class="btn btn-secondary"
onclick="window.modal?.hide()">Cancel</button>
</div>
</form>
</div>ขั้นตอนที่ 3: API Response (Simple)
// PHP API Endpoint
public function action(Request $request) {
$action = $request->post('action')->toString();
if ($action === 'edit') {
$user = $db->first('user', [['id', $request->post('id')->toInt()]]);
$provinces = $db->select('province')->execute()->fetchAll();
// Simple response - Frontend controls modal
return $this->successResponse([
'data' => $user,
'options' => [
'provinces' => $provinces,
'genders' => [
['value' => 'm', 'label' => 'Male'],
['value' => 'f', 'label' => 'Female']
]
]
], null);
}
}{
"success": true,
"data": {
"id": 123,
"name": "John Doe",
"email": "john@example.com",
"provinceID": 10
},
"options": {
"provinces": [
{"id": 10, "name": "Bangkok"},
{"id": 20, "name": "Chiang Mai"}
],
"genders": [
{"value": "m", "label": "Male"},
{"value": "f", "label": "Female"}
]
}
}ผลลัพธ์:
- ✅ Modal เปิดอัตโนมัติพร้อม template
editprofile - ✅ ข้อมูล user ถูก bind เข้า form fields
- ✅ Select options ถูกสร้างจาก
options.provinces - ✅ Form พร้อมใช้งานทันที
5. Modal - API Force Override (Role-Based UI)
Use Case: Admin และ User ต้องใช้ template ต่างกัน
// PHP API
public function action(Request $request) {
$user = $this->getUser();
$targetUser = $db->first('user', [['id', $request->post('id')->toInt()]]);
// Admin gets different template
$template = ($user->status == 1) ? 'editprofile_admin' : 'editprofile';
return $this->successResponse([
'data' => $targetUser,
'actions' => [[
'type' => 'modal',
'template' => $template,
'force' => true, // Override frontend config
'title' => $user->status == 1 ? 'Admin: Edit User' : 'Edit Profile'
]]
], null);
}6. Modal - HTML Content (Backward Compatible)
API Response:
{
"success": true,
"data": {
"name": "สมชาย ใจดี",
"email": "somchai@example.com"
},
"actions": [
{
"type": "modal",
"html": "<div class='user-detail'><h2>สมชาย ใจดี</h2><p>Email: somchai@example.com</p></div>",
"className": "user-modal",
"buttons": [
{
"text": "แก้ไข",
"className": "button primary",
"onclick": "editUser(123)"
},
{
"text": "ปิด",
"className": "button",
"onclick": "modal.hide()"
}
]
}
]
}5. Multiple Actions ต่อเนื่อง
API Response:
{
"success": true,
"actions": [
{
"type": "notification",
"level": "success",
"message": "อัปโหลดสำเร็จ"
},
{
"type": "update",
"target": "#fileList",
"content": "<li>document.pdf</li>",
"method": "append"
},
{
"type": "class",
"target": "#uploadBtn",
"class": "loading",
"method": "remove"
},
{
"type": "event",
"event": "file:uploaded",
"data": {"filename": "document.pdf"}
}
]
}6. Custom Action Handler
JavaScript:
// ลงทะเบียน custom action
ResponseHandler.registerHandler('animateProgress', (action) => {
const progressBar = document.querySelector(action.target);
const targetValue = action.value;
let current = 0;
const interval = setInterval(() => {
current += 5;
progressBar.style.width = `${current}%`;
progressBar.textContent = `${current}%`;
if (current >= targetValue) {
clearInterval(interval);
}
}, 50);
});
// ใช้งาน
await ResponseHandler.executeAction({
type: 'animateProgress',
target: '#progress',
value: 75
});API Response:
{
"success": true,
"actions": [
{
"type": "animateProgress",
"target": "#uploadProgress",
"value": 100
},
{
"type": "notification",
"level": "success",
"message": "อัปโหลดเสร็จสิ้น"
}
]
}Best Practices สำหรับ Hybrid Modal
1. ใช้ Frontend-Driven เป็นหลัก (90%)
เหตุผล:
- แยก UI logic จาก business logic
- ง่ายต่อการ maintain (เปลี่ยน UI ไม่ต้องแก้ backend)
- Developer frontend สามารถทำงานอิสระ
- ลด coupling ระหว่าง frontend และ backend
ตัวอย่าง:
<!-- กำหนดทุกอย่างใน HTML -->
<button data-row-actions='{
"edit": {
"modal": {
"template": "editprofile",
"title": "แก้ไขข้อมูล",
"className": "large-modal"
}
},
"delete": {
"modal": {
"template": "confirm-delete",
"title": "ยืนยันการลบ"
}
}
}'>Actions</button>// API แค่ส่งข้อมูล
echo json_encode([
'success' => true,
'data' => $userData,
'options' => $dropdowns
]);2. ใช้ API Force Override เฉพาะกรณีพิเศษ (10%)
Use Cases ที่เหมาะสม:
- Role-based UI (Admin ดู template แบบหนึ่ง, User ดูอีกแบบ)
- Feature flags / A/B testing
- Dynamic workflow (ขั้นตอนแตกต่างกันตามสถานะ)
- Emergency override (ปิดระบบบางส่วนแบบ real-time)
ตัวอย่าง:
// กรณีพิเศษ: role-based
$template = $isAdmin ? 'admin-edit-profile' : 'user-edit-profile';
echo json_encode([
'success' => true,
'actions' => [
[
'type' => 'modal',
'template' => $template, // Force override
'force' => true,
'data' => $userData
]
]
]);3. ใช้ Auto-Extraction สำหรับ Simple Cases
ข้อดี:
- ลด boilerplate code
- API response กระชับ
- ทำงานได้ทันทีโดยไม่ต้อง config
ตัวอย่าง:
// Simple response - auto extract
echo json_encode([
'success' => true,
'data' => [
'id' => 1,
'name' => 'John',
'email' => 'john@example.com'
],
'options' => [
'roles' => ['admin', 'user', 'guest'],
'status' => ['active', 'inactive']
]
]);// Frontend config - ระบบจะ extract data.data และ data.options อัตโนมัติ
<button data-row-actions='{
"edit": {
"modal": {
"template": "editprofile"
}
}
}'>Edit</button>4. ใช้ Explicit Data สำหรับ Complex Cases
Use Cases:
- ข้อมูลซ้อน (nested objects/arrays)
- ต้องการ transform data ก่อนส่ง modal
- ข้อมูลมาจากหลาย source
- ต้องการควบคุมโครงสร้างแน่นอน
ตัวอย่าง:
// Complex data structure
echo json_encode([
'success' => true,
'actions' => [
[
'type' => 'modal',
'template' => 'advanced-editor',
'data' => [
'user' => $userData,
'permissions' => $userPermissions,
'history' => $recentActivity,
'metadata' => [
'lastLogin' => $lastLogin,
'createdBy' => $creator
]
],
'options' => [
'roles' => $availableRoles,
'departments' => $departments,
'groups' => $groups
]
]
]
]);5. จัดการ Error อย่างถูกต้อง
Pattern ที่แนะนำ:
try {
// Validation
if (empty($data['email'])) {
throw new Exception('กรุณากรอกอีเมล');
}
// Business logic
$result = $model->save($data);
echo json_encode([
'success' => true,
'actions' => [
['type' => 'notification', 'level' => 'success', 'message' => 'บันทึกสำเร็จ'],
['type' => 'modal', 'action' => 'close'],
['type' => 'redirect', 'url' => 'reload', 'target' => 'table']
]
]);
} catch (Exception $e) {
echo json_encode([
'success' => false,
'actions' => [
[
'type' => 'modal',
'html' => '<div class="error-modal">' . htmlspecialchars($e->getMessage()) . '</div>',
'title' => 'เกิดข้อผิดพลาด',
'className' => 'error-modal'
]
]
]);
}6. Template Organization
โครงสร้างที่แนะนำ:
templates/
├── modals/
│ ├── crud/ # CRUD operations
│ │ ├── edit-user.html
│ │ ├── create-user.html
│ │ └── delete-confirm.html
│ ├── forms/ # Complex forms
│ │ ├── advanced-search.html
│ │ └── bulk-import.html
│ ├── viewers/ # View-only modals
│ │ ├── user-profile.html
│ │ └── activity-log.html
│ └── common/ # Reusable components
│ ├── confirm.html
│ └── alert.htmlNaming Convention:
- ใช้ kebab-case:
edit-user.html,confirm-delete.html - ชื่อบอกหน้าที่ชัด:
user-advanced-search.html(ไม่ใช่modal1.html) - Group ตาม feature:
product-edit.html,product-create.html,product-delete.html
7. Data Binding Best Practices
✅ Good:
<!-- ใช้ data attributes สำหรับ dynamic content -->
<input type="text" data-model="name" />
<span data-text="email"></span>
<img data-attr="src:avatarUrl" alt="Avatar" />
<!-- Loop กับ conditional -->
<select data-model="roleID">
<option
data-for="role in options.roles"
data-attr="value:role.id,selected:roleID==role.id"
data-text="role.name">
</option>
</select>❌ Avoid:
<!-- ไม่ควร inline ค่าตรงๆ -->
<input type="text" value="<?= $name ?>" />
<span><?= $email ?></span>
<!-- ไม่ควร mix server-side และ client-side rendering -->
<select>
<?php foreach ($roles as $role): ?>
<option value="<?= $role['id'] ?>"><?= $role['name'] ?></option>
<?php endforeach; ?>
</select>8. State Management
ใช้ StateManager สำหรับ modal state:
// ใน template
const modal = StateManager.getModule('modal');
// Listen to changes
modal.watch('data.status', (newStatus) => {
// Update UI based on status
});
// Update state
modal.set('data.notes', newNotes);ข้อดี:
- Reactive updates (เปลี่ยน state, UI update อัตโนมัติ)
- Easy debugging (ดู state ทั้งหมดได้)
- Undo/Redo implementation ได้ง่าย
9. Performance Optimization
Lazy Load Templates:
// กำหนด template path แทนที่จะโหลดทันที
ResponseHandler.config.templateBasePath = '/templates/modals/';
// Template จะโหลดตอนเปิด modal เท่านั้น
<button data-row-actions='{
"edit": {
"modal": {
"template": "heavy-editor" // โหลดแค่ตอนต้องใช้
}
}
}'>Edit</button>Cache Templates:
// TemplateManager cache อัตโนมัติ
// เปิด modal ครั้งที่ 2+ จะเร็วกว่าครั้งแรก10. Security Considerations
ทำ Validation ทั้ง Client และ Server:
// Client validation
async function submitForm(data) {
if (!data.email || !data.email.includes('@')) {
NotificationManager.error('กรุณากรอกอีเมลที่ถูกต้อง');
return;
}
const response = await fetch('/api/save', {...});
await ResponseHandler.process(response);
}// Server validation (ต้องมี!)
if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
throw new Exception('รูปแบบอีเมลไม่ถูกต้อง');
}Sanitize Output:
<!-- ใน template ใช้ data attributes (auto-escaped) -->
<div data-text="userInput"></div>
<!-- ถ้าต้องการ HTML ให้ระบุชัด -->
<div data-html="trustedHtml"></div>ไม่ควร:
// ❌ ไม่ใช้ eval
{
"type": "eval",
"code": userInput // Dangerous!
}
// ❌ ไม่ใช้ innerHTML กับ user input
element.innerHTML = userInput;Common Pitfalls (Hybrid Modal)
1. ลืมตั้งค่า modalConfig
อาการ: Modal ไม่เปิด หรือเปิดแต่ไม่มีข้อมูล
สาเหตุ:
<!-- ❌ ขาด modal config -->
<button data-row-actions='{"edit": {}}'>Edit</button>แก้ไข:
<!-- ✅ ระบุ modal config -->
<button data-row-actions='{
"edit": {
"modal": {
"template": "editprofile"
}
}
}'>Edit</button>2. Template Path ผิด
อาการ: Console error "Template not found"
สาเหตุ:
// ❌ template ชื่อผิดหรือไม่มีไฟล์
{
"modal": {
"template": "edit-profile" // ไฟล์จริง: editprofile.html
}
}แก้ไข:
// ✅ ตรวจสอบชื่อไฟล์
{
"modal": {
"template": "editprofile" // ตรงกับ /templates/modals/editprofile.html
}
}
// หรือใช้ full path
{
"modal": {
"templateUrl": "/templates/modals/editprofile.html"
}
}3. Data Structure ไม่ตรง
อาการ: Form แสดงผลแต่ไม่มีข้อมูล
สาเหตุ:
// ❌ โครงสร้างไม่ตรงกับ auto-extraction
echo json_encode([
'success' => true,
'user' => $userData, // ไม่ใช่ 'data'
'dropdowns' => $options // ไม่ใช่ 'options'
]);แก้ไข:
// ✅ Option 1: ใช้ auto-extraction structure
echo json_encode([
'success' => true,
'data' => $userData,
'options' => $options
]);
// ✅ Option 2: ระบุ data ชัดเจน
echo json_encode([
'success' => true,
'actions' => [
[
'type' => 'modal',
'template' => 'editprofile',
'data' => $userData,
'options' => $options
]
]
]);4. Modal ไม่ปิดหลัง Submit
อาการ: Submit แล้ว modal ยังเปิดอยู่
สาเหตุ:
// ❌ ลืม action สำหรับปิด modal
echo json_encode([
'success' => true,
'actions' => [
['type' => 'notification', 'message' => 'บันทึกสำเร็จ']
// ขาด action ปิด modal
]
]);แก้ไข:
// ✅ เพิ่ม action ปิด modal และรีโหลดตาราง
echo json_encode([
'success' => true,
'actions' => [
['type' => 'notification', 'level' => 'success', 'message' => 'บันทึกสำเร็จ'],
['type' => 'modal', 'action' => 'close'],
['type' => 'redirect', 'url' => 'reload', 'target' => 'table']
]
]);5. Context ไม่ถูกส่งต่อ
อาการ: modalConfig ไม่ทำงาน
สาเหตุ:
// ❌ ส่ง request โดยไม่มี context
await HTTPManager.post('/api/save', data);แก้ไข:
// ✅ TableManager จัดการ context ให้อัตโนมัติ
// แต่ถ้าเรียกเอง ต้องส่ง context
const modalConfig = {
template: 'editprofile',
title: 'แก้ไขข้อมูล'
};
const response = await HTTPManager.post('/api/save', data);
await ResponseHandler.process(response, { modalConfig });6. force Flag ผิดที่
อาการ: Frontend config ถูก override ไม่ตรงตามต้องการ
สาเหตุ:
// ❌ ใช้ force โดยไม่จำเป็น
echo json_encode([
'success' => true,
'actions' => [
[
'type' => 'modal',
'template' => 'editprofile',
'force' => true // ทำให้ frontend config ไม่ทำงาน
]
]
]);แก้ไข:
// ✅ ใช้ force เฉพาะเมื่อจำเป็น
echo json_encode([
'success' => true,
'data' => $userData,
'options' => $options
// ไม่มี actions - ให้ frontend config ทำงาน
]);
// หรือใช้ suggest แทน
echo json_encode([
'success' => true,
'actions' => [
[
'type' => 'modal',
'template' => 'editprofile', // suggest (ไม่มี force)
'data' => $userData
]
]
]);7. Toast เด้งซ้อนกับ Modal
อาการ: กดปุ่มในตารางแล้ว modal เปิดขึ้นมาพร้อม toast "OK" หรือ "Success" ทั้งที่ไม่มีอะไรต้องแจ้ง
สาเหตุ: message ระดับบนของ response ไม่ใช่ค่าว่าง เมื่อ response ไม่มี action ชนิด notification หรือ alert TableManager จะแสดง message นั้นเป็น toast ให้เอง (row action, bulk action และปุ่ม filter action) ส่วน successResponse() ของ Kotchasan ใช้ 'Success' เป็นค่าเริ่มต้น การละ argument ไว้จึงได้ toast เหมือนกัน
// ❌ modal เปิดพร้อม toast "OK"
return $this->successResponse([
'data' => $item,
'actions' => [
['type' => 'modal', 'action' => 'open', 'template' => 'demo/record.html']
]
], 'OK');
// ❌ ละ message ไว้ — ค่าเริ่มต้น 'Success' ก็กลายเป็น toast
return $this->successResponse(['data' => $item, 'options' => $options]);แก้ไข:
// ✅ response ที่แค่เปิด modal ให้ส่ง message เป็น null
return $this->successResponse([
'data' => $item,
'actions' => [
['type' => 'modal', 'action' => 'open', 'template' => 'demo/record.html']
]
], null);ใส่ข้อความเฉพาะเมื่อต้องการให้ผู้ใช้เห็น toast จริง ๆ เช่น response ที่ปิด modal หลังบันทึกสำเร็จ
State Properties
| Property | Type | Description |
|---|---|---|
state.initialized |
Boolean | สถานะการ initialize |
state.actionHandlers |
Map | Collection ของ action handlers ทั้งหมด |
ตัวอย่าง:
// ตรวจสอบว่า initialize แล้วหรือยัง
if (ResponseHandler.state.initialized) {
console.log('Ready to use');
}
// ดูรายการ handlers ทั้งหมด
console.log(ResponseHandler.state.actionHandlers.keys());Integration
ใช้กับ HTTPManager
const result = await HTTPManager.post('/api/save', data);
await ResponseHandler.process(result);ใช้กับ TableManager
const table = TableManager.create({
source: '/api/users',
onActionComplete: async (response) => {
await ResponseHandler.process(response);
}
});ใช้กับ Form Submit
document.querySelector('form').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const response = await fetch('/api/submit', {
method: 'POST',
body: formData
});
const json = await response.json();
await ResponseHandler.process(json, {
form: e.target
});
});Best Practices
1. ใช้ Actions แทนโค้ดซ้ำซ้อน
❌ ไม่แนะนำ:
const response = await fetch('/api/save', ...);
const json = await response.json();
if (json.success) {
NotificationManager.success('บันทึกสำเร็จ');
setTimeout(() => {
window.location.href = '/users';
}, 1000);
} else {
NotificationManager.error(json.message);
}✅ แนะนำ:
const response = await fetch('/api/save', ...);
const json = await response.json();
await ResponseHandler.process(json);// API Response
echo json_encode([
'success' => true,
'actions' => [
['type' => 'notification', 'level' => 'success', 'message' => 'บันทึกสำเร็จ'],
['type' => 'redirect', 'url' => '/users', 'delay' => 1000]
]
]);2. ใช้ Context สำหรับ Shared Data
await ResponseHandler.process(response, {
tableId: 'users-table',
formId: 'user-form',
currentUserId: 123
});3. สร้าง Custom Actions สำหรับพฤติกรรมพิเศษ
// Action สำหรับ refresh table
ResponseHandler.registerHandler('refreshTable', async (action, context) => {
const tableId = action.tableId || context.tableId;
const table = document.querySelector(`#${tableId}`);
if (table && table.tableManager) {
await table.tableManager.reload();
}
});4. ใช้ Condition เพื่อควบคุมการทำงาน
{
"type": "redirect",
"url": "/admin",
"condition": false // ข้าม action นี้
}5. เรียงลำดับ Actions อย่างมีเหตุผล
{
"actions": [
{"type": "notification", "message": "กำลังบันทึก..."},
{"type": "update", "target": "#result", "content": "..."},
{"type": "notification", "level": "success", "message": "บันทึกเสร็จ"},
{"type": "redirect", "url": "/list", "delay": 1000}
]
}ข้อผิดพลาดที่พบบ่อย
1. ลืม await
❌ ผิด:
ResponseHandler.process(response); // ไม่รอให้เสร็จ
console.log('Done'); // จะทำงานทันที✅ ถูก:
await ResponseHandler.process(response);
console.log('Done'); // จะทำงานหลัง actions เสร็จ2. Action Type ผิด
// ❌ ไม่มี action type "noti"
{
"type": "noti",
"message": "Hello"
}
// ✅ ใช้ "notification"
{
"type": "notification",
"message": "Hello"
}3. ไม่ระบุ target สำหรับ DOM Actions
// ❌ ขาด target
{
"type": "update",
"content": "Hello"
}
// ✅ มี target
{
"type": "update",
"target": "#result",
"content": "Hello"
}4. ใช้ eval โดยไม่เปิดใช้งาน
// ต้องเปิดก่อนใช้ (ไม่แนะนำ)
ResponseHandler.init({
allowEval: true
});Performance
1. ใช้ Batch Actions
แทนที่จะเรียก executeAction หลายครั้ง ให้ใช้ process กับ actions array:
// ✅ ดีกว่า
await ResponseHandler.process({
actions: [action1, action2, action3]
});
// ❌ ช้ากว่า
await ResponseHandler.executeAction(action1);
await ResponseHandler.executeAction(action2);
await ResponseHandler.executeAction(action3);2. ใช้ delay อย่างเหมาะสม
{
"type": "redirect",
"url": "/dashboard",
"delay": 1000 // ให้เวลาผู้ใช้อ่าน notification
}3. Cleanup เมื่อไม่ใช้
// ถ้าไม่ต้องการ auto-init
ResponseHandler.config.autoHandleFormResponses = false;Security
1. ปิด eval (Default)
// ✅ ปลอดภัย - eval ปิดอยู่
ResponseHandler.config.allowEval = false;2. Validate Actions ที่ API
// API ควรกรอง actions ที่อนุญาต
$allowedActions = ['notification', 'redirect', 'modal'];
foreach ($actions as $action) {
if (!in_array($action['type'], $allowedActions)) {
// ไม่ส่ง action ที่ไม่ปลอดภัย
continue;
}
}3. Sanitize Content
// สำหรับ modal content หรือ update content
$content = htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');4. ใช้ CSP Headers
Content-Security-Policy: script-src 'self' 'unsafe-inline'5. ตรวจสอบ Callback Functions
ResponseHandler.registerHandler('callback', (action) => {
const allowedFunctions = ['refreshTable', 'closeModal'];
if (!allowedFunctions.includes(action.function)) {
console.warn('Callback not allowed:', action.function);
return;
}
// ทำงานต่อ...
});Browser Compatibility
| Browser | Version | Support |
|---|---|---|
| Chrome | 60+ | ✅ Full |
| Firefox | 55+ | ✅ Full |
| Safari | 12+ | ✅ Full |
| Edge | 79+ | ✅ Full |
| IE | ❌ | Not Supported |
Features ที่ต้องการ:
- ES6 (async/await, arrow functions, Map)
- Fetch API
- CustomEvent API
- Clipboard API (สำหรับ clipboard action)
Polyfills:
<!-- สำหรับ browser เก่า -->
<script src="https://cdn.polyfill.io/v3/polyfill.min.js"></script>API อ้างอิง
Configuration Properties
{
debug: Boolean,
defaultRedirectDelay: Number,
defaultNotificationDuration: Number,
allowEval: Boolean,
autoHandleFormResponses: Boolean
}Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
init(options) |
options?: Object |
ResponseHandler |
เริ่มต้นการทำงาน |
process(response, context) |
response: Object, context?: Object |
Promise<Object> |
ประมวลผล response |
executeAction(action, context) |
action: Object, context?: Object |
Promise<void> |
ทำงาน action เดี่ยว |
registerHandler(type, handler) |
type: String, handler: Function |
void |
ลงทะเบียน handler |
executeCallback(fn, args) |
fn: Function\|String, args?: Array |
void |
เรียก callback |
emit(eventName, data) |
eventName: String, data?: Any |
void |
ส่ง event |
Action Types
| Type | Description | Main Properties |
|---|---|---|
notification |
แสดง notification | level, message, duration |
alert |
แสดง alert dialog | message |
confirm |
แสดง confirm dialog | message, onConfirm |
modal |
เปิด/ปิด modal | action, content, buttons |
redirect |
เปลี่ยนเส้นทาง | url, delay, target |
update |
อัปเดต element | target, content, method |
remove |
ลบ element | target, animate |
class |
จัดการ CSS class | target, class, method |
attribute |
ตั้งค่า attribute | target, name, value |
event |
ส่ง custom event | event, target, data |
callback |
เรียก function | function, args |
download |
ดาวน์โหลดไฟล์ | url, filename |
clipboard |
คัดลอกไปยัง clipboard | text, notify |
scroll |
เลื่อนไปยัง element | target, behavior |
focus |
โฟกัส element | target, select |
form |
อัปเดตค่าฟอร์ม/option | form, fields, options |
eval |
รันโค้ด (ไม่แนะนำ) | code |
สรุป
ResponseHandler ช่วย:
- ลดโค้ดซ้ำซ้อน - ให้ API ควบคุม UI behavior
- มาตรฐานเดียวกัน - API response format เดียวกันทั้งโปรเจค
- ยืดหยุ่น - สร้าง custom actions ได้
- ปลอดภัย - Security-first design
- ง่ายต่อการดูแล - เปลี่ยนพฤติกรรมที่เซิร์ฟเวอร์ ไม่ต้องแก้ไข JavaScript
เหตุการณ์
| Event | เมื่อเกิด | Detail |
|---|---|---|
modal:shown |
เปิด modal จาก action ใน response | {template, data, options} |