\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 %}
{% form-field-label form sex %} {% form-field-widget form sex %}
{% 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