Now.js Framework Documentation
data-if
data-if
ภาพรวม
data-if แสดงผล elements ตามเงื่อนไข Elements จะถูกลบออกจาก DOM ทั้งหมดเมื่อเงื่อนไขเป็น false
ใช้เมื่อ:
- แสดง/ซ่อนเนื้อหาตามสถานะข้อมูล
- แสดง components แบบมีเงื่อนไข
- สลับส่วนต่างๆ ตามสิทธิ์ผู้ใช้
ทำไมต้องใช้:
- ✅ ลบออกจาก DOM ทั้งหมด (ไม่ใช่แค่ซ่อน)
- ✅ รองรับ expressions ซับซ้อน
- ✅ รองรับ animation สำหรับ transitions
- ✅ Cleanup nested directives อัตโนมัติ
การใช้งานพื้นฐาน
เงื่อนไขอย่างง่าย
<div data-if="isLoggedIn">
ยินดีต้อนรับกลับมา!
</div>กับข้อมูล:
{ isLoggedIn: true }ผลลัพธ์เมื่อ true:
<div>ยินดีต้อนรับกลับมา!</div>ผลลัพธ์เมื่อ false:
<!-- Element ถูกลบออกจาก DOM -->Syntax
<element data-if="expression">| ส่วน | คำอธิบาย |
|---|---|
expression |
JavaScript expression ที่ประเมินเป็น truthy/falsy |
ฟีเจอร์
1. เงื่อนไข Boolean
<div data-if="isVisible">เนื้อหาที่มองเห็น</div>
<div data-if="!isHidden">เนื้อหาที่ไม่ซ่อน</div>2. การมีอยู่ของ Property
<div data-if="user.avatar">
<img data-attr="src:user.avatar">
</div>3. Comparison Operators
<div data-if="count > 0">คุณมีรายการ</div>
<div data-if="status === 'active'">สถานะ active</div>
<div data-if="items.length >= 5">มีหลายรายการ</div>4. Logical Operators
<div data-if="isLoggedIn && isAdmin">แผงควบคุมแอดมิน</div>
<div data-if="hasError || isLoading">แสดงข้อความ</div>
<div data-if="!isDisabled">เนื้อหาที่เปิดใช้งาน</div>5. Expressions ซับซ้อน
<div data-if="user.role === 'admin' && permissions.includes('edit')">
ปุ่มแก้ไข
</div>6. การปฏิเสธ (Negation)
<div data-if="!isLoggedIn">
<a href="/login">กรุณาเข้าสู่ระบบ</a>
</div>รูปแบบ If-Else
เนื่องจากไม่มี data-else ให้ใช้เงื่อนไขปฏิเสธ:
<div data-if="isLoggedIn">
<p>ยินดีต้อนรับ <span data-text="user.name"></span>!</p>
</div>
<div data-if="!isLoggedIn">
<a href="/login">เข้าสู่ระบบ</a>
</div>หลายเงื่อนไข (รูปแบบ else-if)
<div data-if="status === 'loading'">
<div class="spinner">กำลังโหลด...</div>
</div>
<div data-if="status === 'error'">
<div class="error">เกิดข้อผิดพลาด</div>
</div>
<div data-if="status === 'success'">
<div class="content" data-text="data"></div>
</div>การรองรับ Animation
เพิ่ม class animated สำหรับ enter/exit animations:
<div data-if="showModal" class="modal animated">
<div class="modal-content">
เนื้อหา Modal
</div>
</div>CSS:
.modal.animated {
animation: fadeIn 0.3s ease;
}
.modal.fade-out {
animation: fadeOut 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}เมื่อมี class animated:
- แสดง: เพิ่ม
fade-in,animate-inclasses - ซ่อน: เพิ่ม
fade-out,animate-outclasses, รอ animation
ตัวอย่างขั้นสูง
การยืนยันตัวตนผู้ใช้
<header>
<div data-if="user.isLoggedIn" class="user-menu">
<img data-attr="src:user.avatar" class="avatar">
<span data-text="user.name"></span>
<button data-on="click:logout">ออกจากระบบ</button>
</div>
<div data-if="!user.isLoggedIn">
<a href="/login">เข้าสู่ระบบ</a>
<a href="/register">สมัครสมาชิก</a>
</div>
</header>UI ตามสิทธิ์
<div class="actions">
<button data-if="permissions.canView" data-on="click:view">ดู</button>
<button data-if="permissions.canEdit" data-on="click:edit">แก้ไข</button>
<button data-if="permissions.canDelete" data-on="click:delete">ลบ</button>
</div>สถานะการโหลด
<div class="data-container">
<!-- สถานะกำลังโหลด -->
<div data-if="isLoading" class="spinner animated">
<div class="loader"></div>
<p>กำลังโหลด...</p>
</div>
<!-- สถานะ error -->
<div data-if="!isLoading && error" class="error-message">
<p data-text="error.message"></p>
<button data-on="click:retry">ลองใหม่</button>
</div>
<!-- สถานะว่าง -->
<div data-if="!isLoading && !error && items.length === 0">
<p>ไม่พบรายการ</p>
</div>
<!-- เนื้อหา -->
<div data-if="!isLoading && !error && items.length > 0">
<ul data-for="item in items">
<template>
<li data-text="item.name"></li>
</template>
</ul>
</div>
</div>เงื่อนไขซ้อน
<div data-if="order">
<h2>รายละเอียดคำสั่งซื้อ</h2>
<div data-if="order.status === 'pending'">
<p class="warning">รอชำระเงิน</p>
<button data-on="click:pay">ชำระเงินตอนนี้</button>
</div>
<div data-if="order.status === 'completed'">
<p class="success">คำสั่งซื้อเสร็จสมบูรณ์</p>
<div data-if="order.hasReceipt">
<a data-attr="href:order.receiptUrl">ดาวน์โหลดใบเสร็จ</a>
</div>
</div>
</div>ใน data-for Loop
<ul data-for="user in users">
<template>
<li>
<span data-text="user.name"></span>
<span data-if="user.isOnline" class="online-badge">●</span>
<span data-if="user.isAdmin" class="admin-badge">Admin</span>
</li>
</template>
</ul>API อ้างอิง
Expression Operators
| Operator | ตัวอย่าง | คำอธิบาย |
|---|---|---|
! |
!isHidden |
Logical NOT |
&& |
a && b |
Logical AND |
\|\| |
a \|\| b |
Logical OR |
=== |
status === 'active' |
Strict equality |
!== |
type !== 'draft' |
Strict inequality |
>, <, >=, <= |
count > 0 |
Comparisons |
ค่า Truthy/Falsy
| Falsy | Truthy |
|---|---|
false |
true |
0 |
ตัวเลขที่ไม่ใช่ศูนย์ |
"" (string ว่าง) |
String ที่ไม่ว่าง |
null |
Objects, Arrays |
undefined |
Functions |
NaN |
ข้อควรระวัง
⚠️ 1. Element vs Display:none
<!-- data-if ลบ element ออกจาก DOM -->
<div data-if="show">เนื้อหา</div>
<!-- สำหรับแค่ซ่อน ใช้ CSS -->
<div data-style="{ display: show ? 'block' : 'none' }">เนื้อหา</div>
<div data-class="hidden:!show">เนื้อหา</div>⚠️ 2. การเข้าถึง Element ที่ถูกลบ
// ❌ Element อาจไม่มีเมื่อเงื่อนไขเป็น false
const elem = document.querySelector('[data-if]');
elem.textContent; // Error ถ้า element ถูกลบ
// ✅ ตรวจสอบก่อน
if (elem) {
elem.textContent;
}⚠️ 3. Event Handlers บน Element ที่มีเงื่อนไข
<!-- Event handlers ถูกเก็บกวาดเมื่อ element ถูกลบ -->
<div data-if="showEditor">
<textarea data-on="input:handleInput"></textarea>
</div>⚠️ 4. ประสิทธิภาพการสลับบ่อย
<!-- ❌ การสลับบ่อยอาจแพง -->
<div data-if="mouseOver">Tooltip</div>
<!-- ✅ ใช้ CSS สำหรับการสลับบ่อย -->
<div data-class="visible:mouseOver" class="tooltip">Tooltip</div>⚠️ 5. data-component="api" (หรือ data-table) ที่ซ่อนไว้ก็ยังยิงคำขอของมันอยู่ดี
data-if ลบ element ออกจริง แต่ลบหลังจากที่ data-component หรือ
data-table ข้างในถูกสร้างและเริ่มโหลดข้อมูลไปแล้ว — เงื่อนไขที่เป็นเท็จตั้งแต่
แรกจะลบ element ก่อนมันแสดงผลครั้งแรกก็จริง แต่คอมโพเนนต์ข้างในยังคงยิงคำขอ
ทันทีที่ถูกใส่เข้าไปใน DOM ก่อนที่การลบจะเกิดขึ้นเพียงเสี้ยววินาที การห่อ
วิดเจ็ตที่ดึงข้อมูลจาก API ด้วย data-if ซ่อนมันจากสายตาผู้ใช้ได้ แต่ไม่ได้
หยุดคำขอนั้นเลย — เป็นแบบนี้เหมือนกันทั้งสองระบบ เพราะ ApiComponent กับ
TableManager ต่างก็เขียนการยิงคำขอ/จัดการคำตอบของตัวเองแยกกันคนละชุด:
<!-- ❌ ยังคงเรียก /api/v2/admin-only-widget ให้ทุกคน แม้จะถูกซ่อนไว้ -->
<div data-if="permissions['settings.manage']">
<div data-component="api" data-endpoint="/api/v2/admin-only-widget">...</div>
</div>
<!-- ❌ ปัญหาเดียวกัน แบบ data-table -->
<div data-if="permissions['settings.manage']">
<table data-table="adminOnly" data-source="/api/v2/admin-only-widget">...</table>
</div>ถ้าทั้งหน้าไม่ควรให้บทบาทนั้นเข้าถึงได้เลย ให้กั้นที่ระดับเส้นทางแทน (metadata
permission/roles ของเส้นทางเอง ที่ AuthGuard ตรวจก่อนโหลดเทมเพลตด้วยซ้ำ)
วิธีนั้นหยุดคำขอได้จริง ส่วน data-if เหมาะกับการซ่อนวิดเจ็ตบางส่วนที่ฝัง
อยู่ในหน้าที่คนทั่วไปก็เข้าถึงได้ ซึ่งกั้นระดับเส้นทางใช้ไม่ได้ — สำหรับกรณีนั้น
ทั้งสองระบบจะไม่ปล่อยให้คำตอบที่ล้มเหลวของวิดเจ็ตที่ถูกซ่อนไปกระทบส่วนที่เหลือ
ของหน้า ดู การจัดการ 403/401 ของ ApiComponent
และ ของ TableManager ซึ่งทั้งคู่
เช็คก่อนว่า instance นั้นยังติดอยู่กับหน้าจริงหรือไม่ ก่อนจะทำอะไรกับคำตอบ
⚠️ 6. คีย์ที่ไม่มีอยู่ทำให้ธาตุถูกซ่อน — ให้ส่งคีย์มาครบทุกตัว รวมตัวที่เป็น false
data-if ถือว่าเงื่อนไขที่ได้ค่า undefined เป็นเท็จแล้วซ่อนธาตุนั้น ผลคือคีย์ที่พิมพ์ผิดหรือไม่ได้ส่งมา
แยกไม่ออกจากสิทธิ์ที่ถูกปฏิเสธ:
<!-- permissions = { 'settings.manage': false } → ซ่อน ซึ่งถูกต้อง -->
<div data-if="permissions['settings.manage']">...</div>
<!-- permissions = { } → ซ่อนเหมือนกัน แต่เพราะไม่มีคีย์ ไม่ใช่เพราะถูกปฏิเสธ -->
<div data-if="permissions['setings.manage']">...</div>ให้ฝั่งเซิร์ฟเวอร์ส่งแผนที่ที่มีคีย์ทุกตัวที่ระบบรู้จัก พร้อมค่า true/false จริง ๆ แทนที่จะส่งเฉพาะตัวที่
อนุญาต ธาตุที่ถูกซ่อนจะได้แปลว่า "ไม่มีสิทธิ์" เสมอ และคีย์ที่พิมพ์ผิดจะกลายเป็นธาตุที่ซ่อนถาวรให้เห็น
แทนที่จะกลับค่าเงียบ ๆ ในวันที่คีย์นั้นโผล่ขึ้นมา
การเข้าถึงด้วยวงเล็บเหลี่ยมทำงานในเงื่อนไขที่ประกอบหลายส่วนเหมือนตอนอยู่เดี่ยว ๆ —
role === 'webadmin' && permissions['customer.manage'] อ่านทั้งสองข้าง และคีย์ในเครื่องหมายคำพูด
ที่มีจุดอยู่ข้างใน (permissions['customer.manage']) คือการค้นหาชื่อนั้นตรง ๆ หนึ่งครั้ง ไม่ใช่การไล่ลงไป
ที่ customer แล้วต่อด้วย manage
ข้อพิจารณาด้านประสิทธิภาพ
| สถานการณ์ | คำแนะนำ |
|---|---|
| เปลี่ยนไม่บ่อย | data-if (ลบออกจาก DOM) |
| สลับบ่อย | data-class ร่วมกับ CSS visibility |
| เนื้อหาขนาดใหญ่ | data-if (ลดขนาด DOM) |
| ต้องการ animation | data-if ร่วมกับคลาส animated |
Directives ที่เกี่ยวข้อง
- data-for - สำหรับ render รายการ
- data-class - สำหรับ visibility ด้วย class
- data-style - สำหรับ visibility ด้วย style