What each render-program op does to the DOM.
One function per op kind, each writing to exactly one node. These are the leaves of the new architecture: everything above them exists to arrange for the right one of these to run with the right value, and nothing below them touches the DOM.
Matching the string renderer's semantics
These replace a pipeline that escaped a value into HTML, parsed that HTML, and diffed the result into the document. Anything the round trip did incidentally is behaviour applications now depend on, so it is reproduced here deliberately rather than rediscovered as a bug report:
- Escaping. The old path escaped a value and the parser unescaped it, so
the text that reached the document was the value verbatim. Writing
node.datadirectly produces the same string and cannot be mis-escaped, because it never becomes markup at all. nullrenders as nothing, not as the string "null".SafeHtmlin a{{ }}interpolation is inserted as markup. The old path skipped escaping for it; a text write would have shown the tags. Text ops therefore check for it and hand over to the raw path.- Boolean attributes.
disabled="false"removed the attribute and set the property; anything else set both. - URL attributes were sanitised on every parsed tree, so they are sanitised on every write here.
Where the security boundary sits
Nowhere in this file is a value turned into markup unless the op is raw or
the value is a SafeHtml -- the same two doors the string renderer had, and
no new ones. Expressions are still evaluated by the AST evaluator through the
callback these functions are given; this module receives values, never source.
Members
(inner, constant) KEY_MODIFIERS :Map:.<string:, string:>
Key names the @keydown.enter family of modifiers filters on.
Type:
(inner, constant) VALUE_PROPERTY_TAGS :Set:.<string:>
Elements whose value attribute stops driving their value once a user has
typed into them.
Type:
(inner, constant) escaper :HtmlEscaper
The escaper the string renderer used, reused rather than reimplemented.
data-ax-html escapes a plain value and only lets a SafeHtml through. A
second escaping implementation here could drift from that one, and the
direction it would drift in is "escapes less".
Type:
Methods
(static) applyAttribute(element, name, value)
Sets an attribute from a whole-value expression.
Parameters:
| Name | Type | Description |
|---|---|---|
element |
Element | The bound element. |
name |
string | The attribute name. |
value |
any | The evaluated value. |
(static) applyAttributeParts(element, name, value)
Sets an attribute assembled from literal and expression parts.
Parameters:
| Name | Type | Description |
|---|---|---|
element |
Element | The bound element. |
name |
string | The attribute name. |
value |
string | The already-joined value. |
(static) applyBoolean(element, name, value)
Sets or removes a boolean attribute, and mirrors it onto the property.
Parameters:
| Name | Type | Description |
|---|---|---|
element |
Element | The bound element. |
name |
string | The attribute name. |
value |
any | The evaluated value. |
(static) applyClass(element, value, binding)
Applies a class binding, removing only the classes it previously added.
Parameters:
| Name | Type | Description |
|---|---|---|
element |
Element | The bound element. |
value |
any | A string of class names, or an object of name to flag. |
binding |
object | Per-binding state, holding the previous class list. |
(static) applyHtml(element, value)
Replaces an element's inner HTML from an expression.
Parameters:
| Name | Type | Description |
|---|---|---|
element |
Element | The bound element. |
value |
any | The evaluated value. |
(static) applyProp(element, name, value) → {boolean}
Stores an evaluated prop for a child component mounted at this element.
The value is held on the element rather than pushed straight into the child, because at the moment a prop op first runs the child does not exist yet -- the parent's tree is still detached. The owner reads these when it mounts or refreshes its children.
Values are kept as values. The string path carried props through
data-props-* attributes and re-evaluated them from source on every render
of the parent, which meant every prop of every child was re-evaluated
whenever anything in the parent changed.
Parameters:
| Name | Type | Description |
|---|---|---|
element |
Element | The child's mount point. |
name |
string | The prop name. |
value |
any | The evaluated value. |
Returns:
True when the stored value changed.
- Type
- boolean
(static) applyRaw(anchor, value, binding)
Replaces the markup a raw binding owns.
A raw binding owns a range rather than a node: one expression can produce any number of elements. The compiler's text marker stays in the document as an anchor, and the nodes the binding inserted are tracked so the next evaluation can remove exactly those and nothing else. Clearing by emptying the parent would take siblings that belong to other bindings.
Parameters:
| Name | Type | Description |
|---|---|---|
anchor |
Text | The anchor node the compiler reserved. |
value |
any | The evaluated value. |
binding |
object | Per-binding state holding the current range. |
(static) applyShow(element, value, binding)
Toggles an element's visibility, preserving its authored display value.
Parameters:
| Name | Type | Description |
|---|---|---|
element |
Element | The bound element. |
value |
any | The evaluated value; truthiness decides. |
binding |
object | Per-binding state, holding the authored display. |
(static) applyStyle(element, value, binding)
Applies an inline style binding, removing only the properties it set.
data-ax-style has been documented public API for as long as the directive
has existed, and applied nothing: the string renderer never implemented it,
so the E2E suite pinned it as a known gap. It is implemented here because the
compiled path is where a directive's behaviour now lives, and leaving it
unimplemented would have carried a documented no-op into the new
architecture.
Only the properties this binding wrote are cleared on change, so a style the
author wrote in the template's own style attribute survives.
Parameters:
| Name | Type | Description |
|---|---|---|
element |
Element | The bound element. |
value |
any | A CSS text string, or an object of property to value. |
binding |
object | Per-binding state, holding the previous properties. |
(static) applyText(node, value, binding)
Writes a value into a dynamic text node.
Parameters:
| Name | Type | Description |
|---|---|---|
node |
Text | The text node the compiler reserved. |
value |
any | The evaluated value. |
binding |
object | Per-binding state, used to manage raw ranges. |
(static) attachEvent(element, op, run)
Attaches a declared event handler to an element.
The string renderer re-read data-ax-event off the DOM and re-bound every
handler on every update, because a diff could have replaced the node the
listener was on. A compiled element is created once and never replaced, so
one addEventListener at mount is both correct and the whole cost.
Modifiers are applied here rather than inside the handler body so the compiled statement stays exactly what the author wrote.
Parameters:
| Name | Type | Description |
|---|---|---|
element |
Element | The element carrying the handler. |
op |
object | The event op, with its name and modifiers. |
run |
function | Runs the compiled handler. |
(static) reportBindingError(op, error)
Reports a binding that threw, without taking the rest of the update with it.
Parameters:
| Name | Type | Description |
|---|---|---|
op |
object | The op that failed. |
error |
Error | What went wrong. |
(inner) asText(value) → {string}
Converts an evaluated value to the text it renders as.
Parameters:
| Name | Type | Description |
|---|---|---|
value |
any | The evaluated value. |
Returns:
The text, with null and undefined rendering as nothing.
- Type
- string
(inner) clearRawRange(binding)
Removes the nodes a raw binding previously inserted.
Parameters:
| Name | Type | Description |
|---|---|---|
binding |
object | Per-binding state. |
(inner) isBooleanOff(value) → {boolean}
Values that mean "this boolean attribute is off".
Deliberately not JavaScript falsiness. The string renderer decided by looking
at the rendered attribute text, where the only off value was the literal
"false" -- so 0 was on. Matching that keeps disabled="{{ count }}" behaving
as it does today. Empty and null are added because they reached the old path
as an empty attribute value, which read as on: an attribute bound to
nothing being present is a bug rather than a semantic worth carrying forward.
Parameters:
| Name | Type | Description |
|---|---|---|
value |
any | The evaluated value. |
Returns:
True when the attribute should be absent.
- Type
- boolean
(inner) syncValueProperty(element, name, value)
Mirrors an attribute onto the DOM property where the two can diverge.
For a form control the value attribute is the default value: once the
user has typed, the property and the attribute are independent, and setting
the attribute alone leaves what the user sees unchanged. Two-way binding
depends on this -- a state change made in code has to reach the control the
user has already touched. The string renderer did the same thing in its
attribute patch, for the same reason.
Parameters:
| Name | Type | Description |
|---|---|---|
element |
Element | The bound element. |
name |
string | The attribute just written. |
value |
string | The value written. |
(inner) toCssProperty(name) → {string}
Converts a camelCase style property name to its CSS spelling.
Parameters:
| Name | Type | Description |
|---|---|---|
name |
string | The property name as written. |
Returns:
The hyphenated name.
- Type
- string