Skip to content

Commit 7ea9457

Browse files
committed
add support for <select> as a form input
Closes #5
1 parent 6bdbd8b commit 7ea9457

File tree

5 files changed

+67
-14
lines changed

5 files changed

+67
-14
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "sqlpage"
3-
version = "0.6.10"
3+
version = "0.6.11"
44
edition = "2021"
55
description = "A SQL-only web application framework. Takes .sql files and formats the query result using pre-made configurable professional-looking components."
66
keywords = ["web", "sql", "framework"]

examples/official-site/sqlpage/migrations/01_documentation.sql

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ INSERT INTO parameter(component, name, description, type, top_level, optional) S
223223
('label', 'A friendly name for the text field to show to the user.', 'TEXT', FALSE, TRUE),
224224
('placeholder', 'A placeholder text that will be shown in the field when is is empty.', 'TEXT', FALSE, TRUE),
225225
('value', 'A default value that will already be present in the field when the user loads the page.', 'TEXT', FALSE, TRUE),
226+
('options', 'A json array of objects containing the label and value of all possible options of a select field. Used only when type=select.', 'JSON', FALSE, TRUE),
226227
('required', 'Set this to true to prevent the form contents from being sent if this field is left empty by the user.', 'BOOL', FALSE, TRUE),
227228
('min', 'The minimum value to accept for an input of type number', 'NUMBER', FALSE, TRUE),
228229
('max', 'The minimum value to accept for an input of type number', 'NUMBER', FALSE, TRUE),
@@ -251,12 +252,31 @@ to allow users to create a new component.
251252
When loading the page, the value for `$component` will be `NULL` if no value has been submitted.
252253
',
253254
json('[{"component":"form", "action": "documentation.sql"}, {"name": "component"}]')),
254-
('form', 'A user registration form.', json('[{"component":"form", "title": "User", "validate": "Create new user"}, '||
255+
('form', 'A user registration form, illustrating the use of required fields, and different input types.',
256+
json('[{"component":"form", "title": "User", "validate": "Create new user"}, '||
255257
'{"name": "First name", "placeholder": "John"}, '||
256258
'{"name": "Last name", "required": true, "description": "We need your last name for legal purposes."},'||
257259
'{"name": "Resume", "type": "textarea"},'||
258-
'{"name": "Gender", "type": "select", "options": [{"name": "Female", "value": 0}, {"name": "Male", "value": 1}, {"name": "Other", "value": 2}]},'||
259-
'{"name": "Birth date", "type": "date", "max": "2010-01-01", "value": "1994-04-16"}]'));
260+
'{"name": "Birth date", "type": "date", "max": "2010-01-01", "value": "1994-04-16"}]')),
261+
('form', 'This example illustrates the use of the `select` type.
262+
In this select input, the various options are hardcoded, but they could also be loaded from a database table,
263+
using a function to convert the rows into a json array like
264+
- `json_group_array()` in SQLite,
265+
- `json_agg()` in Postgres, or
266+
- `JSON_ARRAYAGG()` in MySQL.
267+
268+
269+
In SQLite, the query would look like
270+
```sql
271+
SELECT
272+
''select'' as type,
273+
json_group_array(json_object("label", name, "value", id)) as options
274+
FROM fruits
275+
```
276+
', json('[{"component":"form"}, '||
277+
'{"name": "Fruit", "type": "select", "value": 1, "options": '||
278+
'"[{\"label\": \"Orange\", \"value\": 0}, {\"label\": \"Apple\", \"value\": 1}, {\"label\": \"Banana\", \"value\": 3}]"}
279+
]'));
260280

261281
INSERT INTO component(name, icon, description) VALUES
262282
('chart', 'timeline', 'A component that plots data. Line, area, bar, and pie charts are all supported. Each item in the component is a data point in the graph.');

sqlpage/templates/form.handlebars

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,45 @@
2626
{{default label name}}
2727
{{~#if required}}
2828
<span class="text-danger ms-1" aria-label="required" title="required">*</span>{{/if}}
29-
<input
30-
type="{{default type "text"}}"
29+
{{#if (eq type 'textarea')}}
30+
<textarea
3131
name="{{name}}"
3232
class="form-control"
3333
placeholder="{{placeholder}}"
34-
{{#if value}}value="{{value}}" {{/if~}}
35-
{{~#if max}}max="{{max}}" {{/if~}}
36-
{{~#if min}}min="{{min}}" {{/if~}}
37-
{{~#if step}}step="{{step}}" {{/if~}}
38-
{{~#if minlength}}minlength="{{minlength}}" {{/if~}}
39-
{{~#if maxlength}}maxlength="{{maxlength}}" {{/if~}}
40-
{{~#if required}}required="required" {{/if~}}
34+
rows="{{default rows 3}}"
35+
{{#if value}}value="{{value}}" {{/if~}}
36+
{{~#if minlength}}minlength="{{minlength}}" {{/if~}}
37+
{{~#if maxlength}}maxlength="{{maxlength}}" {{/if~}}
38+
{{~#if required}}required="required" {{/if~}}
39+
{{~#if autofocus}}autofocus {{/if~}}
40+
></textarea>
41+
{{else}}{{#if (eq type 'select')}}
42+
<select name="{{name}}" class="form-select"
43+
{{~#if required}} required="required" {{/if~}}
44+
{{~#if autofocus}} autofocus {{/if~}}
45+
{{~#if multiple}} multiple {{/if~}}
46+
>
47+
{{#each (parse_json options)}}
48+
<option value="{{value}}" {{#if (eq ../value value)}}selected{{/if}}>{{label}}</option>
49+
{{/each}}
50+
</select>
51+
{{else}}
52+
<input name="{{name}}" class="form-control"
53+
{{~#if type}} type="{{type}}" {{/if~}}
54+
{{~#if placeholder}} placeholder="{{placeholder}}" {{/if~}}
55+
{{~#if value}} value="{{value}}" {{/if~}}
56+
{{~#if max}} max="{{max}}" {{/if~}}
57+
{{~#if min}} min="{{min}}" {{/if~}}
58+
{{~#if step}} step="{{step}}" {{/if~}}
59+
{{~#if minlength}} minlength="{{minlength}}" {{/if~}}
60+
{{~#if maxlength}} maxlength="{{maxlength}}" {{/if~}}
61+
{{~#if required}} required="required" {{/if~}}
4162
{{~#if (or (eq autocomplete false) (eq autocomplete 0))}}autocomplete="off" {{/if~}}
4263
{{~#if (or (eq autocomplete true) (eq autocomplete 1))}}autocomplete="on" {{/if~}}
4364
{{~#if (gt (len autocomplete) 1)}}autocomplete="{{autocomplete}}" {{/if~}}
4465
{{~#if autofocus}}autofocus {{/if~}}
4566
>
67+
{{/if}}{{/if}}
4668
{{#if description}}
4769
<small class="form-hint mt-0">{{description}}</small>
4870
{{/if}}

src/templates.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,17 @@ impl AllTemplates {
162162
handlebars_helper!(stringify: |v: Json| v.to_string());
163163
handlebars.register_helper("stringify", Box::new(stringify));
164164

165+
handlebars_helper!(parse_json: |v: Json| match v {
166+
obj @ serde_json::value::Value::String(s) =>
167+
serde_json::from_str(s)
168+
.unwrap_or_else(|_| {
169+
log::warn!("Failed to parse JSON string: {}", s);
170+
obj.clone()
171+
}),
172+
other => other.clone()
173+
});
174+
handlebars.register_helper("parse_json", Box::new(parse_json));
175+
165176
handlebars_helper!(default: |a: Json, b:Json| if a.is_null() {b} else {a}.clone());
166177
handlebars.register_helper("default", Box::new(default));
167178

0 commit comments

Comments
 (0)