Now.js Framework Documentation

Now.js Framework Documentation

ModalDataBinder - Declarative Modal Data Binding

EN 27 Nov 2025 12:18

ModalDataBinder - Declarative Modal Data Binding

Complete documentation for ModalDataBinder - a helper for using modals declaratively via HTML attributes with data binding, template evaluation, and gallery navigation support.

📋 Table of Contents

  1. Overview
  2. Installation
  3. Basic Usage
  4. HTML Attributes
  5. Template Context Evaluation
  6. Gallery Mode
  7. Working with Modal Elements
  8. JavaScript API
  9. Usage Examples
  10. API Reference
  11. Best Practices

Overview

ModalDataBinder is a helper that enables declarative modal usage through HTML attributes without writing additional JavaScript. It supports data binding from template loops and gallery navigation.

Key Features

  • Declarative API: Use via HTML attributes immediately
  • Data Binding: Automatically bind data from template context
  • Template Evaluation: Evaluate expressions from template loops
  • Gallery Navigation: Support prev/next navigation
  • Dual Mode: Support both pre-existing HTML modals and Modal instances
  • Auto-initialization: Starts automatically when page loads
  • Event Delegation: Uses event delegation for performance

When to Use ModalDataBinder

Use ModalDataBinder when:

  • Modal elements already exist in HTML
  • Need to bind data from template loops
  • Want gallery navigation (prev/next)
  • Prefer declarative approach without writing JavaScript

Don't use ModalDataBinder when:

  • Creating modals programmatically (use Modal class instead)
  • Need full control over every detail
  • No data binding needed

Installation

ModalDataBinder is included with Now.js Framework and starts automatically:

// No import needed - starts automatically
console.log(window.ModalDataBinder); // ModalDataBinder object

Dependencies

  • Modal – For creating Modal instances (for dynamic modals)
  • BackdropManager – For backdrop management
  • TemplateManager – For template context evaluation (optional)

Basic Usage

1. Simple Modal

<!-- Modal Element -->
<div class="modal" id="simple-modal">
  <div class="modal-content">
    <button class="modal-close">&times;</button>
    <div class="modal-body">
      <h3 data-modal-target="title"></h3>
      <p data-modal-target="message"></p>
    </div>
  </div>
</div>

<!-- Trigger Button -->
<button data-modal="simple-modal"
        data-modal-bind="title:'Hello', message:'Welcome'">
  Open Modal
</button>

2. Modal with Template Loop

<!-- Gallery Items -->
<div class="gallery">
  <div class="gallery-item"
       data-modal="image-modal"
       data-modal-bind="image:item.image, title:item.title, desc:item.desc"
       data-image="photo1.jpg"
       data-title="Photo 1"
       data-desc="Description of photo 1">
    <img src="photo1.jpg">
  </div>

  <div class="gallery-item"
       data-modal="image-modal"
       data-modal-bind="image:item.image, title:item.title, desc:item.desc"
       data-image="photo2.jpg"
       data-title="Photo 2"
       data-desc="Description of photo 2">
    <img src="photo2.jpg">
  </div>
</div>

<!-- Modal -->
<div class="modal" id="image-modal">
  <div class="modal-content">
    <button class="modal-close">&times;</button>
    <div class="modal-body">
      <img data-modal-target="image">
      <h3 data-modal-target="title"></h3>
      <p data-modal-target="desc"></p>
    </div>
  </div>
</div>
<!-- Gallery Items -->
<div class="gallery">
  <div class="gallery-item"
       data-modal="gallery-modal"
       data-modal-bind="image:item.image, title:item.title"
       data-modal-gallery="true"
       data-image="photo1.jpg"
       data-title="Photo 1">
    <img src="photo1.jpg">
  </div>

  <!-- More items... -->
</div>

<!-- Gallery Modal -->
<div class="modal" id="gallery-modal">
  <div class="modal-content">
    <button class="modal-close">&times;</button>
    <div class="modal-body">
      <img data-modal-target="image">
      <h3 data-modal-target="title"></h3>
    </div>
    <div class="modal-controls">
      <button data-modal-nav="prev">‹ Previous</button>
      <button data-modal-nav="next">Next ›</button>
    </div>
  </div>
</div>

HTML Attributes

Trigger Attributes

