Now.js Framework Documentation

Now.js Framework Documentation

ApiComponent - UI Component Wrapper

EN 30 Nov 2025 11:56

ApiComponent - UI Component Wrapper

Documentation for ApiComponent, a UI Component Wrapper for calling APIs and displaying results in HTML

📋 Table of Contents

  1. Overview
  2. Installation and Import
  3. Basic Usage
  4. HTML Attributes
  5. Configuration Options
  6. Template System
  7. Pagination
  8. Polling
  9. Events
  10. CSS State Classes
  11. JavaScript API
  12. Usage Examples
  13. API Reference
  14. Best Practices

Overview

ApiComponent is a UI Component that allows you to call APIs and display results in HTML easily without writing JavaScript yourself.

Key Features

  • HTML-Based: Configure via HTML attributes
  • Template Support: Support template system for rendering
  • Auto-Loading: Auto-load data when creating component
  • Caching: Cache data to improve performance
  • Pagination: Support pagination and infinite scroll
  • Polling: Auto-update data at intervals
  • Event System: Emit events on changes
  • Retry Logic: Auto-retry on errors
  • Loading States: Handle loading, error, empty states

When to Use ApiComponent

Use ApiComponent when:

  • You need to display API data in HTML
  • You need ease of use (no JavaScript required)
  • You need pagination or infinite scroll
  • You need auto-refresh data (polling)
  • You need automatic loading/error states

Don't use ApiComponent when:

  • You need detailed rendering control (use ApiService + custom code)
  • Working with complex real-time data (use WebSocket)
  • You need very complex logic

Installation and Import

ApiComponent is loaded with the Now.js Framework and is ready to use immediately via the window object:

// No import needed - ready to use immediately
console.log(window.ApiComponent); // ApiComponent object

Dependencies

ApiComponent requires these dependencies:

  • HttpClient or simpleFetch - For API calls
  • TemplateManager - For rendering (optional)

Two Modes of Operation

ApiComponent supports 2 different modes and automatically detects which mode to use:

1. Simple Binding Mode

When: When there is NO <template> element inside the component

Purpose: Display simple data like user names, images, numbers by updating values in existing elements

Example:

<!-- Simple Binding Mode - no template -->
<div data-component="api"
     data-endpoint="/api/v1/auth/me"
     data-cache="true">
  <img data-source-key="data.avatar.0.url" data-source-attr="src" class="avatar">
  <span data-source-key="data.name">Loading...</span>
  <span data-source-key="data.email">Loading...</span>
</div>

Supported Attributes:

  • data-source-key - path to data (dot notation)
  • data-source-attr - attribute to update (textContent, src, href, innerHTML, etc.)
  • data-source-template - simple template like "Hello, {data.name}!"

Best for:

  • Logged-in user info (topbar, profile card)
  • Notification badges (count displays)
  • Simple single-value displays

2. Template Rendering Mode

When: When there IS a <template> element inside the component

Purpose: Create new HTML from template for complex data like lists, cards, dynamic content

Example:

<!-- Template Rendering Mode - has template -->
<div data-component="api"
     data-endpoint="/api/users">
  <template>
    <ul>
      <li data-for="user of data">
        <img data-bind="user.avatar" data-attr="src">
        <span data-bind="user.name"></span>
        <span data-if="user.status === 'active'">Active</span>
      </li>
    </ul>
  </template>
</div>

Supported Features:

  • data-for - loop through data
  • data-if - conditional rendering
  • data-bind - bind data
  • Other TemplateManager directives

Best for:

  • Lists (users, products, posts)
  • Dynamic components with multiple items
  • UI requiring loops and conditions

Mode Comparison

Feature Simple Binding Template Rendering
Template Element ❌ No ✅ Yes
Use Case Single values Lists/Collections
Complexity Simple Complex
HTML Output Updates existing Creates new HTML
Loops
Conditions
Performance Faster Slower (creates HTML)

Basic Usage

<div data-component="api"
     data-endpoint="/api/users"
     data-method="GET">
  <template>
    <div data-for="item in data">
      <h3>{item.name}</h3>
      <p>{item.email}</p>
    </div>
  </template>
</div>

2. JavaScript-Based

const element = document.querySelector('#my-api-component');

const instance = ApiComponent.create(element, {
  endpoint: '/api/users',
  method: 'GET',
  autoload: true
});

