sercrod

*updated

Summary

*updated registers a post-update hook.

It is evaluated after a Sercrod host has finished its update cycle, and can be attached in two places:

Typical uses:

For propagating updates upwards in the Sercrod tree, see *updated-propagate / n-updated-propagate.

Basic example

A simple host-level hook that logs every render:

<script>
  function onHostUpdated(event) {
    console.log("Host updated:", event.type, "id:", event.target.id);
  }
</script>

<serc-rod
  id="app"
  data='{"count": 0}'
  *updated="onHostUpdated($event)"
>
  <button *on="click: count++">Increment</button>
</serc-rod>

Behavior:

Behavior

High-level behavior:

Aliases:

Handler resolution on Sercrod hosts

For Sercrod hosts, the runtime interprets the attribute value in three stages.

  1. Split into tokens
  1. Object-bulk entries

For each entry, Sercrod first checks if it is the name of a global object:

Example:

<script>
  const AppUpdatedHooks = {
    log(host) {
      console.log("Updated:", host.id);
    },
    measure(host) {
      console.log("Children:", host.childElementCount);
    }
  };
</script>

<serc-rod
  id="app"
  data='{"value": 1}'
  *updated="AppUpdatedHooks"
>
  <p *print="value"></p>
</serc-rod>

Notes:

  1. Selector form "(selector)"

If an entry starts and ends with parentheses, Sercrod treats the content as a CSS selector:

Resolution:

Effectively:

  1. Fallback expression

If no entries were handled as an object or selector, Sercrod evaluates the entire attribute string once as a JavaScript statement in the host data scope:

Details:

Handler resolution on ordinary elements

For *updated / n-updated attached to ordinary elements inside a Sercrod host, the runtime uses a separate path:

For each visited element with *updated or n-updated:

  1. Determine the host
  1. Build the evaluation environment
  1. Interpret the value

Important current behavior:

Evaluation timing

*updated participates in the Sercrod host update cycle.

For each host:

Practical consequences:

Execution model

On Sercrod hosts, conceptually:

  1. Sercrod obtains the attribute value and splits it into entries.

  2. For each entry:

    • If it matches a global object name, call each function in that object with the host as argument.

    • Else if it is of the form "(selector)", resolve the selector relative to the nearest Sercrod ancestor and:

      • Call update(true) on the matching root.
      • Call _call_updated_hooks on matching descendant Sercrod hosts.
  3. If none of the entries were handled in special ways, treat the whole attribute value as one JavaScript statement and evaluate it in the host’s data scope with el and $event bound appropriately.

On ordinary elements inside a host:

  1. After the host finishes a real render, it walks its subtree (excluding nested Sercrod instances).

  2. For each element with *updated / n-updated:

    • Determine the host for that element.
    • If the value is "(selector)", re-render matching Sercrod hosts.
    • Else evaluate the attribute value as a JavaScript statement in the host’s data scope, with el bound to the element.

Infinite loop protection:

Variable creation and scope layering

For host-level *updated fallback expressions:

For *updated on ordinary elements:

Other scope rules:

Parent access

*updated does not create new parent pointers itself, but leverages the shared scope model:

In both cases, you do not get implicit references to arbitrary DOM ancestors beyond what you can derive via el and selectors.

Use with conditionals and loops

*updated is not structural and does not conflict with *if, *for, *each, or other structural directives.

Guidelines:

Best practices

Examples

Host-level lifecycle hook:

<script>
  function focusFirstInput(event) {
    const host = event.target;
    const input = host.querySelector("input, textarea, select");
    if (input) input.focus();
  }
</script>

<serc-rod
  id="formHost"
  data='{"showForm": true}'
  *updated="focusFirstInput($event)"
>
  <form *if="showForm">
    <input type="text" name="name">
    <button type="submit">Save</button>
  </form>
</serc-rod>

Element-level hook inside a repeated list:

<script>
  function highlightNewItem(el) {
    el.classList.add("is-new");
    setTimeout(() => el.classList.remove("is-new"), 300);
  }
</script>

<serc-rod id="list" data='{"items":[{"id":1},{"id":2}]}'>
  <ul>
    <li
      *for="item of items"
      *updated="highlightNewItem(el)"
    >
      <span *print="item.id"></span>
    </li>
  </ul>
</serc-rod>

Host-level selector form:

<serc-rod id="root" data='{"filter": "all"}'>
  <serc-rod
    id="panelA"
    data='{"items": []}'
    *updated="(serc-rod#panelB)"
  >
    <!-- ... -->
  </serc-rod>

  <serc-rod
    id="panelB"
    data='{"items": []}'
    *updated="onPanelBUpdated($event)"
  >
    <!-- ... -->
  </serc-rod>
</serc-rod>

Notes