Skip to content

fix(html): fall back to attribute for read-only properties - #320

Merged
smalluban merged 1 commit into
hybridsjs:mainfrom
dobexx:fix/template-readonly-property-fallback
Sep 25, 2026
Merged

smalluban merged 1 commit into
hybridsjs:mainfrom
dobexx:fix/template-readonly-property-fallback

Conversation

@dobexx

@dobexx dobexx commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

Fixes #319.

An attribute expression sets the property of the same name whenever propertyName in target is true (src/template/resolvers/property.js:37 and :42, and src/template/core.js:435 for mixed values). That is also true for read-only properties, like form and list of <input> or a getter-only accessor of an external custom element. The assignment then throws a TypeError in module code, and the rest of the template is not updated. Reproduction in #319.

This change sets the property only if it's writable: a data property with writable: true or an accessor with a setter. Otherwise, the expression falls back to the attribute, as it already does for a missing property. A small isWritable() helper in src/template/utils.js replaces the three in checks. It runs the in check first, so attributes without a property, like class="btn ${type}" in the mixed-value path, cost the same as before. The docs sentence in Properties & Attributes now mentions read-only properties.

Independent of #316: both change property.js in different lines and merge cleanly in either order. Until #316 is merged, this change also covers SVG children in html templates, whose x, width and points properties are read-only.

One behavior change: an expression on a read-only hybrids property (a computed property without a setter) now sets an attribute of the same name instead of throwing.

Tests in test/spec/html.js:

  • attribute expression with existing property: <input form list> gets both attributes, and input.form is the form. A getter-only property of an external element gets the attribute.
  • attribute expression with combined text value: <input list="list-${…}"> gets the attribute.

The three tests fail without the fix (Chrome Headless: Cannot set property … which has only a getter, WebKit: Attempted to assign to readonly property.). Full suite in Chrome Headless on main: 611 of 611 with the fix, 608 of 611 without it; on top of #316 in Chrome Headless and WebKit: 612 of 612 with the fix, 609 of 612 without it. Firefox was not run locally. Lint is clean, and prettier is clean for the changed source and test files.

An attribute expression set the property whenever it existed on the
element (`name in target`), also when the property was read-only. For
`form` or `list` of the `<input>` element, or a getter-only accessor of
an external custom element, the assignment throws a TypeError in strict
mode, and the rest of the template is not updated. Set the property only
if it is writable (a writable data property or an accessor with a
setter), otherwise set the attribute.
@coveralls

Copy link
Copy Markdown

Coverage Status

coverage: 99.875%. remained the same — dobexx:fix/template-readonly-property-fallback into hybridsjs:main

@smalluban smalluban left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@smalluban
smalluban merged commit a0b7e1e into hybridsjs:main Sep 25, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Attribute expressions throw for read-only properties like form and list of <input>

3 participants