Attribute Type Required Description
data-modal string ID of modal to open
data-modal-bind string Data binding expressions (comma-separated)
data-modal-gallery boolean Enable gallery mode
data-modal-gallery-loop boolean Loop when reaching end (default: true)
Attribute Type Description
data-modal-target string Data key name to bind
data-modal-html boolean Use innerHTML instead of textContent
Attribute Type Description
data-modal-nav string Navigation direction: "prev" or "next"

Data Binding Syntax Examples

<!-- Simple values -->
<button data-modal="my-modal"
        data-modal-bind="name:'John', age:30">
  Open
</button>

<!-- From data attributes -->
<div data-modal="my-modal"
     data-modal-bind="title:item.title, desc:item.desc"
     data-title="My Title"
     data-desc="My Description">
  Click me
</div>

<!-- Mixed -->
<div data-modal="my-modal"
     data-modal-bind="name:item.name, status:'active', count:$index"
     data-name="John">
  Item
</div>

Template Context Evaluation

ModalDataBinder can evaluate expressions from template context:

Special Variables

Variable Description Example
$index Element index in parent count:$index
$item Element's entire dataset data:$item
item.property Property from dataset name:item.title

Expression Evaluation

<!-- Template Loop Example -->
<div data-for="product of products">
  <div class="product-card"
       data-modal="product-modal"
       data-modal-bind="
         id:item.id,
         name:item.name,
         price:item.price,
         index:$index
       "
       data-id="123"
       data-name="Product Name"
       data-price="999">
    <h3>{product.name}</h3>
  </div>
</div>

Data Attribute Mapping

<!-- Element with data attributes -->
<div data-title="My Title"
     data-description="My Description"
     data-image-url="/image.jpg"
     data-modal="my-modal"
     data-modal-bind="
       title:item.title,
       desc:item.description,
       image:item.imageUrl
     ">
  Click
</div>

Mapping Rules:

  • item.titledata-title
  • item.descriptiondata-description
  • item.imageUrldata-image-url (camelCase → kebab-case)

Gallery mode enables navigation between items:

<!-- Gallery Items -->
<div class="gallery">
  <div data-modal="gallery-modal"
       data-modal-gallery="true"
       data-modal-bind="image:item.image, title:item.title"
       data-image="photo1.jpg"
       data-title="Photo 1">
    <img src="photo1.jpg">
  </div>

  <div data-modal="gallery-modal"
       data-modal-gallery="true"
       data-modal-bind="image:item.image, title:item.title"
       data-image="photo2.jpg"
       data-title="Photo 2">
    <img src="photo2.jpg">
  </div>

  <div data-modal="gallery-modal"
       data-modal-gallery="true"
       data-modal-bind="image:item.image, title:item.title"
       data-image="photo3.jpg"
       data-title="Photo 3">
    <img src="photo3.jpg">
  </div>
</div>

<!-- Gallery Modal -->
<div class="modal" id="gallery-modal">
  <div class="modal-content">
    <button class="modal-close">&times;</button>
    <img data-modal-target="image" class="gallery-image">
    <h3 data-modal-target="title"></h3>

    <!-- Navigation Buttons -->
    <button class="modal-prev" data-modal-nav="prev">‹</button>
    <button class="modal-next" data-modal-nav="next">›</button>
  </div>
</div>
<!-- Don't loop when reaching end -->
<div data-modal="gallery-modal"
     data-modal-gallery="true"
     data-modal-gallery-loop="false"
     data-modal-bind="...">
  Item
</div>
<!-- Previous Button -->
<button data-modal-nav="prev">Previous</button>

<!-- Next Button -->
<button data-modal-nav="next">Next</button>

<!-- Custom Styling -->
<button class="btn-prev" data-modal-nav="prev">
  <i class="icon-arrow-left"></i> Previous
</button>

Working with Modal Elements

ModalDataBinder supports 2 operating modes:

1. Pre-existing HTML Modals

For modal elements that already exist in HTML:

<!-- Modal already in HTML -->
<div class="modal" id="my-modal">
  <div class="modal-content">
    <button class="modal-close">&times;</button>
    <div class="modal-body">
      <h3 data-modal-target="title"></h3>
      <p data-modal-target="message"></p>
    </div>
  </div>
</div>

<!-- Trigger -->
<button data-modal="my-modal"
        data-modal-bind="title:'Hello', message:'World'">
  Open Modal
