You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
super.disconnectedCallback();// Automatically removes event listeners bound with `.on()`
111
129
if (this.intersectionObserver) this.intersectionObserver.disconnect();
112
130
}
113
131
}
@@ -141,6 +159,18 @@ if (this.has('count')) { /* Do something */ }
141
159
this.delete('count'); // Removes the signal and its dependencies
142
160
```
143
161
162
+
### Characteristics and Special Values
163
+
164
+
Signals in UIElement are of a **fixed type** and **non-nullable**. This allows to **simplify the logic** as you will never have to check the type or perform null-checks.
165
+
166
+
* If you use **TypeScript** (recommended), **you will be warned** that `null` or `undefined` cannot be assigned to a signal or if you try to assign a value of a wrong type.
167
+
* If you use vanilla **JavaScript** without a build step, setting a signal to `null` or `undefined` **will log an error to the console and abort**. However, strict type checking is not enforced at runtime.
168
+
169
+
Because of the **non-nullable nature of signals** in UIElement, we need two special values that can be assigned to any signal type:
170
+
171
+
* **`RESET`**: Will **reset to the server-rendered version** that was there before UIElement took control. This is what you want to do most of the times when a signal lacks a specific value.
172
+
* **`UNSET`**: Will **delete the signal**, **unsubscribe its watchers** and also **delete related attributes or style properties** in effects. Use this with special care!
173
+
144
174
### Why Signals with a Map Interface?
145
175
146
176
UIElement **uses signals** instead of standard properties or attributes because it **ensures reactivity, loose coupling, and avoids common pitfalls with the DOM API**.
@@ -175,16 +205,28 @@ states = {
175
205
};
176
206
```
177
207
208
+
<callout-box class="caution">
209
+
210
+
**Careful**: Attributes **may not be present** on the element or **parsing to the desired type may fail**. To ensure **non-nullability** of signals, UIElement falls back to neutral defaults:
211
+
212
+
* `''` (empty string) for `string`
213
+
* `0` for `number`
214
+
* `{}` (empty object) for objects of any kind
215
+
216
+
Pre-defined parsers (see next section) come with a variant `*WithDefault()` that allow you to set custom fallback values for attribute parsers.
217
+
218
+
</callout-box>
219
+
178
220
### Pre-defined Parsers in UIElement
179
221
180
222
| Function | Description |
181
223
| ------------ | ----------- |
182
224
| `asBoolean` | Converts `"true"` / `"false"` to a **boolean** (`true` / `false`). Also treats empty attributes (`checked`) as `true`. |
183
-
| `asInteger` | Converts a numeric string (e.g., `"42"`) to an **integer** (`42`). |
184
-
| `asNumber` | Converts a numeric string (e.g., `"3.14"`) to a **floating-point number** (`3.14`). |
185
-
| `asString` | Returns the attribute value as a **string** (unchanged). |
225
+
| `asInteger`, `asIntegerWithDefault(1)` | Converts a numeric string (e.g., `"42"`) to an **integer** (`42`). |
226
+
| `asNumber`, `asNumberWithDefault(0.1)` | Converts a numeric string (e.g., `"3.14"`) to a **floating-point number** (`3.14`). |
227
+
| `asString`, `asStringwithDefault('foo')` | Returns the attribute value as a **string** (unchanged). |
186
228
| `asEnum([...])` | Ensures the string matches **one of the allowed values**. Example: `asEnum(['small', 'medium', 'large'])`. If the value is not in the list, it defaults to the first option. |
187
-
| `asJSON` | Parses a JSON string (e.g., `'["a", "b", "c"]'`) into an **array** or **object**. If invalid, returns `null`. |
229
+
| `asJSON`, `asJSONWithDefault({ theme:'dark' })` | Parses a JSON string (e.g., `'["a", "b", "c"]'`) into an **array** or **object**. If invalid, returns `{}`. |
188
230
189
231
</section>
190
232
@@ -258,14 +300,20 @@ this.first('.count').sync(
258
300
| Function | Description |
259
301
| ------------------- | ----------- |
260
302
| `setText()` | Updates **text content** with a `string` signal value (while preserving comment nodes). |
261
-
| `setProperty()` | Updates a given **property** with any signal value. |
303
+
| `setProperty()` | Updates a given **property** with any signal value.* |
262
304
| `setAttribute()` | Updates a given **attribute** with a `string` signal value. |
263
305
| `toggleAttribute()` | Toggles a given **boolean attribute** with a `boolean` signal value. |
264
306
| `toggleClass()` | Toggles a given **CSS class** with a `boolean` signal value. |
265
307
| `setStyle()` | Updates a given **CSS property** with a `string` signal value. |
266
308
| `createElement()` | Inserts a **new element** with a given tag name with a `Record<string, string>` signal value for attributes. |
267
309
| `removeElement()` | Removes an element if the `boolean` signal value is `true`. |
268
310
311
+
<callout-box class="tip">
312
+
313
+
**Tip**: TypeScript will check whether a value of a given type is assignable to a certain element type. You might have to specify a type hint for the queried element type. Prefer `setProperty()` over `setAttribute()` for increased type safety. Setting string attributes is possible for all elements, but will have an effect only on some.
314
+
315
+
</callout-box>
316
+
269
317
### Simplifying Effect Notation
270
318
271
319
For effects that take two arguments, **the second argument can be omitted** if the signal key matches the targeted property name, attribute, class, or style property.
0 commit comments