3. Auto-Initialization

ApiComponent will automatically create instances for elements with data-component="api":

<!-- Will be created automatically when page loads -->
<div data-component="api" data-endpoint="/api/products"></div>

HTML Attributes

ApiComponent supports these attributes for configuration:

Basic Attributes

Attribute Type Default Description
data-endpoint string - API endpoint URL (required)
data-method string 'GET' HTTP method (GET, POST, PUT, DELETE)
data-base-url string '' Base URL for API
data-autoload boolean true Auto-load data on create
data-timeout number 30000 Request timeout (ms)

Cache Attributes

Attribute Type Default Description
data-cache boolean false Enable caching
data-cache-time number 60000 Cache duration (ms)

Template Attributes

Attribute Type Default Description
data-template string - External template ID

Pagination Attributes

Attribute Type Default Description
data-pagination boolean true Enable pagination
data-page-param string 'page' Page parameter name
data-limit-param string 'pageSize' Limit parameter name
data-page-size number 10 Items per page

Polling Attributes

Attribute Type Default Description
data-polling boolean false Enable polling
data-polling-interval number 30000 Polling interval (ms)

Event Attributes

Attribute Type Default Description
data-refresh-event string - Event names to trigger refresh (comma-separated)

Data Attributes

Attribute Type Default Description
data-params JSON {} Query parameters
data-data JSON {} Request body data
data-headers JSON {} Custom headers
data-data-path string 'data' Path to data in response
data-meta-path string 'meta' Path to meta in response

Props Attribute

You can use data-props to set all configuration in JSON format:

<div data-component="api"
     data-props='{
       "endpoint": "/api/users",
       "method": "GET",
       "cache": true,
       "cacheTime": 300000,
       "params": {"status": "active"}
     }'>
</div>

Configuration Options

When creating an instance with JavaScript, you can configure all options:

const instance = ApiComponent.create(element, {
  // Basic
  method: 'GET',
  baseURL: '',
  endpoint: '/api/users',
  autoload: true,
  timeout: 30000,

  // Cache
  cache: false,
  cacheTime: 60000,

  // Retry
  retry: 3,

  // Messages
  loadingText: 'Loading...',
  errorText: 'Error occurred',
  emptyText: 'No data found',

  // Event Handlers
  onSuccess: (data, response) => {
    console.log('Success:', data);
  },
  onError: (error) => {
    console.error('Error:', error);
  },
  onEmpty: () => {
    console.log('No data');
  },

  // Pagination
  pagination: true,
  pageParam: 'page',
  limitParam: 'pageSize',
  pageSize: 10,

  // Polling
  polling: false,
  pollingInterval: 30000,

  // Events
  refreshEvent: 'user:updated',

  // Data
  params: {},
  data: {},
  headers: {},

  // Response Paths
  dataPath: 'data',
  metaPath: 'meta',

  // Template
  template: 'my-template-id',

  // Debug
  debug: false
});

Template System

ApiComponent uses Now.js's TemplateManager for rendering

Inline Template

<div data-component="api" data-endpoint="/api/users">
  <template>
    <div data-for="user in data">
      <h3>{user.name}</h3>
      <p>{user.email}</p>
      <span data-if="user.status === 'active'">Active</span>
    </div>
  </template>
</div>

External Template

<!-- Define template -->
<template id="user-template">
  <div class="user-card">
    <h3>{name}</h3>
    <p>{email}</p>
  </div>
</template>

<!-- Use template -->
<div data-component="api"
     data-endpoint="/api/users"
     data-template="user-template">
</div>

Template Directives

ApiComponent supports TemplateManager directives:

data-for (Loop)

<template>
  <ul>
    <li data-for="item in data">
      {item.name}
    </li>
  </ul>
</template>

data-if (Conditional)

<template>
  <div data-for="user in data">
    <h3>{user.name}</h3>
    <span data-if="user.status === 'active'" class="badge">Active</span>
    <span data-if="user.status === 'inactive'" class="badge">Inactive</span>
  </div>
</template>

Nested Loops

<template>
  <div data-for="category in data">
    <h2>{category.name}</h2>
    <ul>
      <li data-for="product in category.products">
        {product.name} - {product.price}
      </li>
    </ul>
  </div>
</template>

Pagination

