Now.js Framework Documentation
ModalDataBinder - Declarative Modal Data Binding
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
- Overview
- Installation
- Basic Usage
- HTML Attributes
- Template Context Evaluation
- Gallery Mode
- Working with Modal Elements
- JavaScript API
- Usage Examples
- API Reference
- 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
Modalclass 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 objectDependencies
- 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">×</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">×</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>3. Gallery Mode
<!-- 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">×</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) |
Modal Target Attributes
| Attribute | Type | Description |
|---|---|---|
data-modal-target |
string | Data key name to bind |
data-modal-html |
boolean | Use innerHTML instead of textContent |
Navigation Attributes
| 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.title→data-titleitem.description→data-descriptionitem.imageUrl→data-image-url(camelCase → kebab-case)
Gallery Mode
Gallery mode enables navigation between items:
Enable Gallery
<!-- 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">×</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>Gallery Options
<!-- 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>Gallery Navigation
<!-- 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">×</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 IDdata(Object) - Data to bindoptions(Object) - Additional options
updateModalData(modalId, data)
Update data of open modal
ModalDataBinder.updateModalData('my-modal', {
message: 'Updated message'
});Parameters:
modalId(string) - Modal IDdata(Object) - Data to update
closeModal(modalId)
Close modal
ModalDataBinder.closeModal('my-modal');Parameters:
modalId(string) - Modal ID
navigateGallery(modalId, direction)
Navigate gallery
// Go to next
ModalDataBinder.navigateGallery('gallery-modal', 'next');
// Go to previous
ModalDataBinder.navigateGallery('gallery-modal', 'prev');Parameters:
modalId(string) - Modal IDdirection(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
1. Product Gallery
<!-- 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">×</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">×</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">×</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">×</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">×</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 ModalDataBinderopenModalWithData(modalId, data, options)- Open modal with dataupdateModalData(modalId, data)- Update datacloseModal(modalId)- Close modalnavigateGallery(modalId, direction)- Navigate gallerygetModalData(modalId)- Get datadestroy()- Destroy all instances
Private Methods
bindModalTriggers()- Bind click eventsbindGalleryNavigation()- Bind navigation eventshandleModalTrigger(trigger)- Handle trigger clickparseBindData(bindStr)- Parse bind string to objectevaluateBindData(bindData, trigger)- Evaluate expressionsfindTemplateContext(element)- Find template contextevaluateExpression(expr, context, trigger)- Evaluate expressionsetupGallery(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 eventslog(...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">×</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
- Modal - Modal class for programmatic modal creation
- BackdropManager - Backdrop management
- TemplateManager - Template rendering system