Now.js Framework Documentation

Now.js Framework Documentation

data-model

EN 11 Dec 2025 11:58

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>
<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 editing

Common 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