@loadbare/app 0.8.2 → 0.10.0
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 +3 -3
- package/dist/build/assemble.d.ts.map +1 -1
- package/dist/build/assemble.js +74 -66
- package/dist/build/assemble.js.map +1 -1
- package/dist/build/expand.d.ts.map +1 -1
- package/dist/build/expand.js +20 -19
- package/dist/build/expand.js.map +1 -1
- package/dist/build/locations.d.ts +2 -3
- package/dist/build/locations.d.ts.map +1 -1
- package/dist/build/locations.js +2 -3
- package/dist/build/locations.js.map +1 -1
- package/dist/build/pages.d.ts +3 -4
- package/dist/build/pages.d.ts.map +1 -1
- package/dist/build/pages.js +3 -4
- package/dist/build/pages.js.map +1 -1
- package/dist/core/lb-constants.d.ts +25 -23
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +95 -158
- package/dist/core/lb-constants.js.map +1 -1
- package/dist/core/lb-types.d.ts +73 -75
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js +58 -5
- package/dist/core/lb-types.js.map +1 -1
- package/dist/hub/lb-apply.d.ts +47 -37
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +174 -193
- package/dist/hub/lb-apply.js.map +1 -1
- package/dist/hub/lb-hub.browser.d.ts +1 -1
- package/dist/hub/lb-hub.browser.d.ts.map +1 -1
- package/dist/hub/lb-hub.browser.js +419 -415
- package/dist/hub/lb-hub.browser.js.map +1 -1
- package/dist/server/lb-express.d.ts +20 -13
- package/dist/server/lb-express.d.ts.map +1 -1
- package/dist/server/lb-express.js +50 -52
- package/dist/server/lb-express.js.map +1 -1
- package/dist/server/lb-server.d.ts +81 -116
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +151 -48
- package/dist/server/lb-server.js.map +1 -1
- package/docs/TECHREF-1.0.md +893 -558
- package/docs/comparison.md +222 -185
- package/docs/prior-art.md +15 -14
- package/docs/reference/builder.md +9 -3
- package/docs/reference/chrome.md +107 -56
- package/docs/reference/custom-elements.md +199 -173
- package/docs/reference/data-binding.md +375 -370
- package/docs/reference/overview.md +12 -10
- package/docs/reference/page-files.md +161 -86
- package/docs/reference/server.md +13 -7
- package/docs/reference/widgets.md +104 -110
- package/docs/roadmap.md +36 -31
- package/docs/terms-of-art.md +57 -0
- package/docs/testing.md +97 -68
- package/docs/theory.md +92 -58
- package/docs/tutorials/010-pages-and-navigation.md +20 -12
- package/docs/tutorials/020-css.md +6 -3
- package/docs/tutorials/030-html-decomposition.md +9 -7
- package/docs/tutorials/040-displaying-data.md +30 -13
- package/docs/tutorials/{050-actions.md → 050-requests.md} +25 -15
- package/docs/tutorials/060-custom-element-code.md +17 -16
- package/docs/tutorials/065-conditional-rendering.md +34 -23
- package/docs/tutorials/070-displaying-a-list.md +29 -21
- package/docs/tutorials/072-inserting-into-a-list.md +24 -16
- package/docs/tutorials/074-deleting-from-a-list.md +9 -7
- package/docs/tutorials/076-updating-a-list-item.md +11 -10
- package/docs/tutorials/080-widget-requests.md +71 -43
- package/docs/tutorials/090-using-widget-libraries.md +22 -22
- package/package.json +1 -1
- package/skills/loadbare-app/SKILL.md +178 -111
- package/skills/loadbare-app/references/TECHREF-1.0.md +893 -558
- package/skills/loadbare-app/references/builder.md +9 -3
- package/skills/loadbare-app/references/chrome.md +107 -56
- package/skills/loadbare-app/references/custom-elements.md +199 -173
- package/skills/loadbare-app/references/data-binding.md +375 -370
- package/skills/loadbare-app/references/overview.md +12 -10
- package/skills/loadbare-app/references/page-files.md +161 -86
- package/skills/loadbare-app/references/server.md +13 -7
- package/skills/loadbare-app/references/widgets.md +104 -110
- package/docs/analysis-accidental-complexity.md +0 -149
- package/docs/analysis-closed-set.md +0 -210
|
@@ -1,485 +1,490 @@
|
|
|
1
1
|
# Data Binding
|
|
2
2
|
|
|
3
|
-
A page
|
|
4
|
-
server to change that data
|
|
5
|
-
|
|
6
|
-
it
|
|
3
|
+
A page shows server data by naming queries and columns in its HTML, and asks
|
|
4
|
+
the server to change that data by naming requests. The developer writes
|
|
5
|
+
seven `lb-` attributes. The server declares every query a page may show and
|
|
6
|
+
every request it may send, and refuses anything it has not declared.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
Everything is a query. A query is a name for rows, of kind `row` or `rows`,
|
|
9
|
+
with a key column. The server declares all three, so the markup names the
|
|
10
|
+
query and the columns it shows, and repeats neither kind nor key.
|
|
11
11
|
|
|
12
12
|
## A page that binds data
|
|
13
13
|
|
|
14
|
-
Here is a page that
|
|
14
|
+
Here is a page that shows one row, lists rows, and sends three requests:
|
|
15
15
|
|
|
16
16
|
```html
|
|
17
17
|
<!-- src/pages/members.page.html -->
|
|
18
|
+
<title>Members</title>
|
|
18
19
|
<h1>Members</h1>
|
|
19
20
|
|
|
20
|
-
<p lb-
|
|
21
|
+
<p lb-query="dues">Dues collected this year: <span lb-column="total"></span></p>
|
|
21
22
|
|
|
22
|
-
<section lb-
|
|
23
|
-
<form lb-
|
|
24
|
-
<input lb-
|
|
23
|
+
<section lb-query="roster">
|
|
24
|
+
<form lb-request="lb-row-insert">
|
|
25
|
+
<input lb-column="name" placeholder="Name" />
|
|
25
26
|
<button type="submit">Add member</button>
|
|
26
27
|
</form>
|
|
27
28
|
|
|
28
29
|
<ul>
|
|
29
|
-
<template
|
|
30
|
+
<template>
|
|
30
31
|
<li>
|
|
31
|
-
<
|
|
32
|
-
<button lb-
|
|
32
|
+
<input lb-column="name" lb-request="lb-row-update" />
|
|
33
|
+
<button lb-request="lb-row-delete">Remove</button>
|
|
33
34
|
</li>
|
|
34
35
|
</template>
|
|
35
36
|
</ul>
|
|
36
37
|
</section>
|
|
37
38
|
```
|
|
38
39
|
|
|
39
|
-
The
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
| `lb-
|
|
47
|
-
| `lb-
|
|
48
|
-
| `lb-
|
|
49
|
-
| `lb-
|
|
50
|
-
| `lb-
|
|
51
|
-
| `lb-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
`
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
| Element | Receives the value as |
|
|
101
|
-
|----------------------------------------|-----------------------------------|
|
|
102
|
-
| A custom element | Its `lb-value` attribute |
|
|
103
|
-
| `<select>`, `<textarea>`, or `<input>` | Its `value`, and `lb-value` |
|
|
104
|
-
| Any other native element | Its `textContent`, and `lb-value` |
|
|
105
|
-
|
|
106
|
-
A widget owns whatever control it wraps, so it is handed the value and
|
|
107
|
-
renders it itself — see [Custom Elements](./custom-elements.md#code) for
|
|
108
|
-
observing `lb-value`. A form control shows its state as its `value`, so a
|
|
109
|
-
`<select>` keeps its options. Any other native element has no behavior of its
|
|
110
|
-
own, so its value is its text. Checkboxes, radio buttons and file inputs
|
|
111
|
-
receive nothing, not even `lb-value`, and the hub reports it to the console.
|
|
112
|
-
|
|
113
|
-
Nothing an application writes ever sets `lb-value`. It is written by
|
|
114
|
-
Loadbare and read by a widget or a stylesheet.
|
|
115
|
-
|
|
116
|
-
### Lists
|
|
117
|
-
|
|
118
|
-
Write `lb-list` on any element, and a `<template>` inside it carrying
|
|
119
|
-
`lb-key`. The hub clones that template once per row, fills each clone, and
|
|
120
|
-
reconciles what is showing against what arrived. No widget is involved, and
|
|
121
|
-
none is needed.
|
|
122
|
-
|
|
123
|
-
An array is the whole set, so it decides membership and order: a row whose
|
|
124
|
-
key did not arrive is gone. A patch touches only the rows it names and leaves
|
|
125
|
-
every other row's contents and position alone.
|
|
126
|
-
|
|
127
|
-
A widget enters only where the rows need scaffolding or placement that only
|
|
128
|
-
it can decide — a `<select>` that builds an `<optgroup>` per distinct value, a
|
|
129
|
-
table that sections and sorts. Such a widget carries `lb-list` itself and
|
|
130
|
-
implements one or both of two optional methods; see
|
|
131
|
-
[Decorating a list](./custom-elements.md#decorating-a-list) and
|
|
132
|
-
[The Basic Widget Library](./widgets.md).
|
|
133
|
-
|
|
134
|
-
A scope with `lb-list` and no row template displays nothing, and that is not
|
|
135
|
-
an error. It is bound to the list without showing it, which is what an insert
|
|
136
|
-
form naming the list it adds a row to already is.
|
|
137
|
-
|
|
138
|
-
## Requests
|
|
139
|
-
|
|
140
|
-
One attribute turns an interaction into a request. `lb-action` names what the
|
|
141
|
-
server is asked for: an action the page declared, or one of Loadbare's
|
|
142
|
-
reserved names, which are the CRUD operations.
|
|
143
|
-
|
|
144
|
-
One attribute name, one wire field, one set of values. The request field
|
|
145
|
-
`action` carries this attribute's value verbatim, so nothing is translated
|
|
146
|
-
between the markup and the server, and the CRUD key is the same value with
|
|
147
|
-
the prefix stripped and the rest camel-cased.
|
|
148
|
-
|
|
149
|
-
| Written | Asks for | Carries |
|
|
150
|
-
|-------------------------------------------|--------------|-------------------------------|
|
|
151
|
-
| `lb-action="name"` | That action | The scope, and what is in it |
|
|
152
|
-
| `lb-action="lb-row-delete"` | `rowDelete` | `list`, `key` |
|
|
153
|
-
| `lb-action="lb-row-insert"` on a `<form>` | `rowInsert` | `list`, `values` |
|
|
154
|
-
| `lb-action="lb-row-update"` on a `<form>` | `rowUpdate` | `list`, `key`, `values` |
|
|
155
|
-
| `lb-action="lb-row-update"` on a widget cell | `rowUpdate` | `list`, `key`, `values` of one cell |
|
|
156
|
-
|
|
157
|
-
All three operations are list operations. Each needs a key, and a key exists
|
|
158
|
-
only on a live row the hub stamped inside a list, so a single-row scope is
|
|
159
|
-
read-only and a declared action is the only thing it can send. An application
|
|
160
|
-
that wants a writable single row declares a list that answers with one row.
|
|
161
|
-
|
|
162
|
-
A name beginning with `lb-` is reserved, in `lb-action` and in either scope
|
|
163
|
-
attribute alike. The server refuses a page that declares an action or a query
|
|
164
|
-
so named, which is what lets a reserved name be added later without colliding
|
|
165
|
-
with one an application already uses. That reservation is also the whole of
|
|
166
|
-
the wire discriminant: a value beginning with `lb-` is an operation, and
|
|
167
|
-
anything else is a name the page declared.
|
|
168
|
-
|
|
169
|
-
The hub sends a request from a native element on the element's own event: a
|
|
170
|
-
form on submit, anything else on click. An insert or update clicked from a
|
|
171
|
-
button reads the form or row the button is in (see [Forms](#forms)). To
|
|
172
|
-
commit one cell as it changes, put `lb-row-update` on a widget cell instead
|
|
173
|
-
(see [Committing one cell](#committing-one-cell)).
|
|
174
|
-
|
|
175
|
-
Declare every action on the server, and permit every operation on its list. A
|
|
176
|
-
name the page has not declared, and an operation a list does not permit, are
|
|
177
|
-
refused; see
|
|
178
|
-
[requests, actions, CRUD](./page-files.md#requests-actions-crud).
|
|
179
|
-
|
|
180
|
-
### Actions
|
|
181
|
-
|
|
182
|
-
Write `lb-action` on a button to ask the server to do something that is not
|
|
183
|
-
one of the three CRUD operations:
|
|
40
|
+
The server declares `dues` as a `row` query and `roster` as a `rows` query,
|
|
41
|
+
each with its key, and permits the three requests on `roster`; see
|
|
42
|
+
[page files](./page-files.md).
|
|
43
|
+
|
|
44
|
+
| Attribute | Goes on | Names |
|
|
45
|
+
|------------------|-------------------------------|------------------------------------------------|
|
|
46
|
+
| `lb-query` | Any element | The query whose rows land in it |
|
|
47
|
+
| `lb-column` | Any element | The column it shows, and the column it gathers |
|
|
48
|
+
| `lb-show` | Any element but `<template>` | The column that decides whether it is present |
|
|
49
|
+
| `lb-request` | Any element | The request it sends when it commits |
|
|
50
|
+
| `lb-url-link` | An `<a>` | A link between pages; see [chrome](./chrome.md#links) |
|
|
51
|
+
| `lb-url-push` | An element with `lb-request` | A URL change that pushes a history entry |
|
|
52
|
+
| `lb-url-unknown` | A `<dialog>` in the chrome | The dialog for a URL that names no page |
|
|
53
|
+
|
|
54
|
+
Every other `lb-` attribute in the document is a stamp. The hub or the
|
|
55
|
+
builder writes a stamp, and a stylesheet or a custom element reads it.
|
|
56
|
+
|
|
57
|
+
| Stamp | Carries |
|
|
58
|
+
|----------------------|----------------------------------------------------|
|
|
59
|
+
| `lb-column-value` | The value the hub set from a column |
|
|
60
|
+
| `lb-key-value` | The key of a live row, or of a `row` landed on an element |
|
|
61
|
+
| `lb-query-row-count` | The number of live rows an element holds |
|
|
62
|
+
| `lb-request-pending` | The element's request is in flight |
|
|
63
|
+
| `lb-request-error` | The element's last request failed |
|
|
64
|
+
| `lb-page` | The stub of a page, on its `<template>` |
|
|
65
|
+
| `lb-page-title` | The text of a page file's `<title>` |
|
|
66
|
+
|
|
67
|
+
The application should never name anything with the `lb-` prefix anywhere:
|
|
68
|
+
no attribute, query, column, request, custom element or event.
|
|
69
|
+
|
|
70
|
+
## Landing
|
|
71
|
+
|
|
72
|
+
Write `lb-query` on an element to show a query there. Every response from
|
|
73
|
+
the server carries response items, one per query, and the hub lands each on
|
|
74
|
+
every element whose `lb-query` names its query. Name a query on as many
|
|
75
|
+
elements as the page needs.
|
|
76
|
+
|
|
77
|
+
What lands depends on the query's kind and on whether the element holds a
|
|
78
|
+
row template, which is the first `<template>` among its descendants outside
|
|
79
|
+
any nested `lb-query`:
|
|
80
|
+
|
|
81
|
+
| Kind | Row template | The hub |
|
|
82
|
+
|--------|--------------|----------------------------------------|
|
|
83
|
+
| `row` | no | Lands the row on the element itself |
|
|
84
|
+
| `row` | yes | Lands one live row |
|
|
85
|
+
| `rows` | yes | Lands one live row per row |
|
|
86
|
+
| `rows` | no | Lands nothing |
|
|
87
|
+
|
|
88
|
+
A live row is an element the hub cloned from a row template for one row.
|
|
89
|
+
The hub stamps it with `lb-key-value`, the row's key. An element a `row`
|
|
90
|
+
lands on itself carries `lb-key-value` as well.
|
|
91
|
+
|
|
92
|
+
Write `lb-column` on each element that shows a column. The element reads
|
|
93
|
+
from its nearest ancestor row: the live row it is in, or the element a
|
|
94
|
+
`row` landed on. A nested `lb-query` begins a new query, and what is inside
|
|
95
|
+
it reads from that query's rows.
|
|
96
|
+
|
|
97
|
+
An element with `lb-query` and `lb-column` both shows the rows of its own
|
|
98
|
+
query and holds the column of the row around it. This
|
|
99
|
+
[`<lb-options>`](./widgets.md#lb-options) lists the accounts and holds its
|
|
100
|
+
row's `account`:
|
|
184
101
|
|
|
185
102
|
```html
|
|
186
|
-
<
|
|
103
|
+
<lb-options lb-query="accounts" lb-column="account">
|
|
104
|
+
<template><option lb-column="name"></option></template>
|
|
105
|
+
</lb-options>
|
|
187
106
|
```
|
|
188
107
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
no argument list. A button carries no value, so the server computes the whole
|
|
192
|
-
of the new state and the page displays only what came back.
|
|
108
|
+
`lb-column` on an element with `lb-query` is undefined unless the element is
|
|
109
|
+
a control.
|
|
193
110
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
on change, not on click.
|
|
111
|
+
An element with `lb-column` and no ancestor row receives nothing. The hub
|
|
112
|
+
still gathers from it, so an insert form writes `lb-column` on controls that
|
|
113
|
+
no row fills.
|
|
198
114
|
|
|
199
|
-
###
|
|
115
|
+
### Where a column lands
|
|
200
116
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
117
|
+
| Element | Receives the value as |
|
|
118
|
+
|------------------------------------------------------|-----------------------------------------|
|
|
119
|
+
| `<input>`, `<select>`, `<textarea>` | Its `value`, and `lb-column-value` |
|
|
120
|
+
| A form-associated custom element with `value` | Its `value`, and `lb-column-value` |
|
|
121
|
+
| Any other custom element | `lb-column-value` only |
|
|
122
|
+
| Any other element | Its text content, and `lb-column-value` |
|
|
204
123
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
124
|
+
A control is an `<input>`, `<select>` or `<textarea>`, or a form-associated
|
|
125
|
+
custom element with a `value` property that fires `change`. The hub sets a
|
|
126
|
+
control's `value`, and gathers it back under the same column. A custom
|
|
127
|
+
element that is not a control keeps the content the builder placed in it
|
|
128
|
+
from its element file, and renders `lb-column-value` itself; see
|
|
129
|
+
[Custom Elements](./custom-elements.md#receiving-a-value).
|
|
208
130
|
|
|
209
|
-
|
|
131
|
+
A checkbox, a radio button and a file input receive nothing, and the hub
|
|
132
|
+
reports it to the console.
|
|
210
133
|
|
|
211
|
-
|
|
134
|
+
The hub hands every value to the browser as the server sent it. What a
|
|
135
|
+
number, a date or a null looks like is decided in the query.
|
|
212
136
|
|
|
213
|
-
|
|
214
|
-
both gather every `lb-cell` of the form into one values map, read from
|
|
215
|
-
the control each cell is or wraps. A cell inside a scope nested in the form
|
|
216
|
-
is that scope's and is not gathered, the same way a value landing on the
|
|
217
|
-
form's row does not reach it. They differ in one thing: `lb-row-update`
|
|
218
|
-
also carries the key of the row it is inside, and `lb-row-insert` carries none,
|
|
219
|
-
because there is no row yet. A declared name on a form sends that action on
|
|
220
|
-
submit, carrying the binding and no values.
|
|
137
|
+
### Rows
|
|
221
138
|
|
|
222
|
-
|
|
223
|
-
|
|
139
|
+
Write a `<template>` inside the element carrying `lb-query`, holding one
|
|
140
|
+
element: the row. The hub clones it once per row, fills each clone, and
|
|
141
|
+
places it immediately before the template:
|
|
224
142
|
|
|
225
143
|
```html
|
|
226
|
-
<
|
|
227
|
-
<
|
|
228
|
-
<
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
</form>
|
|
232
|
-
</li>
|
|
233
|
-
</template>
|
|
144
|
+
<ul lb-query="roster">
|
|
145
|
+
<template>
|
|
146
|
+
<li lb-column="name"></li>
|
|
147
|
+
</template>
|
|
148
|
+
</ul>
|
|
234
149
|
```
|
|
235
150
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
151
|
+
The hub matches each row to a live row by its key. All rows decide
|
|
152
|
+
membership and order: a live row whose key did not arrive is removed. A
|
|
153
|
+
patch changes only the rows it names, and every other live row keeps its
|
|
154
|
+
content and its place.
|
|
155
|
+
|
|
156
|
+
A live row's root counts as a column when it carries `lb-column`, which is
|
|
157
|
+
how an `<option>`, whose content is text, shows the column it is.
|
|
158
|
+
|
|
159
|
+
A custom element that carries `lb-query` and a row template may decide where
|
|
160
|
+
a row goes and add scaffolding around the rows; see
|
|
161
|
+
[Holding rows](./custom-elements.md#holding-rows) and
|
|
162
|
+
[The Basic Widget Library](./widgets.md).
|
|
241
163
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
164
|
+
A `rows` query on an element with no row template lands nothing. That is the
|
|
165
|
+
insert form above: it sits inside `lb-query="roster"` so its request is for
|
|
166
|
+
`roster`, and it shows none of its rows.
|
|
245
167
|
|
|
246
|
-
|
|
247
|
-
`<tr>` or live row around the element that sends it, inside its scope. A
|
|
248
|
-
button submits its form the same way, whatever else sits beside it.
|
|
168
|
+
### Counting rows
|
|
249
169
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
170
|
+
The hub stamps every element that holds a row template with
|
|
171
|
+
`lb-query-row-count`, the number of live rows it holds. An empty query is a
|
|
172
|
+
stylesheet rule:
|
|
253
173
|
|
|
254
174
|
```html
|
|
255
|
-
<
|
|
256
|
-
<
|
|
257
|
-
<
|
|
258
|
-
<
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
</tr>
|
|
264
|
-
</tbody>
|
|
265
|
-
</table>
|
|
175
|
+
<div lb-query="roster">
|
|
176
|
+
<ul>
|
|
177
|
+
<template>
|
|
178
|
+
<li lb-column="name"></li>
|
|
179
|
+
</template>
|
|
180
|
+
</ul>
|
|
181
|
+
<p class="roster-empty">No members yet.</p>
|
|
182
|
+
</div>
|
|
266
183
|
```
|
|
267
184
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
185
|
+
```css
|
|
186
|
+
.roster-empty {
|
|
187
|
+
display: none;
|
|
188
|
+
}
|
|
189
|
+
[lb-query-row-count="0"] .roster-empty {
|
|
190
|
+
display: revert;
|
|
191
|
+
}
|
|
192
|
+
```
|
|
272
193
|
|
|
273
|
-
|
|
274
|
-
the
|
|
275
|
-
refuses it. A widget may dispatch either from any element, and the hub
|
|
276
|
-
gathers the same way; see
|
|
277
|
-
[Sending a request](./custom-elements.md#sending-a-request).
|
|
194
|
+
The element carrying `lb-query` is a `<div>` here rather than the `<ul>`, so
|
|
195
|
+
that the message is inside it and one rule reaches both.
|
|
278
196
|
|
|
279
|
-
|
|
197
|
+
## Conditional rendering
|
|
280
198
|
|
|
281
|
-
|
|
282
|
-
|
|
199
|
+
A page holds every element it can show. Write `lb-show` on an element,
|
|
200
|
+
naming the column that decides whether it is present:
|
|
283
201
|
|
|
284
202
|
```html
|
|
285
|
-
<template
|
|
203
|
+
<template>
|
|
286
204
|
<tr>
|
|
287
|
-
<td
|
|
288
|
-
<td><
|
|
205
|
+
<td lb-column="name"></td>
|
|
206
|
+
<td><button lb-request="lb-row-delete" lb-show="removable">Remove</button></td>
|
|
289
207
|
</tr>
|
|
290
208
|
</template>
|
|
291
209
|
```
|
|
292
210
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
211
|
+
```sql
|
|
212
|
+
(ledger_count = 0 AND system_behavior IS NULL) AS removable
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
A value of `null` or `false` takes the element out of the page, and any other
|
|
216
|
+
value puts it back. The hub never reads a string, so `"false"` is present:
|
|
217
|
+
have the query answer with a boolean or a null. A row that does not carry
|
|
218
|
+
the column leaves the element as it is, so a query answers with the column
|
|
219
|
+
in every row.
|
|
298
220
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
221
|
+
`lb-show` reads from the nearest ancestor row, as `lb-column` does. On an
|
|
222
|
+
element that also carries `lb-query`, the column belongs to the row around
|
|
223
|
+
it, so this picker takes its choices from `groups` and whether it is present
|
|
224
|
+
from the account row:
|
|
302
225
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
226
|
+
```html
|
|
227
|
+
<lb-options lb-query="groups" lb-column="group_id" lb-show="group_choice">
|
|
228
|
+
<template><option lb-column="name"></option></template>
|
|
229
|
+
</lb-options>
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
An element may show a column and be decided by it, which shows a note only
|
|
233
|
+
when there is one:
|
|
306
234
|
|
|
307
|
-
|
|
235
|
+
```html
|
|
236
|
+
<span lb-column="note" lb-show="note"></span>
|
|
237
|
+
```
|
|
308
238
|
|
|
309
|
-
A
|
|
310
|
-
|
|
239
|
+
A condition that is only a style is a class on an element that is present or
|
|
240
|
+
not:
|
|
311
241
|
|
|
312
242
|
```html
|
|
313
|
-
<
|
|
314
|
-
<option value="">Every team</option>
|
|
315
|
-
<option value="Engines">Engines</option>
|
|
316
|
-
</select>
|
|
243
|
+
<span lb-show="out_of_balance" class="danger">Out of balance</span>
|
|
317
244
|
```
|
|
318
245
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
replacing its DOM.
|
|
246
|
+
A condition that is not data, such as a collapsed section or an open menu,
|
|
247
|
+
has no column. Use `<details>`, a stylesheet, or a custom element. Every
|
|
248
|
+
element set from a column carries the value as `lb-column-value`, for a
|
|
249
|
+
stylesheet to select on.
|
|
324
250
|
|
|
325
|
-
|
|
326
|
-
|
|
251
|
+
An element that is not present cannot be clicked. The server still refuses
|
|
252
|
+
what a request may not do.
|
|
327
253
|
|
|
328
|
-
|
|
329
|
-
carries `lb-action` has that request refused.
|
|
254
|
+
### Where an absent element is
|
|
330
255
|
|
|
331
|
-
The
|
|
332
|
-
|
|
333
|
-
|
|
256
|
+
The hub moves an element whose column is off into a `<template lb-show>`
|
|
257
|
+
that stands where it stood, and moves it back out when the column turns on.
|
|
258
|
+
The builder ships every `lb-show` element already inside its template, so
|
|
259
|
+
nothing conditional shows until its row has landed. The developer never
|
|
260
|
+
writes that template.
|
|
334
261
|
|
|
335
|
-
|
|
262
|
+
- Nothing renders it, whatever a stylesheet says.
|
|
263
|
+
- It cannot be focused or clicked, assistive technology does not announce it,
|
|
264
|
+
and the hub does not gather from it.
|
|
265
|
+
- It is moved rather than rebuilt, so a custom element keeps its instance and
|
|
266
|
+
a control keeps what was typed into it.
|
|
267
|
+
- Rows keep landing on it and on everything inside it while it is away, so it
|
|
268
|
+
returns current.
|
|
336
269
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
270
|
+
An absent element keeps its place among its siblings, so a position selector
|
|
271
|
+
(`:first-child`, `:nth-child`, `:empty`, `+`, `~`) counts its template as a
|
|
272
|
+
sibling. A selector by tag, class or attribute is unaffected.
|
|
340
273
|
|
|
341
|
-
|
|
274
|
+
## Requests
|
|
275
|
+
|
|
276
|
+
Write `lb-request` on an element to send a request when the element commits.
|
|
277
|
+
Its value is the request name: one of the three requests Loadbare provides,
|
|
278
|
+
or a name the page declares.
|
|
342
279
|
|
|
343
280
|
```html
|
|
344
|
-
<
|
|
345
|
-
<tr>
|
|
346
|
-
<td lb-cell="name"></td>
|
|
347
|
-
<td><button lb-action="lb-row-delete" lb-show="removable">Remove</button></td>
|
|
348
|
-
</tr>
|
|
349
|
-
</template>
|
|
281
|
+
<button lb-request="mailRoster">Mail the roster</button>
|
|
350
282
|
```
|
|
351
283
|
|
|
352
|
-
|
|
353
|
-
|
|
284
|
+
Every request has one shape:
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{ "name": "lb-row-update", "query": "roster", "key": "17", "values": { "name": "Ada" } }
|
|
354
288
|
```
|
|
355
289
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
290
|
+
The hub takes `query`, `key` and `values` from where the element sits, and
|
|
291
|
+
sends each when it has one. The server runs the handler for the name and
|
|
292
|
+
answers with response items, which land like any others.
|
|
293
|
+
|
|
294
|
+
### Committing
|
|
295
|
+
|
|
296
|
+
| Element | Commits on |
|
|
297
|
+
|------------------|--------------------------------------------|
|
|
298
|
+
| A `<form>` | `submit` |
|
|
299
|
+
| A control | `change` |
|
|
300
|
+
| Any other element | `click` |
|
|
361
301
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
302
|
+
A click inside an element carrying `lb-request` commits it unless the click
|
|
303
|
+
lands on interactive content between them: a link, a button, a control or a
|
|
304
|
+
label, as HTML defines interactive content. A click into an input inside a
|
|
305
|
+
deletable row focuses the input.
|
|
306
|
+
|
|
307
|
+
A submit button with a form owner commits with its form. On `submit`, the
|
|
308
|
+
hub sends the submitter's `lb-request` when the submitter carries one, and
|
|
309
|
+
the form's otherwise, the way `formaction` overrides `action`:
|
|
366
310
|
|
|
367
311
|
```html
|
|
368
|
-
<
|
|
312
|
+
<template>
|
|
313
|
+
<li>
|
|
314
|
+
<form lb-request="lb-row-update">
|
|
315
|
+
<input lb-column="name" />
|
|
316
|
+
<button type="submit">Save</button>
|
|
317
|
+
<button type="submit" lb-request="lb-row-delete">Remove</button>
|
|
318
|
+
</form>
|
|
319
|
+
</li>
|
|
320
|
+
</template>
|
|
369
321
|
```
|
|
370
322
|
|
|
371
|
-
|
|
372
|
-
when
|
|
323
|
+
A control carrying `lb-request` sends on every `change`, which for an
|
|
324
|
+
`<input>` is when the user leaves it with a new value.
|
|
325
|
+
|
|
326
|
+
### Gathering
|
|
327
|
+
|
|
328
|
+
The hub gathers each control's value under the column its `lb-column` names.
|
|
329
|
+
It gathers from one group, the first of these that applies to the element
|
|
330
|
+
carrying `lb-request`:
|
|
331
|
+
|
|
332
|
+
1. The element carries `lb-column`: the element alone.
|
|
333
|
+
2. The element is a `<form>`: every control whose form owner is the form.
|
|
334
|
+
3. The element is in a live row: every control in that live row.
|
|
335
|
+
4. Otherwise: every control whose form owner is the element's form owner.
|
|
336
|
+
|
|
337
|
+
The hub gathers from controls only, and skips a control that belongs to a
|
|
338
|
+
query nested inside the group. A form owner includes a control outside the
|
|
339
|
+
form that names it with the HTML `form` attribute, which is how a table row,
|
|
340
|
+
where a form cannot go, sends one:
|
|
373
341
|
|
|
374
342
|
```html
|
|
375
|
-
<
|
|
343
|
+
<form id="add-member" lb-request="lb-row-insert"></form>
|
|
344
|
+
<table lb-query="roster">
|
|
345
|
+
<tbody>
|
|
346
|
+
<template>
|
|
347
|
+
<tr><td lb-column="name"></td><td lb-column="role"></td></tr>
|
|
348
|
+
</template>
|
|
349
|
+
</tbody>
|
|
350
|
+
<tfoot>
|
|
351
|
+
<tr>
|
|
352
|
+
<td><input lb-column="name" form="add-member" /></td>
|
|
353
|
+
<td>
|
|
354
|
+
<input lb-column="role" form="add-member" />
|
|
355
|
+
<button form="add-member">Add</button>
|
|
356
|
+
</td>
|
|
357
|
+
</tr>
|
|
358
|
+
</tfoot>
|
|
359
|
+
</table>
|
|
376
360
|
```
|
|
377
361
|
|
|
378
|
-
|
|
379
|
-
|
|
362
|
+
The hub sends the request for the nearest ancestor `lb-query` of the
|
|
363
|
+
controls it gathered, and takes `key` from their nearest ancestor row when
|
|
364
|
+
that row is of the same query. When it gathers nothing, it uses the nearest
|
|
365
|
+
ancestor `lb-query` and row of the element carrying `lb-request` instead.
|
|
366
|
+
Controls gathered from two different queries are an error, and the hub
|
|
367
|
+
sends nothing.
|
|
380
368
|
|
|
381
|
-
###
|
|
369
|
+
### Inserting, updating and deleting rows
|
|
382
370
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
developer never writes that template.
|
|
371
|
+
Loadbare provides three requests. Each runs the handler the page declares
|
|
372
|
+
for its query under `crud`; see [page files](./page-files.md#crud).
|
|
386
373
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
-
|
|
390
|
-
|
|
391
|
-
-
|
|
392
|
-
keeps what was typed into it.
|
|
393
|
-
- Values keep landing on it, and on every cell and scope inside it, while it
|
|
394
|
-
is away, so it returns current.
|
|
374
|
+
| Request name | Needs | Runs |
|
|
375
|
+
|-----------------|--------------------------|-------------|
|
|
376
|
+
| `lb-row-insert` | `query`, `values` | `rowInsert` |
|
|
377
|
+
| `lb-row-update` | `query`, `key`, `values` | `rowUpdate` |
|
|
378
|
+
| `lb-row-delete` | `query`, `key` | `rowDelete` |
|
|
395
379
|
|
|
396
|
-
The
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
A condition never changes the structure of a page. An absent element keeps
|
|
400
|
-
its place among its siblings, so a position selector (`:first-child`,
|
|
401
|
-
`:nth-child`, `:empty`, `+`, `~`) counts its template as a sibling. A selector
|
|
402
|
-
by tag, class or attribute is unaffected. Only a list changes a page's
|
|
403
|
-
structure.
|
|
380
|
+
The hub sends one of these only when it has what the table lists, and
|
|
381
|
+
otherwise reports to the console. An insert or an update that gathers
|
|
382
|
+
nothing is not sent.
|
|
404
383
|
|
|
405
|
-
A
|
|
406
|
-
|
|
384
|
+
A key comes from a live row, or from an element a `row` landed on, so all
|
|
385
|
+
three work against either kind:
|
|
407
386
|
|
|
408
387
|
```html
|
|
409
|
-
<
|
|
388
|
+
<form lb-query="profile" lb-request="lb-row-update">
|
|
389
|
+
<input lb-column="email" />
|
|
390
|
+
<button type="submit">Save</button>
|
|
391
|
+
</form>
|
|
410
392
|
```
|
|
411
393
|
|
|
412
|
-
|
|
394
|
+
An update from a single control sends that control's column alone. An
|
|
395
|
+
update from a form or a live row sends every control it gathers. The
|
|
396
|
+
application writes one `rowUpdate` for both, setting the columns `values`
|
|
397
|
+
names, as an SQL UPDATE does.
|
|
413
398
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
- `lb-show` with no row around it: outside every scope, on a scope with none
|
|
417
|
-
around it, or in a list scope outside its row template, where nothing lands.
|
|
418
|
-
- `lb-show` on a `<template>`.
|
|
399
|
+
After a successful insert gathered from a form, the hub resets the form. A
|
|
400
|
+
failed insert leaves the entry for the user to correct.
|
|
419
401
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
carries the value that landed on it as `lb-value`, for a widget to read or a
|
|
423
|
-
stylesheet to select on.
|
|
402
|
+
Leave `lb-request` off a control inside a form or a live row that sends its
|
|
403
|
+
own update, or the same edit is written twice.
|
|
424
404
|
|
|
425
|
-
###
|
|
405
|
+
### Declared requests
|
|
426
406
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
list a stylesheet rule rather than code anywhere:
|
|
407
|
+
A request name the page declares under `handlers` runs the application's
|
|
408
|
+
handler; see [page files](./page-files.md#handlers). It gathers the same way
|
|
409
|
+
and carries `query`, `key` and `values` when it finds them:
|
|
431
410
|
|
|
432
411
|
```html
|
|
433
|
-
<
|
|
434
|
-
<
|
|
435
|
-
<
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
<p class="roster-empty">No members yet.</p>
|
|
440
|
-
</div>
|
|
412
|
+
<template>
|
|
413
|
+
<li>
|
|
414
|
+
<span lb-column="name"></span>
|
|
415
|
+
<button lb-request="sendReminder">Remind</button>
|
|
416
|
+
</li>
|
|
417
|
+
</template>
|
|
441
418
|
```
|
|
442
419
|
|
|
420
|
+
A button carries no value, so a declared request from one sends the values
|
|
421
|
+
of the live row or form it sits in, and the server computes the rest.
|
|
422
|
+
|
|
423
|
+
A request name beginning with `lb-` that is not one of the three is refused
|
|
424
|
+
by the builder.
|
|
425
|
+
|
|
426
|
+
### Request state
|
|
427
|
+
|
|
428
|
+
The hub stamps the element that committed:
|
|
429
|
+
|
|
430
|
+
| Stamp | Means |
|
|
431
|
+
|----------------------|-----------------------------------------------|
|
|
432
|
+
| `lb-request-pending` | The round trip is in flight |
|
|
433
|
+
| `lb-request-error` | The round trip failed, until the next request |
|
|
434
|
+
|
|
435
|
+
`lb-request-error` covers a non-2xx response, a network failure and a
|
|
436
|
+
timeout alike. The hub aborts a round trip after ten seconds, and sets
|
|
437
|
+
`aria-busy="true"` for as long as `lb-request-pending` is present.
|
|
438
|
+
|
|
439
|
+
The hub ignores a commit on an element carrying `lb-request-pending`, so a
|
|
440
|
+
pending button is already disabled and a stylesheet only shows it:
|
|
441
|
+
|
|
443
442
|
```css
|
|
444
|
-
|
|
445
|
-
|
|
443
|
+
[lb-request-pending] {
|
|
444
|
+
opacity: 0.5;
|
|
446
445
|
}
|
|
447
|
-
[lb-
|
|
448
|
-
|
|
446
|
+
[lb-request-error] {
|
|
447
|
+
outline: 2px solid red;
|
|
449
448
|
}
|
|
450
449
|
```
|
|
451
450
|
|
|
452
|
-
The
|
|
453
|
-
is inside it and the same rule can reach both.
|
|
451
|
+
### The request event
|
|
454
452
|
|
|
455
|
-
|
|
453
|
+
The hub dispatches every request as a bubbling `lb-request` event from the
|
|
454
|
+
element that committed, with the request as its `detail`, before sending it.
|
|
455
|
+
An ancestor may stop the event, and the request is not sent. A custom
|
|
456
|
+
element may dispatch the event itself; see
|
|
457
|
+
[Sending a request](./custom-elements.md#sending-a-request).
|
|
456
458
|
|
|
457
|
-
|
|
458
|
-
button, the form, or the widget itself:
|
|
459
|
+
## The URL
|
|
459
460
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
461
|
+
The hub serves one query of its own, `lb-url`: one row holding the path, the
|
|
462
|
+
page's title, and every query parm as a column. A control inside
|
|
463
|
+
`lb-query="lb-url"` that sends `lb-row-update` sets a query parm, and the hub
|
|
464
|
+
answers it with no round trip:
|
|
464
465
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
466
|
+
```html
|
|
467
|
+
<div lb-query="lb-url">
|
|
468
|
+
<select lb-column="team" lb-request="lb-row-update">
|
|
469
|
+
<option value="">Every team</option>
|
|
470
|
+
<option value="Engines">Engines</option>
|
|
471
|
+
</select>
|
|
472
|
+
</div>
|
|
473
|
+
```
|
|
468
474
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
disable itself. An application that styles neither behaves correctly and
|
|
472
|
-
shows nothing.
|
|
475
|
+
See [The URL](./chrome.md#the-url) for its columns, links, history, and the
|
|
476
|
+
unknown-page dialog.
|
|
473
477
|
|
|
474
|
-
|
|
475
|
-
ignored, so a pending one is already disabled and the stylesheet only shows
|
|
476
|
-
it. A widget is not held back, since one that sends on change must send its
|
|
477
|
-
latest value. The hub also sets `aria-busy="true"` for as long as
|
|
478
|
-
`lb-pending` is present.
|
|
478
|
+
## The markup checks
|
|
479
479
|
|
|
480
|
-
|
|
480
|
+
The developer writes `lb-` attributes in markup, and script never assigns
|
|
481
|
+
them. The builder checks the markup of the chrome and of every page, after
|
|
482
|
+
expansion, and refuses to build:
|
|
481
483
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
484
|
+
- an `lb-` attribute that is not one of the seven the developer writes
|
|
485
|
+
- `lb-request` naming an `lb-` request other than the three Loadbare provides
|
|
486
|
+
- `lb-url-link` on anything but an `<a>`
|
|
487
|
+
- `lb-url-push` on an element without `lb-request`
|
|
488
|
+
- `lb-url-unknown` on anything but a `<dialog>`, or outside `<lb-hub>`
|
|
489
|
+
- `lb-show` on a `<template>`, or on a row template's root
|
|
490
|
+
- `lb-show` with no `lb-query` around it
|