Now.js Framework Documentation

Now.js Framework Documentation

data-if

EN 04 Sep 2026 01:40

data-if

Overview

data-if conditionally renders elements based on expression evaluation. Elements are completely removed from the DOM when the condition is false.

When to use:

  • Show/hide content based on data state
  • Conditional rendering of components
  • Toggle sections based on user permissions

Why use it:

  • ✅ Complete DOM removal (not just hiding)
  • ✅ Supports complex expressions
  • ✅ Animation support for transitions
  • ✅ Automatic cleanup of nested directives

Basic Usage

Simple Condition

<div data-if="isLoggedIn">
  Welcome back!
</div>

With data:

{ isLoggedIn: true }

Output when true:

<div>Welcome back!</div>

Output when false:

<!-- Element is removed from DOM -->

Syntax

<element data-if="expression">
Part Description
expression JavaScript expression that evaluates to truthy/falsy

Features

1. Boolean Conditions

<div data-if="isVisible">Visible content</div>
<div data-if="!isHidden">Not hidden content</div>

2. Property Existence

<div data-if="user.avatar">
  <img data-attr="src:user.avatar">
</div>

3. Comparison Operators

<div data-if="count > 0">You have items</div>
<div data-if="status === 'active'">Active status</div>
<div data-if="items.length >= 5">Many items</div>

4. Logical Operators

<div data-if="isLoggedIn && isAdmin">Admin Panel</div>
<div data-if="hasError || isLoading">Show message</div>
<div data-if="!isDisabled">Enabled content</div>

5. Complex Expressions

<div data-if="user.role === 'admin' && permissions.includes('edit')">
  Edit button
</div>

6. Negation

<div data-if="!isLoggedIn">
  <a href="/login">Please log in</a>
</div>

If-Else Pattern

Since there's no data-else, use negated conditions:

<div data-if="isLoggedIn">
  <p>Welcome, <span data-text="user.name"></span>!</p>
</div>

<div data-if="!isLoggedIn">
  <a href="/login">Log in</a>
</div>

Multiple Conditions (else-if pattern)

<div data-if="status === 'loading'">
  <div class="spinner">Loading...</div>
</div>

<div data-if="status === 'error'">
  <div class="error">Something went wrong</div>
</div>

<div data-if="status === 'success'">
  <div class="content" data-text="data"></div>
</div>

Animation Support

Add animated class for enter/exit animations:

<div data-if="showModal" class="modal animated">
  <div class="modal-content">
    Modal content here
  </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; }
}

When animated class is present:

  • Showing: Adds fade-in, animate-in classes
  • Hiding: Adds fade-out, animate-out classes, waits for animation

Advanced Examples

User Authentication

<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">Logout</button>
  </div>

  <div data-if="!user.isLoggedIn">
    <a href="/login">Login</a>
    <a href="/register">Register</a>
  </div>
</header>

Permission-Based UI

<div class="actions">
  <button data-if="permissions.canView" data-on="click:view">View</button>
  <button data-if="permissions.canEdit" data-on="click:edit">Edit</button>
  <button data-if="permissions.canDelete" data-on="click:delete">Delete</button>
</div>

Loading States

<div class="data-container">
  <!-- Loading state -->
  <div data-if="isLoading" class="spinner animated">
    <div class="loader"></div>
    <p>Loading...</p>
  </div>

  <!-- Error state -->
  <div data-if="!isLoading && error" class="error-message">
    <p data-text="error.message"></p>
    <button data-on="click:retry">Retry</button>
  </div>

  <!-- Empty state -->
  <div data-if="!isLoading && !error && items.length === 0">
    <p>No items found</p>
  </div>

  <!-- Content -->
  <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>

Nested Conditions

<div data-if="order">
  <h2>Order Details</h2>

  <div data-if="order.status === 'pending'">
    <p class="warning">Payment pending</p>
    <button data-on="click:pay">Pay now</button>
  </div>

  <div data-if="order.status === 'completed'">
    <p class="success">Order completed</p>
    <div data-if="order.hasReceipt">
      <a data-attr="href:order.receiptUrl">Download receipt</a>
    </div>
  </div>
</div>

