sercrod

@change

Summary

@change attaches a handler to the native change event on an element. The expression on @change is evaluated when the browser fires a change event, typically after the user has committed a new value for a form control. Typical uses include synchronising derived state, running validation at commit time, and triggering side effects when a select, checkbox, or other control is changed.

@change is part of the general event handler family (such as @click, @input, @blur) and follows the same evaluation model as other at sign directives.

Basic example

A select that updates a selected value when the user chooses an option:

<serc-rod id="app" data='{
  "selected": "apple",
  "options": ["apple", "banana", "orange"]
}'>
  <label>
    Favorite fruit:
    <select :value="selected"
            @change="selected = $event.target.value">
      <option *for="opt of options"
              :value="opt"
              *textContent="opt"></option>
    </select>
  </label>

  <p>
    You chose:
    <span *textContent="selected"></span>
  </p>
</serc-rod>

Behaviour:

Behavior

Core rules for @change:

Evaluation timing

@change follows the normal rendering and event wiring phases:

  1. Structural directives such as *if, *for, *each, *switch, *include and *import decide whether an element exists and what its children look like.
  2. Once an element is kept, Sercrod walks its attributes. Attributes whose names start with the configured events prefix (by default @) are treated as event handlers.
  3. For an attribute named @change or equivalent with modifiers, Sercrod calls the dedicated event renderer to register a change listener.
  4. On later renders, Sercrod reattaches handlers as needed, replacing previous listeners for the same event name on the same element.

When the browser fires change on the element, the expression is executed immediately in the same turn as the native event.

Execution model

At a high level, Sercrod processes @change as follows:

  1. During rendering, Sercrod finds an attribute whose name starts with the events prefix and whose event name part is change.
  2. It extracts:
    • The event name change.
    • The set of modifiers such as prevent, stop, once, capture or passive.
    • The expression string from the attribute value.
  3. It builds an evaluation scope that:
    • Exposes host data and local scope variables.
    • Provides $event and $e for the native event and el and $el for the element.
    • Propagates writes back into host data when assigning to known keys.
  4. Sercrod defines a handler function that:
    • Applies .prevent and .stop modifiers to the native event if requested.
    • Evaluates the expression through Sercrod’s event evaluator with the prepared scope and context.
    • Decides how to update the host after the handler runs.
  5. For each element, Sercrod stores a per event handler map so that re rendering removes the previous handler for that event before adding the new one. For the change event there is at most one active handler per element; the last declaration wins.

Error handling:

Update behaviour after @change

After a @change handler finishes, Sercrod decides how to refresh the view:

For @change specifically:

Use with form fields and data bindings

@change is especially useful on form controls where the committed value matters more than each keystroke.

With n input or *input:

Example: normalise and mark dirty when a field changes:

<serc-rod id="app" data='{
  "profile": { "age": 30 },
  "dirty":   { "age": false }
}'>
  <input type="number"
         n-input="profile.age"
         @change="
           profile.age = Number($event.target.value || 0);
           dirty.age = true;
         ">
</serc-rod>

With checkbox and radio:

<serc-rod id="app" data='{
  "newsletter": false
}'>
  <label>
    <input type="checkbox"
           :checked="newsletter"
           @change="newsletter = $event.target.checked">
    Receive newsletter
  </label>
</serc-rod>

With select multiple:

<serc-rod id="app" data='{
  "selected_tags": []
}'>
  <select multiple
          @change="
            selected_tags = Array.from($event.target.selectedOptions)
                                  .map(o => o.value);
          ">
    <option value="news">News</option>
    <option value="offers">Offers</option>
    <option value="tips">Tips</option>
  </select>
</serc-rod>

In each case, @change runs only when the control’s value is committed by the browser, not on every key stroke, making it suitable for validations or side effects that should not run too frequently.

Use with conditionals and loops

As with other event directives, @change composes with structural directives that control the element itself.

Inside conditionals:

<input type="text"
       *if="mode === 'edit'"
       :value="item.title"
       @change="item.title_saved = false">

Inside loops:

<ul>
  <li *for="item of items">
    <input type="text"
           :value="item.label"
           @change="item.touched = true">
  </li>
</ul>

Each instance inside the loop receives its own @change handler bound to that iteration’s item. If a structural directive removes or replaces the element on update, Sercrod re attaches the @change handler to the new element as part of the normal render process.

Sercrod specific restrictions

@change follows the general rules for event directives and has no extra, unique prohibitions:

No additional exclusion rules are imposed that are specific to @change.

Best practices

Additional examples

Trigger validation logic on change:

<serc-rod id="app" data='{
  "value": "",
  "error": ""
}'>
  <input type="text"
         :value="value"
         @change="
           const v = $event.target.value.trim();
           value = v;
           error = v ? '' : 'This field is required.';
         ">

  <p *if="error" *textContent="error"></p>
</serc-rod>

Integrate with a manual save action:

<serc-rod id="app" data='{
  "draft": "",
  "saved": "",
  "dirty": false
}'>
  <textarea :value="draft"
            @change="dirty = true"></textarea>

  <button type="button"
          @click="
            saved = draft;
            dirty = false;
          ">
    Save
  </button>

  <p *if="dirty">
    You have unsaved changes.
  </p>
</serc-rod>

Notes