File size: 8,659 Bytes
43203b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
\input texinfo   @c -*-texinfo-*-
@comment $Id@w{$}
@comment %**start of header
@settitle CL-FORMS
@syncodeindex pg cp
@setfilename cl-forms.info
@include common-lisp.texi

@copying

Copyright @copyright{} 2021 Mariano Montone

@end copying


@dircategory Common Lisp
@direntry
* cl-forms: (cl-forms) Common Lisp web forms handling library.
@end direntry

@titlepage
@title CL-FORMS

@author Mariano Montone ( @email{marianomontone@@gmail.com} )
@page
@vskip 0pt plus 1filll
@insertcopying
@end titlepage

@contents

@node Top
@top Top
@cindex cl-forms

@menu
* Intro::
* Installation::
* Usage::
* API::
* Index::
@end menu

@node Intro
@chapter Introduction
@anchor{introduction}

CL-FORMS is a web forms handling library for Common Lisp.

Although it is potentially framework agnostic, it runs on top of Hunchentoot at the moment.

It features:

@itemize @bullet
@item
Several form field types: String, boolean, integer, email, password fields. And more.
@item Custom fields. CL-FORMS is extensible and it is possible to define new field types.
@item Server and client side validation
@item Rendering backends. Forms can be rendered via CL-WHO, or Djula, or something else; the backend is pluggable. The default renderer is CL-WHO.
@item Themes (like Bootstrap)
@item Control on rendering and layout.
@item Handling of form errors.
@item CSRF protection
@end itemize

@node Installation
@chapter Installation
@anchor{installation}


With Quicklisp:

@lisp
(ql:quickload "cl-forms")
@end lisp

When you want to use a form renderer such as @code{:who} or @code{:djula}, quickload the associated package: @code{cl-forms.who}, @code{cl-forms.who.bootstrap}, @code{cl-forms.djula}.

@node Usage
@chapter Usage
@anchor{usage}

@section Basics

Use @clref{CL-FORMS, DEFFORM, macro} to define a form. Example:

@lisp
(defform fields-form (:action "/fields-post")
  ((name :string :value "")
   (ready :boolean :value t)
   (sex :choice :choices (list "Male" "Female") :value "Male")
   (submit :submit :label "Create")))
@end lisp

On your web handler, grab the form via @clref{CL-FORMS,FIND-FORM,function}, select a renderer with `with-form-renderer`and then render the form with @clref{CL-FORMS,RENDER-FORM,function}:

