6.1.3 Form0
| (require conscript/form0) | package: conscript |
This module reprovides nearly all the bindings in the forms library for creating forms in your study pages, as well as some additional conveniences for using that library.
See the Forms Recipe for steps and examples to combine these procedures into working forms in your study steps.
In addition to the tutorials in this documentation, see (part ("(lib forms/forms.scrbl)" "top")) for a tutorial that walks through the functionality of Forms.
syntax
(form+submit [id formlet-expr] ...)
Each id should be a variable declared with defvar or similar, and each formlet-expr should be one of the values in (part ("(lib forms/forms.scrbl)" "formlets")), or an ensure expression combining one of those formlets with one or more (part ("(lib forms/forms.scrbl)" "validators")).
> (defvar my-name) > (defvar my-middle-name) > (defvar my-age)
> (define-values (form-data on-submit) (form+submit [my-name (ensure binding/text (required))] [my-middle-name binding/text] [my-age (ensure binding/number (required))]))
procedure
(form f action render [ #:id id #:enctype enctype #:combine combine-proc #:defaults defaults]) → xexpr? f : form? action : (-> void?) render : (-> (widget-renderer/c) xexpr?) id : string? = "" enctype : string? = "multipart/form-data"
combine-proc : (-> any/c any/c any/c any/c) =
(λ (k v1 v2) (if (pair? v1) (append v1 (list v2)) (list v1 v2))) defaults : hash? = (hash)
Identical to the form from congame/components/study except for the default combine-proc: when there are multiple bindings for the same field, this procedure’s default combine-proc combines all the bindings for that field into a list (as described in the documentation for form-run).
Use dyn:form when you need to construct forms dynamically at runtime, where the number or types of fields are determined by a computation rather than specified statically. For statically defined forms, prefer form+submit or form*.
The constructor is a function that receives the validated values from each field in order and returns the final result. The children is a list of pairs, where each pair contains a field name (as a symbol) and a formlet (or nested form) for that field.
#lang conscript (require conscript/form0 racket/match) (defvar taste) (defvar price) (defvar healthiness) (define range-formlet (ensure binding/number (required) (number-in-range 1 5))) (defstep (dynamic-form-example) (define f (dyn:form list (for/list ([k (in-list '(taste price healthiness))]) (cons k range-formlet)))) (define (on-submit vs) (match-define (list t p h) vs) (set! taste t) (set! price p) (set! healthiness h)) (define (render rw) @md*{@rw["taste" (input-range "Taste")] @rw["price" (input-range "Price")] @rw["healthiness" (input-range "Healthiness")] @|submit-button|}) @md{# Dynamic Form @form[f on-submit render]})
In this example, the list constructor combines the validated values into a list. You can also use a custom constructor to bundle results into a hash or other data structure:
(dyn:form (lambda vs (for/hash ([k (in-list '(taste price healthiness))] [v (in-list vs)]) (values k v))) (for/list ([k (in-list '(taste price healthiness))]) (cons k range-formlet)))
See the Forms reference in the forms library documentation for more details on the underlying struct.
procedure
(required-unless pred) →
(-> (or/c string? #f) (or/c (cons/c 'ok any/c) (cons/c 'err string?))) pred : (-> any/c)
procedure
(at-least n) →
(-> (or/c number? #f) (or/c (cons/c 'ok any/c) (cons/c 'err string?))) n : number?
> (defvar contribution)
> (define-values (form-data on-submit) (form+submit [contribution (ensure binding/number (at-least 0))]))
procedure
(number-in-range lo hi) →
(-> (or/c number? #f) (or/c (cons/c 'ok any/c) (cons/c 'err string?))) lo : number? hi : number?
> (defvar rating)
> (define-values (form-data on-submit) (form+submit [rating (ensure binding/number (number-in-range 1 10))]))
procedure
(list-longer-than n) →
(-> any/c (or/c (cons/c 'ok any/c) (cons/c 'err string?))) n : exact-nonnegative-integer?
> (defvar ratings)
> (define-values (form-data on-submit) (form+submit [ratings (ensure binding/list (list-longer-than 3))]))
procedure
(radio option [label #:attributes attrs]) → widget/c
option : string? label : (or/c string? #f) = #f attrs : list? = null
Unlike radios, which renders a complete radio group with a label and automatic error display, radio renders only the radio button itself. This allows you to place individual radio buttons in custom layouts, such as inside table cells or in non-linear arrangements.
When using radio widgets, you must manually display validation errors using the errors widget, since individual radio buttons do not include error rendering.
#lang conscript (require conscript/form0) (defvar choice) (defstep (custom-layout) (define choices '(("a" . a) ("b" . b) ("x" . x) ("y" . y))) (define-values (f on-submit) (form+submit [choice (ensure binding/text (required) (one-of choices))])) (define (render rw) @md*{@table[@tr[@td[@rw["choice" @radio["a"]{A}]] @td[@rw["choice" @radio["x"]{X}]]] @tr[@td[@rw["choice" @radio["b"]{B}]] @td[@rw["choice" @radio["y"]{Y}]]]] @rw["choice" errors] @|submit-button|}) @md{# Make Your Choice @form[f on-submit render]})
In this example, four radio buttons sharing the field name "choice" are arranged in a 2×2 table. The errors widget is placed below the table to display any validation messages.
procedure
(checkbox [label] #:attributes attrs) → widget/c
label : (or/c string? #f) = #f attrs : null
procedure
(input-date [label] #:attributes attrs) → widget/c
label : (or/c string? #f) = #f attrs : null
procedure
(input-datetime [label] #:attributes attrs) → widget/c
label : (or/c string? #f) = #f attrs : null
procedure
(input-email [label] #:attributes attrs) → widget/c
label : (or/c string? #f) = #f attrs : null
procedure
(input-file label) → widget/c
label : (or/c string? #f)
procedure
(input-number [label] #:attributes attrs) → widget/c
label : (or/c string? #f) = #f attrs : null
procedure
(input-range [label] #:attributes attrs) → widget/c
label : (or/c string? #f) = #f attrs : null
procedure
(input-text [label] #:attributes attrs) → widget/c
label : (or/c string? #f) = #f attrs : null
procedure
(input-time [label] #:attributes attrs) → widget/c
label : (or/c string? #f) = #f attrs : null
procedure
(textarea [label] #:attributes attrs) → widget/c
label : (or/c string? #f) = #f attrs : null
Note: input-file creates a file upload widget and does not accept the #:attributes parameter.
> ((checkbox) "agree" #f null)
'(div
((class "field-group"))
(label () "Agree" (input ((type "checkbox") (name "agree")))))
> ((input-time) "arrived" #f null)
'(div
((class "field-group"))
(label () "Arrived" (input ((type "time") (name "arrived")))))
procedure
(radios options [label] #:attributes attrs) → widget/c
options : radio-options/c label : (or/c string? #f) = #f attrs : null
procedure
(select options label #:attributes attrs) → widget/c
options : radio-options/c label : string? attrs : null
procedure
(checkboxes options #:attributes attrs) → widget/c
options : radio-options/c attrs : null
> (define opts '(("high" . "Take the high road") ("low" . "Go low"))) > ((radios opts "Metaphorical highway selection") "road" #f null)
'(div
((class "group"))
(label
((class "radio-group"))
"Metaphorical highway selection"
(div
(label
(input ((type "radio") (name "road") (value "high")))
"Take the high road")
(label (input ((type "radio") (name "road") (value "low"))) "Go low"))))
> ((checkboxes opts) "roadboxen" #f null)
'(div
((class "group"))
(div
((class "div"))
(label
()
(input ((type "checkbox") (name "roadboxen") (value "high")))
"Take the high road")
(label
()
(input ((type "checkbox") (name "roadboxen") (value "low")))
"Go low")))
This widget has the same signature as other form widgets (name, value, errors) and can be used with the rw renderer function. It displays any validation error messages associated with the field named name.
Use errors when you need to display validation messages separately from the input widget itself—for example, when using individual radio buttons in a custom layout:
(define (render rw) (md* (rw "choice" (radio "a" "Option A"))"\n" (rw "choice" (radio "b" "Option B"))"\n" (rw "choice" errors)"\n" submit-button))
In this example, the errors widget is placed after the radio buttons to show any validation messages (such as "This field is required") in a single location.
procedure
(make-autofill v) → xexpr?
v : any/c
Use this when rendering step pages to instruct the bot how to fill in certain fields.
> (make-autofill (hasheq 'example (hasheq 'name "Frank" 'mood "Delicate"))) '(meta ((name "formular-autofill") (content "")))
> (parameterize ([current-user-bot? #t]) (make-autofill (hasheq 'example (hasheq 'name "Frank" 'mood "Delicate"))))
'(meta
((name "formular-autofill")
(content
"#hasheq((example . #hasheq((mood . \"Delicate\") (name . \"Frank\"))))")))
procedure
(make-autofill-meta ht) → xexpr?
ht : hash?
Use this when rendering step pages to provide autofill instructions that should always be present in the page markup.
> (make-autofill-meta (hasheq 'example (hasheq 'name "Frank" 'mood "Delicate")))
'(meta
((name "formular-autofill")
(content
"#hasheq((example . #hasheq((mood . \"Delicate\") (name . \"Frank\"))))")))