-
Notifications
You must be signed in to change notification settings - Fork 0
Attribute Parser
All Web Components that should be able to receive initial values for client-side state from the server or updated values from outside UIElement
applications need to observe attributes. This is therefore a very common scenario. It serves the goal of consistency across components, if this is handled by the library. Reuse of components and parser functions is easier, if we can rely on the same behavior in all UIElement
components. We also save on component size, if developers don't have to implement the same or very similar behavior in every component.
Web Components can have observed attributes, that trigger the attributeChangedCallback
:
static observedAttributes = ['value']
UIElement parses these attributes according to a declarative static property called attributeMap
:
static attributeMap = {
value: asInteger
}
The UIElement
base class will assign them to signals in the component with the same name as key:
this.get('value') // will return the current value of the `value` signal as an integer `number` or `undefined` if not a number
To achieve the same, developers would have to write the following code:
static observedAttributes = ['value']
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue === newValue) return
// Hardcoded asInteger parser for 'value' observed attribute
const asInteger = value => value.map(v => parseInt(v, 10)).filter(Number.isFinite)
this.set(name, asInteger([newValue])[0])
}
This is tedious. It gets trickier and error prone if switching cases for different attributes is required.
Conclusion: The implementation in UIElement
let's us do the same in a declarative manner, with pattern matching to predefined or custom parser functions.
- Developers should not have to implement
attributeChangedCallback
in their components - The API should feel natural to use in the context of other conventions for Web Components
- Attribute parser functions should be composable pure functions
- Developers should not have to worry whether the value is defined or not in attribute parser functions
- Developers should be able to pass the attribute parser function as value in
attributeMap
- For simple attribute parser functions, that just transform the value to a certain type, no argument should be required
- Complex attribute parser functions, that use the component context or the old value, should be possible
Decision: attributeMap
- since v0.6.0
Reason: shorter than attributeMapping
Possible alternatives:
-
attributeMapping
- before v0.6.0 -
parseAttributes
- sounds more active -
signalTypes
- focus on outcome rather than input -
types
– short
Decision: static - since v0.7.3
Reason: align it with static observedAttributes
; in most cases there is no need for per-instance differentiation
Possible alternative:
- instance property - before v0.7.3; no need to pass
this
context to parser function
Decision: object - since v0.1.0
Reason: easier to write
Possible alternative:
-
Map
- easier for iteration
Decision: string
, same as attribute name - since v0.1.0
No alternatives.
Decision: pure function - since v0.7.3
Reasons:
- aligns with strive for more functional programming approach
- array notation was confusing
- fixed string function keys were limiting
Possible alternative:
- string function key or array of signal key + string function key - before v0.7.3
Decision:
-
Maybe<string>
, array of 0 or 1 current values - since 0.8.0 -
UIElement
,this
context of component – since 0.7.3 -
string|undefined
, previous value
Reason: array for current value allows to .map()
over and .filter()
out invalid values, without checking whether the value is undefined
Possible alternative:
-
string|undefined
for first argument - before v0.8.0
Decision: Maybe<unknown>
, array of 0 or 1 current values - since 0.8.0
Reason: consistent with input of first argument
Possible alternative:
-
unknown|undefined
- before v0.8.0