</button>

Features:

  • Uses existing modal element
  • Show/hide with CSS class .show
  • Manages backdrop via BackdropManager
  • No animation (use CSS transitions)

2. Dynamic Modal Instances

For modals without .modal class (will create Modal instance):

<!-- Element that's not a modal -->
<div id="dynamic-modal">
  <h3 data-modal-target="title"></h3>
  <p data-modal-target="message"></p>
</div>

<!-- Trigger -->
<button data-modal="dynamic-modal"
        data-modal-bind="title:'Dynamic', message:'Modal'">
  Open Dynamic Modal
</button>

Features:

  • Creates Modal instance automatically
  • Animation support
  • Full accessibility features
  • Lifecycle callbacks

JavaScript API

getInstance(modalId)

Get modal instance or reference

const modalRef = ModalDataBinder.state.modalInstances.get('my-modal');

if (modalRef.isPreExisting) {
  // Pre-existing modal
  console.log(modalRef.element);
} else {
  // Modal instance
  console.log(modalRef); // Modal instance
}

openModalWithData(modalId, data, options)

Open modal with data

ModalDataBinder.openModalWithData('my-modal', {
  title: 'Hello',
  message: 'World'
}, {
  gallery: false
});

Parameters:

  • modalId (string) - Modal ID
  • data (Object) - Data to bind
  • options (Object) - Additional options

updateModalData(modalId, data)

Update data of open modal

ModalDataBinder.updateModalData('my-modal', {
  message: 'Updated message'
});

Parameters:

  • modalId (string) - Modal ID
  • data (Object) - Data to update

closeModal(modalId)

Close modal

ModalDataBinder.closeModal('my-modal');

Parameters:

  • modalId (string) - Modal ID

Navigate gallery

// Go to next
ModalDataBinder.navigateGallery('gallery-modal', 'next');

// Go to previous
ModalDataBinder.navigateGallery('gallery-modal', 'prev');

Parameters:

  • modalId (string) - Modal ID
  • direction (string) - 'prev' or 'next'

getModalData(modalId)

Get modal data

const data = ModalDataBinder.getModalData('my-modal');
console.log(data);

Parameters:

  • modalId (string) - Modal ID

Returns: Object | null

Usage Examples

<!-- Product Grid -->
<div class="product-grid">
  <div class="product-card"
       data-modal="product-modal"
       data-modal-gallery="true"
       data-modal-bind="
         image:item.image,
         name:item.name,
         price:item.price,
         desc:item.description
       "
       data-image="/products/product1.jpg"
       data-name="Product 1"
       data-price="999"
       data-description="Product 1 description">
    <img src="/products/product1.jpg">
    <h3>Product 1</h3>
    <p class="price">$999</p>
  </div>

  <div class="product-card"
       data-modal="product-modal"
       data-modal-gallery="true"
       data-modal-bind="
         image:item.image,
         name:item.name,
         price:item.price,
         desc:item.description
       "
       data-image="/products/product2.jpg"
       data-name="Product 2"
       data-price="1,299"
       data-description="Product 2 description">
    <img src="/products/product2.jpg">
    <h3>Product 2</h3>
    <p class="price">$1,299</p>
  </div>
</div>

<!-- Product Modal -->
<div class="modal" id="product-modal">
  <div class="modal-content">
    <button class="modal-close">&times;</button>
    <div class="modal-body">
      <img data-modal-target="image" class="product-image">
      <h2 data-modal-target="name"></h2>
      <p class="price" data-modal-target="price"></p>
      <p data-modal-target="desc"></p>
      <button class="btn-add-cart">Add to Cart</button>
    </div>
    <div class="modal-nav">
      <button data-modal-nav="prev">‹ Previous</button>
      <button data-modal-nav="next">Next ›</button>
    </div>
  </div>
</div>

2. User Profile Cards

<!-- User Cards -->
<div class="user-grid">
  <div class="user-card"
       data-modal="user-modal"
       data-modal-bind="
         avatar:item.avatar,
         name:item.name,
         email:item.email,
         role:item.role,
         bio:item.bio
       "
       data-avatar="/avatars/user1.jpg"
       data-name="John Doe"
       data-email="john@example.com"
       data-role="Developer"
       data-bio="Full-stack developer">
    <img src="/avatars/user1.jpg" class="avatar">
    <h3>John Doe</h3>
    <p>Developer</p>
  </div>

  <!-- More users... -->