@lisp
(let ((form (forms:find-form 'fields-form)))
   (forms:with-form-renderer :who
      (forms:render-form form))
@end lisp

To handle the form, grab it via @clref{CL-FORMS,FIND-FORM,function} and then call @clref{CL-FORMS,HANDLE-REQUEST,function} (you should probably also call @clref{CL-FORMS,VALIDATE-FORM,function} after). 
Then bind form fields via either @clref{CL-FORMS,WITH-FORM-FIELD-VALUES,macro}, that binds the form field values; or @clref{CL-FORMS,WITH-FORM-FIELDS,macro} that binds the form fields.

@lisp
(let ((form (forms:find-form 'fields-form)))
      (forms:handle-request form)
      (forms:with-form-field-values (name ready sex) form
          (who:with-html-output (forms.who::*html*)
             (:ul
               (:li (who:fmt "Name: ~A" name))
               (:li (who:fmt "Ready: ~A" ready))
               (:li (who:fmt "Sex: ~A" sex))))))
@end lisp

To output HTML on the REPL, do:

@lisp
(with-output-to-string (forms.who:*html*)
   (let ((form (forms:find-form 'fields-form)))
           (forms:with-form-renderer :who
              (forms:render-form form))))
@end lisp

Please have a look at the demo sources for more examples of how to use the library

@section Demo

There's a demo included. To run:

@lisp
(require :cl-forms.demo)
(forms.test:run-demo)
@end lisp

@subsection Basic example

Define a form. Render the form via CL-WHO backend, doing:

@lisp
(forms:with-form-renderer :who
   (forms:render-form form))
@end lisp

Then handle and validate the form.

Source code:

@lisp
@include ../test/demo/fields.lisp
@end lisp

@subsection Validation

Example of forms validation.

Add Clavier constraints to the form. Then call @clref{CL-FORMS, VALIDATE-FORM, function} after @clref{CL-FORMS, HANDLE-REQUEST, function}.

@lisp
@include ../test/demo/validation.lisp
@end lisp

@subsection Client validation

To validate in the client, just set @code{:client-validation} to @code{T}.

@lisp
@include ../test/demo/client-validation.lisp
@end lisp

@subsection Models

Forms can be attached to model objects. Model objects are CLOS instances from where form values are read and written to.

To work with models, forms are defined via defform-builder instead of defform. A form-builder is a function that takes the model objects and attaches it to the form. The form needs to define the accessors to access the model for each form field.

@lisp
@include ../test/demo/models.lisp
@end lisp

@subsection Composition

It is possible to compose forms using the @code{subform} field type:

@lisp
@include ../test/demo/composition.lisp
@end lisp

@subsection Form templates

Form templates is an alternative way of defining and rendering forms. Instead of defining a form with defform and then specifiying a template and render it, forms templates allow to do all that at the same time.

@lisp
@verbatiminclude ../test/demo/form-templates.lisp
@end lisp

@subsection Renderers

@lisp
@include ../test/demo/renderers.lisp
@end lisp

@node Form rendering
@section Form rendering

A form can be rendered via different renderers and themes.
There are implemented renderers for CL-WHO and Djula.
The only theme at the moment is a Bootstrap theme that runs under CL-WHO.

To be able to render a form a form renderer needs to be bound first. Renderers are bound using @clref{CL-FORMS,WITH-FORM-RENDERER,macro} macro.

Similarly, to use a theme other than the default one, it needs to be bound using @clref{CL-FORMS,WITH-FORM-THEME,macro}.

@subsection Form rendering functions

Forms are renderer using @clref{CL-FORMS,RENDER-FORM,function} to render the whole form all at once, or via @clref{CL-FORMS,RENDER-FORM-START,function},@clref{CL-FORMS,RENDER-FORM-END,function},@clref{CL-FORMS,RENDER-FIELD,function},@clref{CL-FORMS,RENDER-FIELD-LABEL,function},@clref{CL-FORMS,RENDER-FIELD-WIDGET,function}, to only render specific parts of a form and have more control.

@node CL-WHO renderer
@subsection CL-WHO renderer

The CL-WHO renderer uses CL-WHO library for rendering forms.

Needs @code{cl-forms.who} ASDF system loaded.

To render a form using CL-WHO bind the renderer via @clref{CL-FORMS,WITH-FORM-RENDERER, macro}, bind @code{FORMS.WHO:*HTML*} variable, and then render the form:

@lisp
(let ((form (forms::find-form 'fields-form)))
   (who:with-html-output (forms.who:*html*)
      (forms:with-form-renderer :who
         (forms:render-form form))))
@end lisp

@node Bootstrap theme
@subsection Bootstrap theme

There's a Bootstrap theme implemented for CL-WHO renderer.

Needs @code{cl-forms.who.bootstrap} ASDF system loaded.

Select the theme via @clref{CL-FORMS,WITH-FORM-THEME,macro}:

@lisp
(let ((form (forms::find-form 'bs-fields-form)))
    (forms:with-form-theme 'forms.who::bootstrap-form-theme
      (forms:with-form-renderer :who
        (who:with-html-output (forms.who::*html*)
           (forms:render-form form)))))
@end lisp

@node Djula
@subsection Djula

CL-FORMS integrates with Djula template system.

Needs @code{cl-forms.djula} ASDF system loaded.

Djula tags:
@itemize
@item @code{@{% form form %@}}. Renders a whole form.
@item @code{@{% form-start form %@}}. Renders the form start part.
@item @code{@{% form-end form %@}}. Renders the form end part.
@item @code{@{% form-row form field-name %@}}. Renders the row with label and widget for the form field.
@item @code{@{% form-field-label form field-name %@}}. Renders the form field label.
@item @code{@{% form-field-widget form field-name %@}}. Renders the form field widget.
@end itemize

Make sure to @code{@{% set-package %@}} at the beggining of your Djula template to the package where the form lives. Otherwise, Djula wont' be able to find form fields by name.

Examples:

A Djula template that renders a whole form:

@verbatim
{% form form %}
@end verbatim

A Djula template that renders a form by parts:

@verbatim
{% set-package forms.test %}

{% form-start form %}
{% form-row form name %}
{% form-row form ready %}
<div>
  {% form-field-label form sex %}
  {% form-field-widget form sex %}
</div>
{% form-row form avatar %}
{% form-row form disabled %}
{% form-row form disabled-checkbox %}
{% form-row form readonly-checkbox %}
{% form-row form submit %}
{% form-end form %}
@end verbatim

@node API
@chapter API
@anchor{api}

@menu
* CL-FORMS package::
@end menu

@node CL-FORMS package
@section CL-FORMS package
(@clpackage :cl-forms)

@node Index
@chapter Index

@printindex cp
@printindex vr
@printindex fn

@bye