ApiComponent supports server-side pagination

Basic Pagination

<div data-component="api"
     data-endpoint="/api/products"
     data-pagination="true"
     data-page-size="20">
  <template>
    <div data-for="product in data">
      <h3>{product.name}</h3>
      <p>{product.price}</p>
    </div>
  </template>

  <!-- Pagination Controls -->
  <button onclick="this.closest('.api-component').apiInstance.loadMore()">
    Load More
  </button>
</div>

Custom Pagination Parameters

<div data-component="api"
     data-endpoint="/api/posts"
     data-page-param="p"
     data-limit-param="limit"
     data-page-size="15">
</div>

Request will be:

GET /api/posts?p=1&limit=15

Expected Response Format

{
  "data": [
    {"id": 1, "name": "Item 1"},
    {"id": 2, "name": "Item 2"}
  ],
  "meta": {
    "total": 100,
    "last_page": 10,
    "current_page": 1,
    "per_page": 10
  }
}

Disable Pagination

<div data-component="api"
     data-endpoint="/api/settings"
     data-pagination="false">
</div>

Polling

ApiComponent can poll endpoints automatically to update data

Enable Polling

<div data-component="api"
     data-endpoint="/api/notifications"
     data-polling="true"
     data-polling-interval="10000">
  <!-- Will reload data every 10 seconds -->
</div>

Control Polling via JavaScript

const instance = ApiComponent.getInstance('#my-component');

// Start polling
ApiComponent.startPolling(instance);

// Stop polling
ApiComponent.stopPolling(instance);

Polling Events

element.addEventListener('api:polling:start', (e) => {
  console.log('Polling started');
});

element.addEventListener('api:polling:stop', (e) => {
  console.log('Polling stopped');
});

Events

ApiComponent emits events on changes

Event Types

Event When Detail
api:init Component initialized {instance}
api:loaded Data loaded successfully {data, response, fromCache}
api:error Error occurred {error, message}
api:empty No data returned -
api:content-rendered Content rendered {data}
api:polling:start Polling started -
api:polling:stop Polling stopped -
api:destroy Component destroyed -

Listen to Events

const element = document.querySelector('#my-api-component');

element.addEventListener('api:loaded', (e) => {
  console.log('Data loaded:', e.detail.data);
  console.log('From cache:', e.detail.fromCache);
});

element.addEventListener('api:error', (e) => {
  console.error('Error:', e.detail.error);
});

element.addEventListener('api:empty', (e) => {
  console.log('No data available');
});

Refresh on Custom Event

<div data-component="api"
     data-endpoint="/api/cart"
     data-refresh-event="cart:updated">
  <!-- Will refresh when 'cart:updated' event is emitted -->
</div>

<script>
// Anywhere in code
EventManager.emit('cart:updated');
</script>

CSS State Classes

ApiComponent automatically adds CSS classes to indicate the current state of the component. These classes use the api- prefix to avoid conflicts with common class names.

State Classes

Class When Applied Description
api-component Always Added when component is initialized
api-loading During data fetch Data is being loaded from API
api-content After successful load Data loaded and rendered successfully
api-error On error An error occurred during loading
api-empty When no data API returned empty data or empty array

Usage Example

/* Style loading state */
.api-component.api-loading {
  opacity: 0.6;
  pointer-events: none;
}

.api-component.api-loading::after {
  content: 'Loading...';
  display: block;
  text-align: center;
  padding: 20px;
}

/* Style error state */
.api-component.api-error {
  background-color: #fee;
  border: 1px solid #fcc;
  padding: 20px;
}

.api-component.api-error::before {
  content: '⚠️ Error loading data';
  display: block;
  color: #c00;
  font-weight: bold;
  margin-bottom: 10px;
}

/* Style empty state */
.api-component.api-empty {
  text-align: center;
  padding: 40px;
  color: #999;
}

.api-component.api-empty::after {
  content: 'No data available';
  display: block;
}

/* Style content state */
.api-component.api-content {
  /* Your content styles */
}

Complete Example

<div data-component="api"
     data-endpoint="/api/products"
     class="product-list">
  <template>
    <div data-for="product in data" class="product-card">
      <h3>{product.name}</h3>
      <p>{product.price}</p>
    </div>
  </template>
</div>

