sercrod

Attribute bindings (fallback for :name)

Summary

This page describes the general behavior of Sercrod’s attribute bindings that do not have their own dedicated manual.

Any attribute whose name starts with : (for example :title, :aria-label, :data-id) participates in this mechanism. The expression to the right of = is evaluated, and the result is mapped to a plain HTML attribute on the rendered element.

Typical examples that use this fallback:

Attributes such as:

have (or may have) their own dedicated manuals that describe additional details. However, they still share the generic evaluation pipeline described here.

Reserved keys:

Basic example

A typical use of fallback attribute bindings:

<serc-rod id="user-card" data='{
  "user": {
    "id": "u-123",
    "name": "Alice",
    "role": "admin",
    "active": true
  }
}'>
  <div
    :data-id="user.id"
    :aria-label="user.name"
    :role="user.role"
    :tabindex="user.active ? 0 : -1"
  >
    <span *print="user.name"></span>
  </div>
</serc-rod>

Behavior:

Behavior

At render time, Sercrod scans each element for attributes that start with ::

Attribute name:

Special cases in the fallback pipeline:

Reserved and excluded:

Value mapping

The generic fallback follows a small set of rules to decide how to apply values:

For non-special attribute names (everything except class, style, value and the URL-like list):

For URL-like attributes:

For :value on form controls:

Filters and customization

Attribute bindings use the following hooks on the Sercrod class:

The fallback implementation behaves like this after the filter:

Error handling

If evaluation of the attribute expression throws an error:

This behavior ensures that failures during attribute evaluation do not break the render process; instead, the element falls back to a neutral state.

Evaluation timing

Attribute bindings run as part of _renderElement, after structural directives have decided whether the element will be rendered.

In broad order:

  1. Structural directives (*if, *for, *each, *switch, *template, *include, and similar) decide:

    • Whether the element is rendered at all.
    • How many copies (if any) are created.
    • Which children will be present.
  2. For each concrete element being rendered, Sercrod evaluates attribute bindings:

    • :class, :style, :value, and all other :name attributes (except :text / :html) are processed.
    • Each attribute is evaluated once for the current scope.
  3. Event bindings and other element-level behaviors are attached.

  4. Text content, %expr% expansions, inner HTML, and children are rendered.

Implications:

Execution model

For the fallback path, you can think of :name="expr" as:

  1. Parse:

    • Collect attributes whose names start with : on the template node.
    • For each attribute, compute name = attr.name.slice(1).
  2. Evaluate:

    • Evaluate expr using the current scope with a context { el: templateNode, mode: "attr:"+name }.
  3. Apply:

    • If name === "class":
      • Use the class-specific rules (string, array, object).
    • Else if name === "style":
      • Use the style-specific rule and Sercrod._filters.style.
    • Else if name === "value" and the element is a form control:
      • Assign to both el.value and the value attribute.
    • Else if name is URL-like:
      • Pass through Sercrod._filters.url.
    • Otherwise:
      • Pass through Sercrod._filters.attr and set or remove the attribute accordingly.
  4. Cleanup (optional):

    • If the runtime is configured with cleanup.handlers = true, the original :name attributes are removed from the output DOM.

Variable creation and scope layering

Attribute bindings do not create any new variables.

Available inside the expression:

Parent access

Attribute expressions can freely refer to outer data:

The fallback implementation does not alter how scope resolution works; it simply evaluates the expression under the usual rules.

Use with conditionals and loops

Attribute bindings are compatible with conditionals and loops:

As a rule of thumb:

Best practices

Examples

Using :aria-* and :data-* for accessibility and diagnostics:

<serc-rod id="menu" data='{
  "items": [
    { "id": "home", "label": "Home", "active": true },
    { "id": "settings", "label": "Settings", "active": false }
  ]
}'>
  <nav aria-label="Main menu">
    <ul>
      <li
        *for="item of items"
        :data-id="item.id"
        :aria-current="item.active ? 'page' : null"
        :tabindex="item.active ? 0 : -1"
      >
        <button
          type="button"
          :aria-pressed="item.active"
        >
          <span *print="item.label"></span>
        </button>
      </li>
    </ul>
  </nav>
</serc-rod>

Using :role and :data-* together:

<div
  :role="isDialogOpen ? 'dialog' : null"
  :data-state="isDialogOpen ? 'open' : 'closed'"
>
  <p *print="message"></p>
</div>

In these examples:

Notes