Now.js Framework Documentation

Now.js Framework Documentation

data-if

TH 04 Sep 2026 01:40

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-in classes
  • ซ่อน: เพิ่ม fade-out, animate-out classes, รอ 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