<style>
/* Base styling */
.product-list {
  min-height: 200px;
  transition: all 0.3s ease;
}

/* Loading state */
.product-list.api-loading {
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: loading 1.5s ease-in-out infinite;
}

@keyframes loading {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* Error state */
.product-list.api-error {
  background-color: #fee2e2;
  border: 2px solid #ef4444;
  padding: 20px;
  border-radius: 8px;
}

/* Empty state */
.product-list.api-empty {
  background-color: #f9fafb;
  border: 2px dashed #d1d5db;
  padding: 40px;
  text-align: center;
  color: #6b7280;
}

/* Content loaded */
.product-list.api-content {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 20px;
}
</style>

Benefits of api- Prefix

The api- prefix prevents conflicts with commonly used class names:

  • .loading - might conflict with your own loading classes

  • .error - very common class name in many projects

  • .content - extremely common, often used for layout

  • .empty - commonly used for empty states

  • .api-loading - specific to ApiComponent

  • .api-error - clear it's from ApiComponent

  • .api-content - won't conflict with layout classes

  • .api-empty - specific to API data state

JavaScript API

ApiComponent has methods for control via JavaScript

Create Instance

const instance = ApiComponent.create(element, options);

Get Instance

// From element
const instance = ApiComponent.getInstance(element);

// From ID
const instance = ApiComponent.getInstance('#my-component');

// From element property
const instance = element.apiInstance;

Load Data

// Load data (reset pagination)
await ApiComponent.loadData(instance);

// Load more (append data)
await ApiComponent.loadMore(instance);

Refresh Data

// Refresh (reset to page 1)
ApiComponent.refresh(instance);

// Or use instance method
instance.refresh();

Submit Data

// Submit data (POST)
await ApiComponent.submit(instance, {
  name: 'John Doe',
  email: 'john@example.com'
});

Polling Control

// Start polling
ApiComponent.startPolling(instance);

// Stop polling
ApiComponent.stopPolling(instance);

Cache Management

// Clear all cache
ApiComponent.clearCache();

Destroy Instance

// Destroy instance and cleanup
ApiComponent.destroy(instance);

Usage Examples

1. Simple Product List

<div data-component="api"
     data-endpoint="/api/products"
     data-cache="true"
     data-cache-time="300000">
  <template>
    <div class="product-grid">
      <div data-for="product in data" class="product-card">
        <img data-src="product.image" data-alt="product.name">
        <h3>{product.name}</h3>
        <p class="price">{product.price} USD</p>
        <button>Add to Cart</button>
      </div>
    </div>
  </template>
</div>

2. User Profile with POST

<form id="profile-form">
  <input type="text" name="name" placeholder="Name">
  <input type="email" name="email" placeholder="Email">
  <button type="submit">Save</button>
</form>

<div id="profile-result"
     data-component="api"
     data-endpoint="/api/profile"
     data-method="POST"
     data-autoload="false">
  <template>
    <div class="alert success">
      Profile updated: {data.name}
    </div>
  </template>
</div>

<script>
document.getElementById('profile-form').addEventListener('submit', async (e) => {
  e.preventDefault();

  const formData = new FormData(e.target);
  const data = Object.fromEntries(formData);

  const instance = ApiComponent.getInstance('#profile-result');
  await ApiComponent.submit(instance, data);
});
</script>

3. Infinite Scroll

<div data-component="api"
     data-endpoint="/api/posts"
     data-page-size="20"
     id="posts">
  <template>
    <div data-for="post in data" class="post">
      <h2>{post.title}</h2>
      <p>{post.excerpt}</p>
    </div>
  </template>
</div>

<script>
// Infinite scroll
window.addEventListener('scroll', () => {
  if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - 500) {
    const instance = ApiComponent.getInstance('#posts');
    if (!instance.loading && instance.page < instance.totalPages) {
      ApiComponent.loadMore(instance);
    }
  }
});
</script>

4. Real-time Notifications (Polling)

<div data-component="api"
     data-endpoint="/api/notifications"
     data-polling="true"
     data-polling-interval="10000"
     data-cache="false">
  <template>
    <div class="notification-list">
      <div data-for="notif in data" class="notification">
        <span class="icon" data-if="notif.type === 'info'">ℹ️</span>
        <span class="icon" data-if="notif.type === 'warning'">⚠️</span>
        <span class="icon" data-if="notif.type === 'error'">❌</span>
        <div class="content">
          <h4>{notif.title}</h4>
          <p>{notif.message}</p>
          <time>{notif.created_at}</time>
        </div>
      </div>
    </div>
  </template>
