Skip to content

Commit

Permalink
add support for <select> as a form input
Browse files Browse the repository at this point in the history
Closes #5
  • Loading branch information
lovasoa committed Jun 11, 2023
1 parent 6bdbd8b commit 7ea9457
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "sqlpage"
version = "0.6.10"
version = "0.6.11"
edition = "2021"
description = "A SQL-only web application framework. Takes .sql files and formats the query result using pre-made configurable professional-looking components."
keywords = ["web", "sql", "framework"]
Expand Down
26 changes: 23 additions & 3 deletions examples/official-site/sqlpage/migrations/01_documentation.sql
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ INSERT INTO parameter(component, name, description, type, top_level, optional) S
('label', 'A friendly name for the text field to show to the user.', 'TEXT', FALSE, TRUE),
('placeholder', 'A placeholder text that will be shown in the field when is is empty.', 'TEXT', FALSE, TRUE),
('value', 'A default value that will already be present in the field when the user loads the page.', 'TEXT', FALSE, TRUE),
('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),
('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),
('min', 'The minimum value to accept for an input of type number', 'NUMBER', FALSE, TRUE),
('max', 'The minimum value to accept for an input of type number', 'NUMBER', FALSE, TRUE),
Expand Down Expand Up @@ -251,12 +252,31 @@ to allow users to create a new component.
When loading the page, the value for `$component` will be `NULL` if no value has been submitted.
',
json('[{"component":"form", "action": "documentation.sql"}, {"name": "component"}]')),
('form', 'A user registration form.', json('[{"component":"form", "title": "User", "validate": "Create new user"}, '||
('form', 'A user registration form, illustrating the use of required fields, and different input types.',
json('[{"component":"form", "title": "User", "validate": "Create new user"}, '||
'{"name": "First name", "placeholder": "John"}, '||
'{"name": "Last name", "required": true, "description": "We need your last name for legal purposes."},'||
'{"name": "Resume", "type": "textarea"},'||
'{"name": "Gender", "type": "select", "options": [{"name": "Female", "value": 0}, {"name": "Male", "value": 1}, {"name": "Other", "value": 2}]},'||
'{"name": "Birth date", "type": "date", "max": "2010-01-01", "value": "1994-04-16"}]'));
'{"name": "Birth date", "type": "date", "max": "2010-01-01", "value": "1994-04-16"}]')),
('form', 'This example illustrates the use of the `select` type.
In this select input, the various options are hardcoded, but they could also be loaded from a database table,
using a function to convert the rows into a json array like
- `json_group_array()` in SQLite,
- `json_agg()` in Postgres, or
- `JSON_ARRAYAGG()` in MySQL.
In SQLite, the query would look like
```sql
SELECT
''select'' as type,
json_group_array(json_object("label", name, "value", id)) as options
FROM fruits
```
', json('[{"component":"form"}, '||
'{"name": "Fruit", "type": "select", "value": 1, "options": '||
'"[{\"label\": \"Orange\", \"value\": 0}, {\"label\": \"Apple\", \"value\": 1}, {\"label\": \"Banana\", \"value\": 3}]"}
]'));

INSERT INTO component(name, icon, description) VALUES
('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.');
Expand Down
40 changes: 31 additions & 9 deletions sqlpage/templates/form.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,45 @@
{{default label name}}
{{~#if required}}
<span class="text-danger ms-1" aria-label="required" title="required">*</span>{{/if}}
<input
type="{{default type "text"}}"
{{#if (eq type 'textarea')}}
<textarea
name="{{name}}"
class="form-control"
placeholder="{{placeholder}}"
{{#if value}}value="{{value}}" {{/if~}}
{{~#if max}}max="{{max}}" {{/if~}}
{{~#if min}}min="{{min}}" {{/if~}}
{{~#if step}}step="{{step}}" {{/if~}}
{{~#if minlength}}minlength="{{minlength}}" {{/if~}}
{{~#if maxlength}}maxlength="{{maxlength}}" {{/if~}}
{{~#if required}}required="required" {{/if~}}
rows="{{default rows 3}}"
{{#if value}}value="{{value}}" {{/if~}}
{{~#if minlength}}minlength="{{minlength}}" {{/if~}}
{{~#if maxlength}}maxlength="{{maxlength}}" {{/if~}}
{{~#if required}}required="required" {{/if~}}
{{~#if autofocus}}autofocus {{/if~}}
></textarea>
{{else}}{{#if (eq type 'select')}}
<select name="{{name}}" class="form-select"
{{~#if required}} required="required" {{/if~}}
{{~#if autofocus}} autofocus {{/if~}}
{{~#if multiple}} multiple {{/if~}}
>
{{#each (parse_json options)}}
<option value="{{value}}" {{#if (eq ../value value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
{{else}}
<input name="{{name}}" class="form-control"
{{~#if type}} type="{{type}}" {{/if~}}
{{~#if placeholder}} placeholder="{{placeholder}}" {{/if~}}
{{~#if value}} value="{{value}}" {{/if~}}
{{~#if max}} max="{{max}}" {{/if~}}
{{~#if min}} min="{{min}}" {{/if~}}
{{~#if step}} step="{{step}}" {{/if~}}
{{~#if minlength}} minlength="{{minlength}}" {{/if~}}
{{~#if maxlength}} maxlength="{{maxlength}}" {{/if~}}
{{~#if required}} required="required" {{/if~}}
{{~#if (or (eq autocomplete false) (eq autocomplete 0))}}autocomplete="off" {{/if~}}
{{~#if (or (eq autocomplete true) (eq autocomplete 1))}}autocomplete="on" {{/if~}}
{{~#if (gt (len autocomplete) 1)}}autocomplete="{{autocomplete}}" {{/if~}}
{{~#if autofocus}}autofocus {{/if~}}
>
{{/if}}{{/if}}
{{#if description}}
<small class="form-hint mt-0">{{description}}</small>
{{/if}}
Expand Down
11 changes: 11 additions & 0 deletions src/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,17 @@ impl AllTemplates {
handlebars_helper!(stringify: |v: Json| v.to_string());
handlebars.register_helper("stringify", Box::new(stringify));

handlebars_helper!(parse_json: |v: Json| match v {
obj @ serde_json::value::Value::String(s) =>
serde_json::from_str(s)
.unwrap_or_else(|_| {
log::warn!("Failed to parse JSON string: {}", s);
obj.clone()
}),
other => other.clone()
});
handlebars.register_helper("parse_json", Box::new(parse_json));

handlebars_helper!(default: |a: Json, b:Json| if a.is_null() {b} else {a}.clone());
handlebars.register_helper("default", Box::new(default));

Expand Down

0 comments on commit 7ea9457

Please sign in to comment.