sercrod

*updated-propagate

Summary

*updated-propagate requests a forced update on another Sercrod host after the current host or element has been updated. It does not evaluate a JavaScript expression. Instead, it reads a small string syntax and calls update(true, caller) on a single target Sercrod host.

Alias:

Key points:

Basic example

Propagate from a nested Sercrod to its outer root:

<serc-rod id="root" data='{"message":"Hello"}'>
  <h1 *print="message"></h1>

  <serc-rod id="child"
           data='{"message":"Child"}'
           *updated="onChildUpdated"
           *updated-propagate="root">
    <p *print="message"></p>
  </serc-rod>
</serc-rod>

Behavior:

Behavior

High level behavior:

Important:

Target specification syntax

The value of *updated-propagate (or n-updated-propagate) is called the target spec. The runtime interprets it in the following order:

  1. Empty or omitted

    • If the attribute is present but has no value (for example <div *updated-propagate></div>) or an empty string, Sercrod treats it as "1".

    • On a Sercrod host:

      • "1" means “go up one Sercrod host and update that ancestor”.
    • On an ordinary element:

      • Due to the current implementation, "1" does not result in any propagation.
      • If you want to reach the nearest Sercrod host from a normal element, you must specify at least "2" explicitly.
  2. Parenthesized selector: "(selector)"

    • If the value matches "(...)":

      • Sercrod strips the outer parentheses and treats the inside as a CSS selector.
      • It then calls closest(selector) from the element where the directive lives.
      • If the result is a Sercrod host, that host receives a forced update.

    Examples:

    <!-- On a Sercrod host: propagate to the nearest ancestor matching .layout-root -->
    <serc-rod *updated-propagate="(.layout-root)">
      ...
    </serc-rod>
    
    <!-- On a child element: propagate to the closest .card host -->
    <div class="card-body" *updated-propagate="(.card)">
      ...
    </div>
  3. Keyword "root"

    • Special keyword that refers to the top-level Sercrod host around the current element.

    • On a Sercrod host:

      • Sercrod walks upward and uses the first Sercrod host it can find above the current one.
      • That host is treated as “root” for this evaluation, and receives update(true, caller).
    • On an ordinary element inside a host:

      • The host uses its own notion of the outermost Sercrod root, if available.
      • If not available, it walks upward, finds a Sercrod host, then climbs to the outermost Sercrod ancestor.
      • That top-level host receives update(true, host).

    This is the recommended form when you want to ensure that “the top Sercrod container for this UI” refreshes, regardless of nesting depth.

  4. Numeric depth: "N"

    • If the value consists only of digits, Sercrod parses it as an integer depth.

    • On a Sercrod host:

      • The number counts Sercrod ancestors.
      • Sercrod starts from parentElement and climbs upward.
      • Each time it encounters a Sercrod host, it decrements the counter.
      • When the counter reaches zero on a Sercrod host, that host receives update(true, callerHost).

      Examples:

      • "1": the nearest Sercrod parent (if any).
      • "2": the Sercrod grandparent, and so on.
    • On an ordinary element inside a host:

      • The numeric depth is interpreted relative to the nearest Sercrod host above the element.
      • Before climbing the DOM, Sercrod subtracts 1 from the specified depth when the directive is on a normal element.
      • Then it climbs upwards and decrements the counter on each Sercrod ancestor, similar to the host case.

      Consequences:

      • With the current implementation:
        • "1" on a normal element effectively results in no propagation.
        • "2" propagates to the nearest Sercrod host.
        • Higher numbers target further Sercrod ancestors.

      Recommendation:

      • Prefer "root" or "(selector)" when targeting from normal elements.
      • If you do use numbers on child elements, start from "2" for “nearest Sercrod host”.
  5. Fallback: bare selector string

    • If the spec does not match any of the above patterns, Sercrod treats it as a CSS selector and calls closest(spec) from the element.
    • If the closest match is a Sercrod host, that host receives update(true, caller).

    Example:

    <!-- Propagate to the nearest Sercrod matching .panel-root -->
    <div *updated-propagate=".panel-root"></div>

Evaluation timing

*updated-propagate is evaluated after the main update work of the host:

Effects on the target host:

Loop prevention:

Execution model

Conceptually, Sercrod performs the following steps whenever it evaluates *updated-propagate:

  1. Read the raw attribute:

    • On a host: this.getAttribute("*updated-propagate") || this.getAttribute("n-updated-propagate").
    • On a normal element: el.getAttribute("*updated-propagate") || el.getAttribute("n-updated-propagate").
  2. Normalize the spec:

    • If the value is null, there is no propagation.
    • If the value is the empty string, it is treated as "1".
    • The string is trimmed of surrounding whitespace.
  3. Interpret the spec:

    • If it starts and ends with parentheses, treat it as a selector inside "(...)".
    • Else if it is exactly "root", resolve the appropriate root Sercrod host.
    • Else if it is all digits, treat it as a numeric depth (with the extra adjustment for normal elements).
    • Else treat it as a bare CSS selector.
  4. Resolve the target host:

    • Use closest(selector) on the appropriate starting element.
    • Or walk upward through parents, counting Sercrod hosts.
    • Or use the topmost Sercrod root for "root".
  5. Call update:

    • If a Sercrod host is found, call target.update(true, caller) where:
      • caller is the current host when called from a host-level *updated-propagate.
      • caller is the scanning host when called from a normal child element.

No errors are thrown to user code:

Variable creation and scope layering

*updated-propagate does not introduce any new variables:

Any data or event context used by the target host’s *updated handlers is created by that host itself, according to the rules of *updated, not by *updated-propagate.

Parent access

*updated-propagate is a routing directive, not a data access directive:

The main “parent” concept here is the structural parent host in the DOM tree, as used by numeric depth and the root keyword.

Use with *updated and events

*updated-propagate is designed to complement *updated:

Event objects:

Use with conditionals and loops

*updated-propagate is independent of structural directives such as *if, *for, and *each:

There are no special rules tying *updated-propagate to conditionals or loops beyond the normal update timing.

Best practices

Examples

Propagate to the nearest parent Sercrod host:

<serc-rod id="parent" data='{"value": 0}'>
  <serc-rod id="child" *updated-propagate="1">
    <p>Child content</p>
  </serc-rod>
</serc-rod>

Propagate from an inner element to the root:

<serc-rod id="root" data='{"saved": false}'>
  <form>
    <button type="submit"
            *updated="onButtonUpdated"
            *updated-propagate="root">
      Save
    </button>
  </form>
</serc-rod>

Using a CSS selector in parentheses:

<serc-rod class="panel" data='{"message": ""}'>
  <div class="panel-body">
    <input type="text"
           *input="message = $event.target.value"
           *updated-propagate="(.panel)">
  </div>
</serc-rod>

Notes