Now.js Framework Documentation
data-model
data-model
Overview
data-model creates two-way data binding between form inputs and data state. Changes in the input update the data, and changes in data update the input.
When to use:
- Form inputs that need to sync with state
- Real-time form validation
- Interactive controls
Why use it:
- ✅ True two-way binding
- ✅ Supports all input types
- ✅ Built-in modifiers (lazy, number, trim)
- ✅ Automatic type handling
Basic Usage
Text Input
<input type="text" data-model="username">
<p>Hello, <span data-text="username"></span>!</p>As you type, both update in sync.
Initial Value from State
{
username: "John"
}<input type="text" data-model="username">
<!-- Input shows "John" initially -->Syntax
<input data-model="dataPath.modifier1.modifier2">| Part | Description |
|---|---|
dataPath |
Path to state property |
.modifier |
Optional modifiers |
Supported Input Types
Text Inputs
<input type="text" data-model="form.name">
<input type="email" data-model="form.email">
<input type="password" data-model="form.password">
<input type="tel" data-model="form.phone">
<input type="url" data-model="form.website">
<input type="search" data-model="searchQuery">Textarea
<textarea data-model="form.message"></textarea>Select
<select data-model="form.country">
<option value="">Choose...</option>
<option value="th">Thailand</option>
<option value="us">United States</option>
</select>Multi-Select
<select multiple data-model="selectedItems">
<option value="a">Option A</option>
<option value="b">Option B</option>
<option value="c">Option C</option>
</select>State receives array: ["a", "c"]
Checkbox
<input type="checkbox" data-model="form.agreeTerms">State receives boolean: true or false
Radio Buttons
<input type="radio" name="gender" value="male" data-model="form.gender">
<input type="radio" name="gender" value="female" data-model="form.gender">
<input type="radio" name="gender" value="other" data-model="form.gender">State receives selected value: "male", "female", or "other"
File Input
<input type="file" data-model="uploadedFiles">State receives FileList object
Number Input
<input type="number" data-model="form.quantity">
<input type="range" data-model="form.volume">Modifiers
.lazy
Update on change event instead of input (only when input loses focus):
<input type="text" data-model="search.lazy">Useful for expensive operations that shouldn't run on every keystroke.
.number
Convert value to number:
<input type="text" data-model="form.age.number">// Without .number: "25" (string)
// With .number: 25 (number).trim
Trim whitespace from value:
<input type="text" data-model="form.name.trim">// Input: " John Doe "
// State: "John Doe"Combining Modifiers
<input type="text" data-model="form.amount.number.trim">Advanced Examples
Complete Form
<form data-form="registration">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" data-model="form.name.trim">
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" data-model="form.email.trim">
</div>
<div class="form-group">
<label for="age">Age</label>
<input type="number" id="age" data-model="form.age.number">
</div>
<div class="form-group">
<label for="country">Country</label>
<select id="country" data-model="form.country">
<option value="">Select...</option>
<option value="th">Thailand</option>
<option value="us">United States</option>
</select>
</div>
<div class="form-group">
<label>
<input type="checkbox" data-model="form.subscribe">
Subscribe to newsletter
</label>
</div>
<button type="submit">Submit</button>
</form>Live Search
<div class="search-box">
<input type="text"
placeholder="Search..."
data-model="searchQuery">
<div data-if="searchQuery.length > 0">
<p>Searching for: <span data-text="searchQuery"></span></p>
</div>
<div data-if="searchResults.length > 0" data-for="result in searchResults">
<template>
<div class="result" data-text="result.title"></div>
</template>
</div>
</div>Settings Panel
<div class="settings">
<h3>Settings</h3>
<label>
<input type="checkbox" data-model="settings.darkMode">
Dark Mode
</label>
<label>
<input type="checkbox" data-model="settings.notifications">
Enable Notifications
</label>
<label>
Volume: <span data-text="settings.volume"></span>%
<input type="range"
min="0" max="100"
data-model="settings.volume.number">
</label>
<label>
Language:
<select data-model="settings.language">
<option value="en">English</option>
<option value="th">ไทย</option>
</select>
</label>
</div>With Validation
<form data-form="contact">
<div class="form-group">
<label>Email</label>
<input type="email"
data-model="form.email"
data-on="blur:validateEmail">
<span data-if="errors.email" class="error" data-text="errors.email"></span>
</div>
<div class="form-group">
<label>Message</label>
<textarea data-model="form.message"
data-on="input:validateMessage"></textarea>
<span class="char-count">
<span data-text="form.message.length"></span>/500
</span>
</div>
</form>Multi-Select Tags
<div class="tag-selector">
<select multiple data-model="selectedTags">
<option value="html">HTML</option>
<option value="css">CSS</option>
<option value="javascript">JavaScript</option>
<option value="php">PHP</option>
</select>
<div class="selected-tags" data-if="selectedTags.length > 0">
<span>Selected: </span>
<span data-for="tag in selectedTags">
<template>
<span class="tag" data-text="tag"></span>
</template>
</span>
</div>
</div>API Reference
State Updates
| Input Type | State Value |
|---|---|
| Text/Email/etc. | String |
| Number | String (use .number for Number) |
| Checkbox | Boolean |
| Radio | Selected value (String) |
| Select | Selected value (String) |
| Select multiple | Array of values |
| File | FileList object |
Focus Handling
data-model preserves cursor position during updates:
// Cursor position and selection are maintained
// when external updates happen while user is editingCommon Pitfalls
⚠️ 1. Number Type Without Modifier
<!-- ❌ Returns string "25" -->
<input type="number" data-model="age">
<!-- ✅ Returns number 25 -->
<input type="number" data-model="age.number">⚠️ 2. Checkbox Initial State
// ❌ Undefined won't check/uncheck properly
{ agreeTerms: undefined }
// ✅ Use boolean
{ agreeTerms: false }⚠️ 3. Select Without Empty Option
<!-- ❌ First option auto-selected but state may not reflect -->
<select data-model="country">
<option value="th">Thailand</option>
<option value="us">USA</option>
</select>
<!-- ✅ Add empty option for initial state -->
<select data-model="country">
<option value="">Select country...</option>
<option value="th">Thailand</option>
<option value="us">USA</option>
</select>⚠️ 4. Reading vs Setting Files
<!-- data-model can READ file selection -->
<input type="file" data-model="selectedFile">
<!-- ❌ Cannot SET file value (browser security) -->
<!-- Use data-files for displaying existing files -->⚠️ 5. Nested Property Path
<!-- ❌ If form object doesn't exist -->
<input data-model="form.name">
<!-- ✅ Initialize nested object first -->
{
form: {
name: ""
}
}Comparison: data-model vs data-attr
| Feature | data-model | data-attr="value:..." |
|---|---|---|
| Two-way binding | ✅ Yes | ❌ One-way only |
| Input → State | ✅ Yes | ❌ No |
| Modifiers | .lazy, .number, .trim |
None |
| All input types | ✅ Yes | Basic only |
Related Directives
- data-attr - For one-way attribute binding
- data-checked - Alternative for checkboxes
- data-on - For custom input handling