</div>

5. Search with Debounce

<input type="text" id="search-input" placeholder="Search products...">

<div data-component="api"
     data-endpoint="/api/products/search"
     data-autoload="false"
     id="search-results">
  <template>
    <div data-for="product in data">
      <h4>{product.name}</h4>
      <p>{product.description}</p>
    </div>
  </template>
</div>

<script>
let searchTimeout;
const searchInput = document.getElementById('search-input');
const instance = ApiComponent.getInstance('#search-results');

searchInput.addEventListener('input', (e) => {
  clearTimeout(searchTimeout);

  searchTimeout = setTimeout(() => {
    instance.options.params = { q: e.target.value };
    ApiComponent.refresh(instance);
  }, 300);
});
</script>

6. Dynamic Endpoint

<select id="category-select">
  <option value="electronics">Electronics</option>
  <option value="clothing">Clothing</option>
  <option value="books">Books</option>
</select>

<div data-component="api"
     data-endpoint="/api/products"
     data-params='{"category": "electronics"}'
     id="products">
  <template>
    <div data-for="product in data">
      <h3>{product.name}</h3>
    </div>
  </template>
</div>

<script>
document.getElementById('category-select').addEventListener('change', (e) => {
  const instance = ApiComponent.getInstance('#products');
  instance.options.params.category = e.target.value;
  ApiComponent.refresh(instance);
});
</script>

7. Nested Data Structure

<div data-component="api"
     data-endpoint="/api/categories">
  <template>
    <div data-for="category in data" class="category">
      <h2>{category.name}</h2>
      <div class="products">
        <div data-for="product in category.products" class="product">
          <h4>{product.name}</h4>
          <p>{product.price} USD</p>
        </div>
      </div>
    </div>
  </template>
</div>

8. Custom Response Path

<!-- API Response: { "success": true, "result": { "items": [...] } } -->
<div data-component="api"
     data-endpoint="/api/items"
     data-data-path="result.items">
  <template>
    <div data-for="item in data">
      {item.name}
    </div>
  </template>
</div>

9. Event-Driven Updates

<!-- Shopping Cart -->
<div data-component="api"
     data-endpoint="/api/cart"
     data-refresh-event="cart:updated"
     id="cart">
  <template>
    <div>
      Total Items: {data.count}
      Total Price: {data.total} USD
    </div>
  </template>
</div>

<!-- Add to Cart Button -->
<button onclick="addToCart(123)">Add to Cart</button>

<script>
async function addToCart(productId) {
  await fetch('/api/cart/add', {
    method: 'POST',
    body: JSON.stringify({ productId }),
    headers: {'Content-Type': 'application/json'}
  });

  // Trigger refresh
  EventManager.emit('cart:updated');
}
</script>

10. Error Handling with Custom UI

<div data-component="api"
     data-endpoint="/api/products"
     id="products">
  <template>
    <div data-for="product in data">
      <h3>{product.name}</h3>
    </div>
  </template>
</div>

<script>
const element = document.getElementById('products');

element.addEventListener('api:error', (e) => {
  const errorDiv = document.createElement('div');
  errorDiv.className = 'alert alert-error';
  errorDiv.textContent = 'Failed to load products. ';

  const retryBtn = document.createElement('button');
  retryBtn.textContent = 'Retry';
  retryBtn.onclick = () => {
    ApiComponent.refresh(e.detail.instance);
    errorDiv.remove();
  };

  errorDiv.appendChild(retryBtn);
  element.insertBefore(errorDiv, element.firstChild);
});
</script>

API Reference

Methods

// Create
create(element: HTMLElement|string, options?: object): Instance

// Get Instance
getInstance(element: HTMLElement|string): Instance|null

// Data Operations
loadData(instance: Instance, append?: boolean): Promise<void>
refresh(instance: Instance): void
loadMore(instance: Instance): void
submit(instance: Instance, data: object): Promise<void>

// Polling
startPolling(instance: Instance): void
stopPolling(instance: Instance): void

// Cache
clearCache(): void

