sercrod

@input

Summary

@input attaches a handler to the native input event on an element. The expression on @input is evaluated every time the browser fires an input event for that control. Typical uses include live validation, updating derived state as the user types, or reacting to slider or range changes.

@input is part of the event handler family (such as @click, @change, @blur) and follows the same general evaluation rules as other @ directives, with input-specific update behavior inside the Sercrod runtime.

Basic example

Tracking a live character count while the user types:

<serc-rod id="app" data='{
  "text": "",
  "length": 0
}'>
  <textarea :value="text"
            @input="length = ($event.target.value || '').length">
  </textarea>

  <p>
    Length: {{%length%}} characters
  </p>
</serc-rod>

Behavior:

Behavior

Core rules:

Sercrod does not change the native timing or semantics of the input event; it only evaluates the expression when the event occurs and then applies its usual re-render rules for input-like events.

Evaluation timing

For @input, evaluation follows the general event pipeline:

On subsequent renders (due to data changes or other events), Sercrod ensures that @input still points to the current expression and scope.

Execution model

Conceptually, the runtime behaves as follows for @input:

  1. During rendering, Sercrod finds an attribute whose name matches the event prefix plus input (by default @input).

  2. It extracts:

    • The event name input.
    • Any modifiers (for example, @input.prevent.update).
    • The raw expression string from the attribute value.
  3. Sercrod registers a native listener on the element using addEventListener("input", handler, options) with:

    • capture set if .capture modifier is present.
    • passive set if .passive modifier is present.
    • once set if .once modifier is present.
  4. When the browser fires input:

    • The handler applies modifiers:

      • .prevent calls event.preventDefault().
      • .stop calls event.stopPropagation().
    • Sercrod creates a proxy scope in which:

      • $event and $e refer to the native event.
      • el and $el refer to the target element.
      • Reads fall back from the local scope to window if a name is not found.
      • Writes update the local scope and, when relevant, the host data.
    • Sercrod calls eval_event with the expression and this proxy scope.

    • If evaluation throws, Sercrod catches the error and routes it through the configured logging mechanisms.

  5. After the expression runs, Sercrod decides how to refresh:

    • For @input, the event is classified as “input-like”, so Sercrod performs a lightweight children-only update of the host.
    • Non-input events may instead perform a full host update, depending on configuration and modifiers.
  6. If cleanup.handlers is enabled in the configuration, Sercrod removes the original @input attribute from the DOM after the listener is attached, leaving only the wired listener and any non-directive attributes.

The @input handler does not attempt to modify the value of the control by itself; it just runs your expression and then triggers the appropriate re-render path.

Use with form fields and data bindings

@input is often paired with form bindings and colon attributes:

Use with conditionals and loops

@input works well inside conditionals and loops:

If a structural directive replaces an input element on re-render, Sercrod re-attaches the @input handler on the new node as part of normal rendering.

Sercrod-specific restrictions

For @input there are no special “cannot combine on the same element” rules beyond the shared event rules:

Internally, Sercrod treats input as an “input-like” event and always performs a lightweight children-only update after your handler runs. In the current implementation, the generic .update and .noupdate modifiers do not suppress this behavior for @input.

Best practices

Additional examples

Live preview of formatted text:

<serc-rod id="app" data='{
  "source": "",
  "preview": ""
}'>
  <textarea *input="source"
            *eager
            @input="preview = source.toUpperCase()">
  </textarea>

  <h3>Preview</h3>
  <pre *print="preview"></pre>
</serc-rod>

Numeric input with live clamping:

<serc-rod id="app" data='{
  "volume": 50
}'>
  <input type="number" min="0" max="100"
         *input="volume"
         *eager
         @input="
           if (volume < 0)   volume = 0;
           if (volume > 100) volume = 100;
         ">

  <p>Volume: {{%volume%}}</p>
</serc-rod>

Logging raw input events for debugging:

<serc-rod id="app" data='{
  "log": []
}'>
  <input type="text"
         @input="log.push($event.target.value)">

  <ul>
    <li *for="v of log">
      {{%v%}}
    </li>
  </ul>
</serc-rod>

Notes