@boolAttr Decorator
Maps a boolean property to the mere presence of an HTML (or data-*
) attribute.
Presence → true
, absence → false
. No third state, no default override, no custom parser.
Why
Frequently you need a simple feature toggle (e.g. disabled
, open
, interactive
) whose truthiness depends solely on whether an attribute exists. Implementing manual getters/setters for each is repetitive:
get disabled() { return this.hasAttribute('disabled'); }
set disabled(v: boolean) { v ? this.setAttribute('disabled', '') : this.removeAttribute('disabled'); }
@boolAttr
encapsulates this pattern:
- Minimal logic, zero configuration in common cases
- Guarantees consistent DOM reflection semantics
- Readability & reduced boilerplate
Use @attr({parser: parseBoolean, serializer: toBooleanAttribute, defaultValue: ...})
instead if you need a tri‑state boolean with a default value (see Comparison section).
Quick Start
import {boolAttr} from '@exadel/esl/modules/esl-utils/decorators';
class TogglePanel extends HTMLElement {
@boolAttr() disabled!: boolean; // <toggle-panel disabled>
@boolAttr({name: 'no-close'}) noclose!: boolean; // custom attribute name
@boolAttr({dataAttr: true}) active!: boolean; // maps to data-active
}
const p = new TogglePanel();
p.disabled = true; // sets attribute disabled=""
p.disabled = false; // removes attribute
API
boolAttr(config?: BoolAttrConfig): PropertyDecorator;
interface BoolAttrConfig {
name?: string; // custom attribute name (kebab-cased by default)
readonly?: boolean; // getter only (writes ignored)
dataAttr?: boolean; // prefix with data-
}
Behavior
Action | Effect |
---|---|
Read property | hasAttribute(attrName) (true/false) |
Write truthy (not readonly) | setAttribute(attrName, '') (empty string) |
Write falsy (not readonly) | removeAttribute(attrName) |
readonly: true | Only getter defined; assignments do nothing |
dataAttr: true | Attribute name becomes data-{kebab} |
Host Resolution ($host Support)
@boolAttr
works not only on direct HTMLElement
subclasses. If applied to an object exposing a $host: HTMLElement
property (e.g. mixin / controller / behavior pattern), it resolves and manipulates the host element's attribute.
class FocusBehaviour { // not an element
constructor(public $host: HTMLElement) {}
@boolAttr() focused!: boolean;
}
const el = document.createElement('div');
const behavior = new FocusBehaviour(el);
behavior.focused = true; // sets attribute on el
If $host
is missing or null, reads return false
and writes are effectively ignored (attribute helpers are no-ops for invalid host).
Comparison with Related Decorators
Decorator | True Condition | False Condition | Default w/o Attribute | Third State? | Inheritance | Custom Parse/Serialize |
---|---|---|---|---|---|---|
@boolAttr | Attribute present | Attribute absent | false | No | No | No |
Tri-state @attr | Parser truth mapping | Absent fallback (e.g. defaultValue ) or explicit false text | Configurable (defaultValue ) | Yes (default vs explicit) | Yes (inherit ) | Yes |
@attr + simple parser | Parser result | Parser result | Configurable | Depends | Yes | Yes |
Use @boolAttr
when you only care about presence. Switch to @attr
when you need:
- A default
true
without writing the attribute - Inter-element inheritance (
inherit
) - Distinguishing explicit false vs absence
- Custom parse/serialize formats
Examples
Custom Attribute Name
class Component extends HTMLElement {
@boolAttr({name: 'no-shadow'}) noShadow!: boolean; // <component no-shadow>
}
Data Attribute
class FeatureFlag extends HTMLElement {
@boolAttr({dataAttr: true}) experimental!: boolean; // data-experimental
}
Readonly View of a Marker
class View extends HTMLElement {
@boolAttr({readonly: true}) hydrated!: boolean; // code can read, not set
connectedCallback() { this.toggleAttribute('hydrated', true); }
}
Override Attribute Mapping via Subclass
If you need to lock a boolean to true
(ignoring attribute removal) use @prop
in a subclass:
class BaseEl extends HTMLElement { @boolAttr() interactive!: boolean; }
class LockedEl extends BaseEl { @prop(true, {readonly: true}) override interactive!: boolean; }
Edge Cases
Case | Result |
---|---|
Assign null / undefined | Treated as falsy -> attribute removed |
Assign number 0 | Falsy -> removed |
Assign string 'false' | Truthy (non-empty) -> attribute present |
Need explicit textual false | Not supported; use tri-state @attr pattern |
Performance Notes
The getter is O(1) DOM hasAttribute
call. No parsing / serialization overhead. Suitable for high-frequency reads.
Best Practices
- Prefer
@boolAttr
for pure presence toggles—keeps intent clear. - Use
@attr
tri-state pattern for default-enabled features that can be explicitly disabled. - Combine with
@prop
to freeze or preconfigure booleans in subclasses. - Keep attribute names semantic and kebab-cased (default behavior already handles conversion).