// Lifecycle
destroy(instance: Instance): boolean
init(options?: object): Promise<ApiComponent>

Instance Properties

{
  id: string,
  element: HTMLElement,
  options: object,
  data: any,
  loading: boolean,
  error: string|null,
  timestamp: number|null,
  page: number,
  totalPages: number,
  pageSize: number,
  totalItems: number,
  polling: boolean,
  timer: number|null,
  abortController: AbortController|null,
  templateContent: string|null
}

Best Practices

1. ✅ Use Data Attributes for Simple Cases

<!-- ❌ Bad: Use JavaScript when not necessary -->
<div id="products"></div>
<script>
ApiComponent.create('#products', {endpoint: '/api/products'});
</script>

<!-- ✅ Good: Use HTML attributes -->
<div data-component="api" data-endpoint="/api/products"></div>

2. ✅ Enable Caching for Static Data

<!-- ✅ Good: Cache data that doesn't change often -->
<div data-component="api"
     data-endpoint="/api/categories"
     data-cache="true"
     data-cache-time="300000">
</div>

3. ✅ Use External Templates for Reusability

<!-- ✅ Good: Separate template for reusability -->
<template id="product-card">
  <div class="card">
    <h3>{name}</h3>
    <p>{price}</p>
  </div>
</template>

<div data-component="api"
     data-endpoint="/api/products"
     data-template="product-card">
</div>

<div data-component="api"
     data-endpoint="/api/featured-products"
     data-template="product-card">
</div>

4. ✅ Clean Up When Removing Components

// ✅ Good: Destroy instance when no longer needed
const instance = ApiComponent.getInstance('#my-component');
ApiComponent.destroy(instance);

// Or when removing element
element.remove(); // Auto-cleanup if using ComponentManager

5. ✅ Use Pagination for Large Datasets

<!-- ✅ Good: Use pagination for large datasets -->
<div data-component="api"
     data-endpoint="/api/posts"
     data-page-size="20">
</div>

6. ✅ Handle Empty States

<div data-component="api"
     data-endpoint="/api/notifications">
  <template>
    <div data-if="data.length > 0">
      <div data-for="notif in data">
        {notif.message}
      </div>
    </div>
    <div data-if="data.length === 0">
      No notifications
    </div>
  </template>
</div>

7. ✅ Use Polling Wisely

<!-- ❌ Bad: Polling every 1 second -->
<div data-polling="true" data-polling-interval="1000"></div>

<!-- ✅ Good: Polling every 30 seconds or use WebSocket -->
<div data-polling="true" data-polling-interval="30000"></div>

8. ✅ Disable Autoload When Needed

<!-- ✅ Good: Disable autoload when you need control -->
<div data-component="api"
     data-endpoint="/api/search"
     data-autoload="false"
     id="search">
</div>

<script>
// Load when user types search
searchInput.addEventListener('input', (e) => {
  const instance = ApiComponent.getInstance('#search');
  instance.options.params = {q: e.target.value};
  ApiComponent.refresh(instance);
});
</script>

9. ✅ Use Custom Data Paths

<!-- ✅ Good: Specify path when API response isn't {data: [...]} -->
<div data-component="api"
     data-endpoint="/api/legacy"
     data-data-path="result.items"
     data-meta-path="result.pagination">
</div>

10. ✅ Listen to Events for Custom Actions

// ✅ Good: Use events for custom actions
element.addEventListener('api:loaded', (e) => {
  // Update other UI elements
  updateStats(e.detail.data);

  // Track analytics
  analytics.track('data_loaded', {
    endpoint: e.detail.instance.options.endpoint
  });
});

Summary

When to Use ApiComponent

Use Case Use ApiComponent?
Display list of data from API ✅ Yes - HTML-based
Need pagination ✅ Yes - built-in support
Need infinite scroll ✅ Yes - loadMore()
Auto-refresh data ✅ Yes - polling support
Need detailed control ❌ No - use ApiService
Complex real-time ❌ No - use WebSocket
Form submission ✅ Can use - submit()
Simple static content ❌ Not needed

Key Features

Feature Available Note
HTML Attributes No JavaScript needed
Template System TemplateManager integration
Auto-Loading Configurable
Caching In-memory
Pagination Server-side
Polling Auto-refresh
Events Custom events
Retry Auto-retry on error
Loading States loading, error, empty