</div>

<!-- User Modal -->
<div class="modal" id="user-modal">
  <div class="modal-content">
    <button class="modal-close">&times;</button>
    <div class="modal-body">
      <div class="user-profile">
        <img data-modal-target="avatar" class="avatar-large">
        <h2 data-modal-target="name"></h2>
        <p class="role" data-modal-target="role"></p>
        <p class="email" data-modal-target="email"></p>
        <p class="bio" data-modal-target="bio"></p>
      </div>
    </div>
  </div>
</div>

3. Image Lightbox

<!-- Image Grid -->
<div class="image-grid">
  <div class="image-item"
       data-modal="lightbox"
       data-modal-gallery="true"
       data-modal-bind="image:item.src, caption:item.caption"
       data-src="/images/photo1.jpg"
       data-caption="Photo 1">
    <img src="/images/thumb1.jpg">
  </div>

  <div class="image-item"
       data-modal="lightbox"
       data-modal-gallery="true"
       data-modal-bind="image:item.src, caption:item.caption"
       data-src="/images/photo2.jpg"
       data-caption="Photo 2">
    <img src="/images/thumb2.jpg">
  </div>

  <!-- More images... -->
</div>

<!-- Lightbox Modal -->
<div class="modal modal-lightbox" id="lightbox">
  <div class="modal-content">
    <button class="modal-close">&times;</button>
    <img data-modal-target="image" class="lightbox-image">
    <p class="caption" data-modal-target="caption"></p>
    <button class="lightbox-prev" data-modal-nav="prev">‹</button>
    <button class="lightbox-next" data-modal-nav="next">›</button>
  </div>
</div>

<style>
.modal-lightbox {
  background: rgba(0,0,0,0.9);
}
.lightbox-image {
  max-width: 90vw;
  max-height: 80vh;
  object-fit: contain;
}
.lightbox-prev,
.lightbox-next {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  font-size: 48px;
  color: white;
  background: none;
  border: none;
  cursor: pointer;
}
.lightbox-prev { left: 20px; }
.lightbox-next { right: 20px; }
</style>

4. Notification Details

<!-- Notification List -->
<div class="notification-list">
  <div class="notification-item"
       data-modal="notification-modal"
       data-modal-bind="
         title:item.title,
         message:item.message,
         time:item.time,
         type:item.type
       "
       data-title="New Notification"
       data-message="You have a new message from Admin"
       data-time="5 minutes ago"
       data-type="info">
    <span class="icon">🔔</span>
    <div class="content">
      <h4>New Notification</h4>
      <p>You have a new message...</p>
    </div>
  </div>

  <!-- More notifications... -->
</div>

<!-- Notification Modal -->
<div class="modal" id="notification-modal">
  <div class="modal-content">
    <button class="modal-close">&times;</button>
    <div class="modal-body">
      <h2 data-modal-target="title"></h2>
      <p class="time" data-modal-target="time"></p>
      <p class="message" data-modal-target="message"></p>
      <span class="badge" data-modal-target="type"></span>
    </div>
  </div>
</div>

5. Dynamic Content with Template

<!-- Template Loop -->
<div data-for="article of articles">
  <article class="article-card"
           data-modal="article-modal"
           data-modal-bind="
             title:item.title,
             author:item.author,
             date:item.date,
             content:item.content,
             tags:item.tags
           "
           data-title="{article.title}"
           data-author="{article.author}"
           data-date="{article.date}"
           data-content="{article.content}"
           data-tags="{article.tags}">
    <h3>{article.title}</h3>
    <p class="meta">By {article.author} • {article.date}</p>
  </article>
</div>

<!-- Article Modal -->
<div class="modal" id="article-modal">
  <div class="modal-content">
    <button class="modal-close">&times;</button>
    <div class="modal-body">
      <h1 data-modal-target="title"></h1>
      <p class="meta">
        By <span data-modal-target="author"></span>
        • <span data-modal-target="date"></span>
      </p>
      <div data-modal-target="content" data-modal-html></div>
      <div class="tags" data-modal-target="tags"></div>
    </div>
  </div>
</div>

API Reference

Configuration