In 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 Reference

Expression Operators

Operator Example Description
! !isHidden Logical NOT
&& a && b Logical AND
\|\| a \|\| b Logical OR
=== status === 'active' Strict equality
!== type !== 'draft' Strict inequality
>, <, >=, <= count > 0 Comparisons

Truthy/Falsy Values

Falsy Truthy
false true
0 Non-zero numbers
"" (empty string) Non-empty strings
null Objects, Arrays
undefined Functions
NaN

Common Pitfalls

⚠️ 1. Element vs Display:none

<!-- data-if removes element from DOM -->
<div data-if="show">Content</div>

<!-- For visibility only, use CSS -->
<div data-style="{ display: show ? 'block' : 'none' }">Content</div>
<div data-class="hidden:!show">Content</div>

⚠️ 2. Accessing Removed Element

// ❌ Element may not exist when condition is false
const elem = document.querySelector('[data-if]');
elem.textContent; // Error if element is removed

// ✅ Check existence first
if (elem) {
  elem.textContent;
}

⚠️ 3. Event Handlers on Conditional Elements

<!-- Event handlers are cleaned up when element is removed -->
<div data-if="showEditor">
  <textarea data-on="input:handleInput"></textarea>
</div>

⚠️ 4. Rapid Toggle Performance

<!-- ❌ Rapid toggling can be expensive -->
<div data-if="mouseOver">Tooltip</div>

<!-- ✅ Use CSS for frequent toggles -->
<div data-class="visible:mouseOver" class="tooltip">Tooltip</div>

⚠️ 5. A Hidden data-component="api" (or data-table) Still Fires Its Request

data-if removes the element, but only after any nested data-component or
data-table inside it has already been created and started loading — an
initially-false condition removes the element before its first paint, but a
component nested inside still issues its request the instant it's added to
the DOM, milliseconds before the removal happens. Wrapping an API-driven
widget in data-if hides it from the user; it does not stop the request —
this applies identically to both component systems, since they have entirely
separate request paths (ApiComponent and TableManager each implement their
own fetch/response handling independently):

<!-- ❌ Still calls /api/v2/admin-only-widget for every user, even when hidden -->
<div data-if="permissions['settings.manage']">
  <div data-component="api" data-endpoint="/api/v2/admin-only-widget">...</div>
</div>

<!-- ❌ Same problem, the data-table way -->
<div data-if="permissions['settings.manage']">
  <table data-table="adminOnly" data-source="/api/v2/admin-only-widget">...</table>
</div>

For a whole page that a role should never reach at all, gate it at the route
level instead (the route's own permission/roles metadata, checked by
AuthGuard before the template even loads) — that genuinely prevents the
request. data-if is the right tool for hiding a partial widget embedded in
an otherwise-shared page, where route-level gating doesn't apply; for that
case, neither component system lets a hidden widget's failed response act on
the rest of the page — see ApiComponent's
and TableManager's 403/401
handling, both of which check that the instance is still genuinely mounted
before reacting to its response.

⚠️ 6. A Missing Key Hides the Element — Send Every Key, Including the False Ones

data-if reads a condition that resolves to undefined as false and hides the element.
That makes a misspelled or absent key indistinguishable from a denied permission:

<!-- permissions = { 'settings.manage': false } → hidden, correctly -->
<div data-if="permissions['settings.manage']">...</div>

<!-- permissions = { } → also hidden, but because the key is missing, not because it is denied -->
<div data-if="permissions['setings.manage']">...</div>

Have the server send a map carrying every key it knows about, each with a real
true/false value, rather than only the granted ones. Then a hidden element always
means "denied", and a typo shows up as a permanently hidden element instead of quietly
inverting itself the day the key appears.

Bracket access works the same inside a compound condition as it does alone —
role === 'webadmin' && permissions['customer.manage'] reads both operands, and a
quoted key containing dots (permissions['customer.manage']) is one lookup by that
exact name, not a walk down customer then manage.

Performance Considerations

Scenario Recommendation
Rarely changes data-if (removes from DOM)
Frequent toggles data-class with CSS visibility
Large content data-if (reduces DOM size)
Animation needed data-if with animated class