| \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 |
| |