ModalDataBinder.config = {
  debug: true,                    // Enable debug logging
  galleryClass: 'modal-gallery',  // CSS class for gallery
  bindAttribute: 'data-modal-bind',
  targetAttribute: 'data-modal-target',
  sanitize: true                  // Enable sanitization
};

State

ModalDataBinder.state = {
  initialized: false,
  modalInstances: new Map(),  // modalId => Modal instance or {element, isPreExisting}
  galleryData: new Map()      // modalId => {items, currentIndex, loop}
};

Methods

Public Methods

  • init() - Initialize ModalDataBinder
  • openModalWithData(modalId, data, options) - Open modal with data
  • updateModalData(modalId, data) - Update data
  • closeModal(modalId) - Close modal
  • navigateGallery(modalId, direction) - Navigate gallery
  • getModalData(modalId) - Get data
  • destroy() - Destroy all instances

Private Methods

  • bindModalTriggers() - Bind click events
  • bindGalleryNavigation() - Bind navigation events
  • handleModalTrigger(trigger) - Handle trigger click
  • parseBindData(bindStr) - Parse bind string to object
  • evaluateBindData(bindData, trigger) - Evaluate expressions
  • findTemplateContext(element) - Find template context
  • evaluateExpression(expr, context, trigger) - Evaluate expression
  • setupGallery(modalId, trigger, data) - Setup gallery
  • _bindDataToElement(modalElement, data) - Bind data to element
  • _setElementContent(element, value, key) - Set content
  • _showPreExistingModal(modalElement) - Show pre-existing modal
  • _bindPreExistingModalEvents(modalElement) - Bind events
  • log(...args) - Debug logging

Best Practices

✅ Do

1. Use Data Attributes for Data

<!-- ✅ Good - use data attributes -->
<div data-modal="my-modal"
     data-modal-bind="name:item.name, email:item.email"
     data-name="John"
     data-email="john@example.com">
  Click
</div>

<!-- ❌ Bad - hardcode values -->
<div data-modal="my-modal"
     data-modal-bind="name:'John', email:'john@example.com'">
  Click
</div>

2. Use Gallery Mode for Collections

<!-- ✅ Good - gallery mode -->
<div data-modal="gallery"
     data-modal-gallery="true"
     data-modal-bind="...">
  Item
</div>

<!-- ❌ Bad - separate modal per item -->
<div data-modal="item1-modal">Item 1</div>
<div data-modal="item2-modal">Item 2</div>

3. Use Semantic HTML

<!-- ✅ Good - semantic -->
<button data-modal="confirm-modal">Confirm</button>

<!-- ❌ Bad - div as button -->
<div data-modal="confirm-modal">Confirm</div>

❌ Don't

1. Don't Put HTML in Bind Expressions

<!-- ❌ Dangerous - XSS -->
<div data-modal="my-modal"
     data-modal-bind="content:'<script>alert(1)</script>'">
  Click
</div>

<!-- ✅ Safe - use data attributes -->
<div data-modal="my-modal"
     data-modal-bind="content:item.content"
     data-content="Safe content">
  Click
</div>

2. Don't Duplicate Modal IDs

<!-- ❌ Bad - duplicate IDs -->
<div class="modal" id="my-modal">...</div>
<div class="modal" id="my-modal">...</div>

<!-- ✅ Good - unique IDs -->
<div class="modal" id="modal-1">...</div>
<div class="modal" id="modal-2">...</div>

3. Don't Forget Close Button

<!-- ❌ Bad - no close button -->
<div class="modal" id="my-modal">
  <div class="modal-content">
    <p>Content</p>
  </div>
</div>

<!-- ✅ Good - has close button -->
<div class="modal" id="my-modal">
  <div class="modal-content">
    <button class="modal-close">&times;</button>
    <p>Content</p>
  </div>
</div>

💡 Tips

1. Use Debug Mode

// Enable debug logging
ModalDataBinder.config.debug = true;

// See logs in console
// [ModalDataBinder] Using pre-existing modal element: my-modal
// [ModalDataBinder] Opened modal my-modal with data {...}

2. Check Modal State

// View modal instances
console.log(ModalDataBinder.state.modalInstances);

// View gallery data
console.log(ModalDataBinder.state.galleryData);

3. Use CSS for Animation

.modal {
  opacity: 0;
  transition: opacity 0.3s;
}

.modal.show {
  opacity: 1;
}

See Also