litewire 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.MD +206 -27
- package/package.json +1 -1
package/README.MD
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
|
-
# Litewire 1.0.
|
|
1
|
+
# Litewire 1.0.3
|
|
2
2
|
|
|
3
|
-
Litewire is a browser-side HTML-over-the-wire library. It binds
|
|
4
|
-
reactive
|
|
5
|
-
page, and can load ES module components on
|
|
3
|
+
Litewire is a small browser-side HTML-over-the-wire library. It binds HTTP
|
|
4
|
+
requests, reactive state, and event expressions to HTML attributes, swaps
|
|
5
|
+
server-rendered HTML into the page, and can load ES module components on
|
|
6
|
+
demand.
|
|
7
|
+
|
|
8
|
+
Version 1.0.3 adds reactive `lw:class` bindings, iterable lists with
|
|
9
|
+
`template[lw:for]`, and event-triggered component mounting. It also improves
|
|
10
|
+
radio model initialization and browser-history markup restoration.
|
|
6
11
|
|
|
7
12
|
## Contents
|
|
8
13
|
|
|
9
14
|
- [Install and start](#install-and-start)
|
|
15
|
+
- [End-to-end example](#end-to-end-example)
|
|
10
16
|
- [Configuration](#configuration)
|
|
11
17
|
- [AJAX requests](#ajax-requests)
|
|
12
18
|
- [Reactive state and bindings](#reactive-state-and-bindings)
|
|
19
|
+
- [Lists](#lists)
|
|
13
20
|
- [Dynamic components](#dynamic-components)
|
|
14
21
|
- [HTML swaps and DOM updates](#html-swaps-and-dom-updates)
|
|
15
22
|
- [Browser history](#browser-history)
|
|
16
23
|
- [Lifecycle events](#lifecycle-events)
|
|
17
24
|
- [CSRF](#csrf)
|
|
25
|
+
- [Troubleshooting](#troubleshooting)
|
|
18
26
|
- [Security considerations](#security-considerations)
|
|
19
27
|
- [API reference](#api-reference)
|
|
20
28
|
|
|
@@ -34,10 +42,10 @@ continues scanning elements added later.
|
|
|
34
42
|
|
|
35
43
|
### Use the npm package
|
|
36
44
|
|
|
37
|
-
|
|
45
|
+
Install version 1.0.3 from your configured npm registry:
|
|
38
46
|
|
|
39
47
|
```sh
|
|
40
|
-
npm install litewire@1.0.
|
|
48
|
+
npm install litewire@1.0.3
|
|
41
49
|
```
|
|
42
50
|
|
|
43
51
|
Import the package from your application's JavaScript entry point:
|
|
@@ -66,6 +74,45 @@ and observers.
|
|
|
66
74
|
Litewire sends a GET request when the button is clicked and replaces the
|
|
67
75
|
contents of `#result` with the response HTML.
|
|
68
76
|
|
|
77
|
+
## End-to-end example
|
|
78
|
+
|
|
79
|
+
This example uses an HTML form to submit a search query and swaps the
|
|
80
|
+
server-rendered result into a separate element:
|
|
81
|
+
|
|
82
|
+
```html
|
|
83
|
+
<form
|
|
84
|
+
lw-get="/search"
|
|
85
|
+
lw-target="#search-results"
|
|
86
|
+
lw-indicator="#search-status"
|
|
87
|
+
>
|
|
88
|
+
<label>
|
|
89
|
+
Search
|
|
90
|
+
<input name="q" type="search" required>
|
|
91
|
+
</label>
|
|
92
|
+
<button type="submit">Search</button>
|
|
93
|
+
<span id="search-status" aria-live="polite">Ready</span>
|
|
94
|
+
</form>
|
|
95
|
+
|
|
96
|
+
<section id="search-results" aria-live="polite">
|
|
97
|
+
Enter a query to see results.
|
|
98
|
+
</section>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The browser sends a request like `GET /search?q=...`. The endpoint should
|
|
102
|
+
return an HTML fragment, for example:
|
|
103
|
+
|
|
104
|
+
```html
|
|
105
|
+
<h2>Results for “tea”</h2>
|
|
106
|
+
<ul>
|
|
107
|
+
<li>Green tea</li>
|
|
108
|
+
<li>Black tea</li>
|
|
109
|
+
</ul>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Litewire replaces the contents of `#search-results` with that fragment. The
|
|
113
|
+
new content is scanned for Litewire attributes and any `lw-component` modules
|
|
114
|
+
are loaded. No JSON decoding or client-side template rendering is performed.
|
|
115
|
+
|
|
69
116
|
## Configuration
|
|
70
117
|
|
|
71
118
|
Litewire resolves its base URL in this order:
|
|
@@ -160,11 +207,38 @@ the element is scanned:
|
|
|
160
207
|
<div id="account-summary"></div>
|
|
161
208
|
```
|
|
162
209
|
|
|
210
|
+
### Request and form behavior
|
|
211
|
+
|
|
212
|
+
- A form listens for `submit` by default. Other elements listen for `click`.
|
|
213
|
+
Set `lw-trigger` to a DOM event name to use another event; `load` runs the
|
|
214
|
+
request immediately when the element is discovered.
|
|
215
|
+
- A GET request serializes all successful controls in the nearest containing
|
|
216
|
+
form as URL query parameters. If there is no containing form, a control with
|
|
217
|
+
a `name` and `value` contributes that one parameter.
|
|
218
|
+
- POST, PUT, and DELETE requests send the nearest containing form as
|
|
219
|
+
`FormData`. If there is no form, Litewire sends no request body.
|
|
220
|
+
- Litewire checks `response.ok`; a non-success status is treated as a request
|
|
221
|
+
failure. Successful response bodies are read as text and inserted as HTML.
|
|
222
|
+
Set the response `Content-Type` appropriately on the server, even though
|
|
223
|
+
Litewire does not parse it.
|
|
224
|
+
- `lw-target` is resolved with `document.querySelector`. If omitted, the
|
|
225
|
+
triggering element is the target. Ensure the selector matches the intended
|
|
226
|
+
element when the response is applied.
|
|
227
|
+
- A trigger and each loading indicator receive the configured loading class
|
|
228
|
+
while the request is in flight. By default, indicators are descendants with
|
|
229
|
+
the `litewire-indicator` class; `lw-indicator` can select indicators
|
|
230
|
+
elsewhere in the document.
|
|
231
|
+
- Request failures are logged to the console and emit `litewire:error` from
|
|
232
|
+
the trigger. The loading class is removed on both success and failure.
|
|
233
|
+
|
|
234
|
+
`lw-trigger="load"` is useful for initial content, but avoid putting it on
|
|
235
|
+
large numbers of elements if they would all issue requests at once.
|
|
236
|
+
|
|
163
237
|
## Reactive state and bindings
|
|
164
238
|
|
|
165
|
-
Litewire
|
|
166
|
-
`lw:text`, `lw:show`, and event-expression attributes.
|
|
167
|
-
attribute names is intentional.
|
|
239
|
+
Litewire provides a small reactive layer using `lw:state`, `lw:model`,
|
|
240
|
+
`lw:text`, `lw:show`, `lw:class`, `lw:for`, and event-expression attributes.
|
|
241
|
+
The colon in these attribute names is intentional.
|
|
168
242
|
|
|
169
243
|
### Local state
|
|
170
244
|
|
|
@@ -181,9 +255,10 @@ object:
|
|
|
181
255
|
```
|
|
182
256
|
|
|
183
257
|
Assignments and deletions of top-level state properties trigger rendering of
|
|
184
|
-
|
|
185
|
-
shallowly reactive:
|
|
186
|
-
|
|
258
|
+
bindings on that element and its descendants that belong to that state root.
|
|
259
|
+
State is shallowly reactive:
|
|
260
|
+
mutating a nested object or array in place does not itself trigger a render.
|
|
261
|
+
Replace the top-level property to trigger one, for example
|
|
187
262
|
`settings = { ...settings, enabled: false }`.
|
|
188
263
|
|
|
189
264
|
Nested `lw:state` elements establish their own state scope. A binding uses the
|
|
@@ -196,10 +271,38 @@ global models instead.
|
|
|
196
271
|
text. `null` and `undefined` render as an empty string.
|
|
197
272
|
- `lw:show="expression"` sets the element's `hidden` property based on the
|
|
198
273
|
truthiness of the result.
|
|
274
|
+
- `lw:class="{ className: expression }"` adds each class whose expression is
|
|
275
|
+
truthy and removes classes previously managed by this binding when they
|
|
276
|
+
become false. Other classes on the element are left alone; a key may contain
|
|
277
|
+
multiple space-separated class names.
|
|
199
278
|
|
|
200
279
|
Expressions are evaluated when the element is first scanned and again when
|
|
201
|
-
their owning local state renders. Global model updates
|
|
202
|
-
`lw:text` bindings.
|
|
280
|
+
their owning local state renders. Global model updates refresh matching
|
|
281
|
+
`lw:text` bindings and reevaluate global `lw:class` bindings and loops.
|
|
282
|
+
|
|
283
|
+
### Lists
|
|
284
|
+
|
|
285
|
+
Use `lw:for` on a `<template>` to repeat its contents for each value in an
|
|
286
|
+
iterable. The repeated expressions can access the item name and `$index`;
|
|
287
|
+
`(item, index)` also declares a named index:
|
|
288
|
+
|
|
289
|
+
```html
|
|
290
|
+
<ul lw:state="{ items: ['Apples', 'Pears'] }">
|
|
291
|
+
<template lw:for="(item, index) of items">
|
|
292
|
+
<li>
|
|
293
|
+
<span lw:text="index + 1"></span>.
|
|
294
|
+
<span lw:text="item"></span>
|
|
295
|
+
</li>
|
|
296
|
+
</template>
|
|
297
|
+
</ul>
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
The expression must evaluate to an iterable, such as an array, string, `Set`,
|
|
301
|
+
or `Map`; `null` and `undefined` render no rows. Assign a new array to the
|
|
302
|
+
top-level state property to refresh the list. Litewire recreates the repeated
|
|
303
|
+
DOM when the owning state renders; it does not key or preserve individual rows
|
|
304
|
+
across updates. `lw:for` also works with global models set through
|
|
305
|
+
`litewire.setModel()`.
|
|
203
306
|
|
|
204
307
|
### Form models
|
|
205
308
|
|
|
@@ -216,18 +319,44 @@ their owning local state renders. Global model updates also update matching
|
|
|
216
319
|
The default `lw:model` and `lw:model.live` listen for `input`.
|
|
217
320
|
`lw:model.change` listens for `change`, and `lw:model.blur` listens for `blur`.
|
|
218
321
|
For checkboxes, the model value is a boolean; for radio inputs, it is the
|
|
219
|
-
checked value or `null
|
|
322
|
+
checked value or `null`. Unchecked radio buttons do not initialize or overwrite
|
|
323
|
+
the shared model; other controls use their value.
|
|
220
324
|
|
|
221
325
|
Without a local `lw:state` root, models are stored globally and can be read or
|
|
222
326
|
written with the instance methods `litewire.getModel(name)` and
|
|
223
327
|
`litewire.setModel(name, value)`. Global model changes dispatch a
|
|
224
328
|
`litewire:model` event on `document`, with `{ name, value }` in `event.detail`.
|
|
225
329
|
|
|
330
|
+
For example, a control outside a local state root can populate a global model,
|
|
331
|
+
and a matching text binding can display it:
|
|
332
|
+
|
|
333
|
+
```html
|
|
334
|
+
<input name="status" lw:model="status" aria-label="Status">
|
|
335
|
+
<p>Current status: <span lw:text="status"></span></p>
|
|
336
|
+
|
|
337
|
+
<script type="module">
|
|
338
|
+
import "litewire";
|
|
339
|
+
|
|
340
|
+
window.addEventListener("DOMContentLoaded", () => {
|
|
341
|
+
window.litewire.setModel("status", "Ready");
|
|
342
|
+
});
|
|
343
|
+
</script>
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
When setting global models from application code, set them on the initialized
|
|
347
|
+
`window.litewire` instance. `setModel()` updates a `lw:text` binding whose
|
|
348
|
+
expression is exactly that model name, reevaluates global `lw:class` and
|
|
349
|
+
`lw:for` bindings, and dispatches `litewire:model`.
|
|
350
|
+
|
|
226
351
|
A model name beginning with `#` or `.` is treated as a CSS selector instead of
|
|
227
352
|
a state key. Litewire copies the control's value to matching elements (to
|
|
228
353
|
`value` for form controls, otherwise to `textContent`) and dispatches
|
|
229
354
|
`litewire:model` on each target with `{ value }` in `event.detail`.
|
|
230
355
|
|
|
356
|
+
For example, `lw:model="#live-preview"` copies an input value to the
|
|
357
|
+
`textContent` of the element with that ID. Selector-based models update the
|
|
358
|
+
matching targets; they are not reactive state properties.
|
|
359
|
+
|
|
231
360
|
### Event expressions
|
|
232
361
|
|
|
233
362
|
Supported event attributes are:
|
|
@@ -271,14 +400,18 @@ Supported event modifiers:
|
|
|
271
400
|
| `.throttle` | Runs at most once per 250 ms |
|
|
272
401
|
|
|
273
402
|
Modifiers can be combined, for example `lw:click.prevent.once="save()"`.
|
|
403
|
+
Debounce and throttle delays can be customized in milliseconds, for example
|
|
404
|
+
`lw:input.debounce.400ms="search()"` or
|
|
405
|
+
`lw:click.throttle.1000ms="refresh()"`. `.outside` listens at the document
|
|
406
|
+
level so it can observe events outside the element.
|
|
274
407
|
|
|
275
408
|
## Dynamic components
|
|
276
409
|
|
|
277
410
|
An element with `lw-component` dynamically imports a JavaScript module. The
|
|
278
411
|
path is joined to Litewire's base URL. Litewire selects the module's default
|
|
279
|
-
export first, then
|
|
280
|
-
|
|
281
|
-
|
|
412
|
+
export first, then its first export. If the selected export is a function, it
|
|
413
|
+
is called with the host element and its `data-*` attributes converted to an
|
|
414
|
+
object:
|
|
282
415
|
|
|
283
416
|
```html
|
|
284
417
|
<div
|
|
@@ -297,7 +430,30 @@ export default class Greeting {
|
|
|
297
430
|
```
|
|
298
431
|
|
|
299
432
|
New components inserted by a swap or added to the document are discovered
|
|
300
|
-
automatically.
|
|
433
|
+
automatically. By default, and with `lw-trigger="load"`, a component mounts
|
|
434
|
+
when discovered. Set `lw-trigger` to another event name to defer mounting
|
|
435
|
+
until that event fires. On a triggered component, `lw-target` optionally
|
|
436
|
+
selects the element passed to the component constructor; the component path
|
|
437
|
+
and `data-*` parameters still come from the element carrying `lw-component`.
|
|
438
|
+
A component is mounted only once after a successful mount. To load one after
|
|
439
|
+
an event and mount it into another element, put `lw-trigger` and `lw-target`
|
|
440
|
+
on the component source:
|
|
441
|
+
|
|
442
|
+
```html
|
|
443
|
+
<button
|
|
444
|
+
type="button"
|
|
445
|
+
lw-component="components/widget.js"
|
|
446
|
+
lw-trigger="click"
|
|
447
|
+
lw-target="#widget"
|
|
448
|
+
data-mode="compact"
|
|
449
|
+
>
|
|
450
|
+
Load widget
|
|
451
|
+
</button>
|
|
452
|
+
<div id="widget"></div>
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
The module path and `data-*` parameters come from the element carrying
|
|
456
|
+
`lw-component`; `lw-target` is the element passed to the component.
|
|
301
457
|
|
|
302
458
|
## HTML swaps and DOM updates
|
|
303
459
|
|
|
@@ -332,11 +488,13 @@ a custom URL as the attribute value:
|
|
|
332
488
|
</a>
|
|
333
489
|
```
|
|
334
490
|
|
|
335
|
-
Litewire stores the URL, target selector, and target
|
|
336
|
-
When
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
491
|
+
Litewire stores the URL, target selector, swap type, and target markup in
|
|
492
|
+
history state. When it creates the initial history entry, it saves the current
|
|
493
|
+
markup so Back can restore the prior view. For `outerHTML`, cached markup
|
|
494
|
+
includes the target element itself. When navigating with Back or Forward,
|
|
495
|
+
Litewire restores cached markup when the target still exists; otherwise it
|
|
496
|
+
attempts to fetch the stored URL. This caches only the selected target's
|
|
497
|
+
markup, not the complete document or JavaScript state.
|
|
340
498
|
|
|
341
499
|
## Lifecycle events
|
|
342
500
|
|
|
@@ -373,6 +531,19 @@ When this meta tag is present, Litewire adds its token as the
|
|
|
373
531
|
The server must still validate the token and enforce its own authorization and
|
|
374
532
|
request protections.
|
|
375
533
|
|
|
534
|
+
## Troubleshooting
|
|
535
|
+
|
|
536
|
+
| Symptom | Things to check |
|
|
537
|
+
|---|---|
|
|
538
|
+
| The global `window.litewire` is missing | Load Litewire in a browser as a module and check the console for module loading errors. Auto-initialization occurs after the document is ready. |
|
|
539
|
+
| A request does not run | Confirm the element has one of `lw-get`, `lw-post`, `lw-put`, or `lw-delete`. Check its default or configured `lw-trigger` event and whether another handler prevents the event from reaching it. |
|
|
540
|
+
| The server receives no form values | Make sure the trigger is inside the form, or is the form itself. Non-GET requests without a containing form have no body. For GET, a standalone control needs both `name` and `value`. |
|
|
541
|
+
| The request succeeds but the page does not change | Return an HTML fragment, verify `lw-target` matches an element, and check that the selected `lw-swap` behavior is suitable. |
|
|
542
|
+
| A request reports an error | Inspect the browser console and the `litewire:error` event. Litewire treats HTTP statuses outside the 2xx range as failures. |
|
|
543
|
+
| A state binding stays unchanged | Confirm the binding is under the intended `lw:state` root. Replace a top-level state property; nested mutations are not observed. |
|
|
544
|
+
| A component does not mount | Check the module URL, its default or first export, and that the export is a function. For deferred components, verify the triggering event and optional `lw-target` selector. |
|
|
545
|
+
| A directive in newly inserted HTML does not run | Ensure the HTML is inserted through a Litewire swap or added to the observed document body. The observer watches additions under `document.body`. |
|
|
546
|
+
|
|
376
547
|
## Security considerations
|
|
377
548
|
|
|
378
549
|
Litewire inserts response bodies as HTML. Only return trusted or appropriately
|
|
@@ -387,18 +558,26 @@ untrusted user input.
|
|
|
387
558
|
| Attribute / API | Description |
|
|
388
559
|
|---|---|
|
|
389
560
|
| `lw-get`, `lw-post`, `lw-put`, `lw-delete` | Request path and HTTP method |
|
|
390
|
-
| `lw-trigger` |
|
|
391
|
-
| `lw-target` | CSS selector for the response target |
|
|
561
|
+
| `lw-trigger` | Request event (defaults to `click` or form `submit`; `load` runs on scan); for components, event to defer mounting |
|
|
562
|
+
| `lw-target` | CSS selector for the response target; for a triggered component, selects the element passed to the component |
|
|
392
563
|
| `lw-swap` | `innerHTML`, `outerHTML`, `prepend`, or `append` |
|
|
393
564
|
| `lw-indicator` | CSS selector for loading indicators |
|
|
394
565
|
| `lw-push-url` | `true`, `false`, or a custom history URL |
|
|
395
|
-
| `lw-component` | Path to a dynamically imported ES module |
|
|
566
|
+
| `lw-component` | Path to a dynamically imported ES module; its default export or first export is mounted |
|
|
396
567
|
| `lw:state` | JavaScript object expression that initializes local reactive state |
|
|
397
568
|
| `lw:model`, `.live`, `.change`, `.blur` | Bind a form control to a state or global model |
|
|
398
569
|
| `lw:text` | Render an expression as text |
|
|
399
570
|
| `lw:show` | Toggle the element's `hidden` property from an expression |
|
|
571
|
+
| `lw:class` | Add or remove classes reactively from an object expression |
|
|
572
|
+
| `template[lw:for]` | Repeat template contents for an iterable expression; item and index variables are available to bindings |
|
|
400
573
|
| `lw:click`, `lw:input`, `lw:change`, `lw:submit`, `lw:keydown`, `lw:keyup`, `lw:focus`, `lw:blur` | Evaluate an expression in response to an event; supports the modifiers documented above |
|
|
401
574
|
| `litewire.getModel(name)` | Read a global model |
|
|
402
575
|
| `litewire.setModel(name, value)` | Set a global model and update matching bindings |
|
|
576
|
+
| `new Litewire({ baseUrl, loadingClass })` | Construct and initialize an instance with optional configuration |
|
|
577
|
+
|
|
578
|
+
The browser module exports the `Litewire` class as its default export and
|
|
579
|
+
auto-creates `window.litewire` if that global is not already set. When creating
|
|
580
|
+
a custom instance, set `window.litewire` to a placeholder before importing the
|
|
581
|
+
module if needed to prevent auto-initialization, then assign the instance.
|
|
403
582
|
|
|
404
583
|
**Buy my kids schoolbooks:** [https://paypal.me/justumg](https://paypal.me/justumg)
|