CL-FORMS

Table of Contents

Next:   [Contents][Index]

Top


Next: , Previous: , Up: Top   [Contents][Index]

1 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:


Next: , Previous: , Up: Top   [Contents][Index]

2 Installation

With Quicklisp:

  (ql:quickload "cl-forms")

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


Next: , Previous: , Up: Top   [Contents][Index]

3 Usage

3.1 Basics

Use DEFFORM to define a form. Example:

  (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")))

On your web handler, grab the form via FIND-FORM, select a renderer with ‘with-form-renderer‘and then render the form with RENDER-FORM:

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

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

  (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))))))

To output HTML on the REPL, do:

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

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

3.2 Demo

There’s a demo included. To run:

  (require :cl-forms.demo)
  (forms.test:run-demo)

3.2.1 Basic example

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

  (forms:with-form-renderer :who
     (forms:render-form form))

Then handle and validate the form.

Source code:

  (in-package :forms.test)
   
  (forms:defform fields-form (:action "/fields-post"
                                      :enctype "multipart/form-data")
    ((name :string :value "")
     (ready :boolean :value t)
     (sex :choice :choices (list "Male" "Female") :value "Male")
     (avatar :file :upload-handler 'handle-file-upload)
     (disabled :string :disabled-p t :required-p nil)
     (readonly :string :read-only-p t :required-p nil)
     (readonly-checkbox :boolean :read-only-p t :required-p nil)
     (disabled-checkbox :boolean :disabled-p t :required-p nil)
     (submit :submit :label "Create")))
   
  (defun fields-demo ()
    (who:with-html-output (forms.who:*html*)
      (:h1 (who:str "Fields example"))
      (:div :class :container
            (:div :class :row
                  (:div :class :heading
                        (:h3 (who:str "Simple form")))
                  (let ((form (forms::find-form 'fields-form)))
                    (forms:with-form-renderer :who
                      (forms:render-form form))))
            (:div :class :row
                  (:div :class :heading
                        (:h3 (who:str "Choices")))
                  (let ((form (forms::find-form 'choices-form)))
                    (forms:with-form-renderer :who
                      (forms:render-form form)))))))
   
  (hunchentoot:define-easy-handler (fields-demo-handler :uri "/fields") ()
    (render-demo-page :demo #'fields-demo
                      :source (asdf:system-relative-pathname :cl-forms.demo
                                                             "test/demo/fields.lisp")
                      :active-menu :fields))
   
  (hunchentoot:define-easy-handler (fields-form-post
                                    :uri "/fields-post"
                                    :default-request-type :post)
      ()
    (flet ((fields-post ()
             (let ((form (forms:find-form 'fields-form)))
               (forms::handle-request form)
               (forms::with-form-fields (name ready sex avatar) form
                 (who:with-html-output (forms.who:*html*)
                   (:ul
                    (:li (who:fmt "Name: ~A" (forms::field-value name)))
                    (:li (who:fmt "Ready: ~A" (forms::field-value ready)))
                    (:li (who:fmt "Sex: ~A" (forms::field-value sex)))
                    (:li (who:fmt "Avatar: ~A" (forms::file-name avatar))
                         (when (forms::file-name avatar)
                           (who:htm
                            (:img :width 200 :height 200
                                  :src (format nil "/files?f=~A" (forms::file-name avatar))))))))))))
      (render-demo-page :demo #'fields-post
                        :source (asdf:system-relative-pathname :cl-forms.demo
                                                               "test/demo/fields.lisp")
                        :active-menu :fields)))
   
  ;; Choices widget test
   
  (forms:defform choices-form (:action "/choices-post")
    ((sex :choice
          :choices (list "Male" "Female")
          :value "Male")
     (sex2 :choice
           :choices (list "Male" "Female")
           :value "Female"
           :expanded t)
     (choices :choice
              :choices (list "Foo" "Bar")
              :value (list "Foo")
              :multiple t)
     (choices2 :choice
               :choices (list "Foo" "Bar")
               :value (list "Bar")
               :multiple t
               :expanded  t)
     (submit :submit :label "Ok")))
   
  (hunchentoot:define-easy-handler (choices-form-post :uri "/choices-post"
                                                      :default-request-type :post) ()
    (flet ((choices-post ()
             (let ((form (forms:find-form 'choices-form)))
               (forms::handle-request form)
               (forms::validate-form form)
               (forms::with-form-field-values (sex sex2 choices choices2) form
                 (who:with-html-output (forms.who:*html*)
                   (:ul
                    (:li (who:fmt "Sex: ~A" sex))
                    (:li (who:fmt "Sex2: ~A" sex2))
                    (:li (who:fmt "Choices: ~A" choices))
                    (:li (who:fmt "Choices2: ~A" choices2))))))))
      (render-demo-page :demo #'choices-post
                        :source (asdf:system-relative-pathname :cl-forms.demo
                                                               "test/demo/fields.lisp")
                        :active-menu :fields)))
   
  ;; File handling
   
  (defvar *files* nil)
  (defvar *files-path* (pathname "/tmp/cl-forms/"))
   
  (defun handle-file-upload (file-field)
    ;; Store the file
    (let ((new-path (merge-pathnames 
                         (forms::file-name file-field)
                         *files-path*)))
      (rename-file (forms::file-path file-field)
                   (ensure-directories-exist new-path))
      ;; Save for handler
      (push (cons (forms::file-name file-field)
                  (list new-path (forms::file-content-type file-field)))
            *files*)))
   
  (defun handle-uploaded-file ()
    (let ((finfo (cdr (assoc (hunchentoot:parameter "f") *files* :test #'equalp))))
      (hunchentoot:handle-static-file (first finfo) (second finfo))))
   
  (push 
   (hunchentoot:create-prefix-dispatcher "/files" 'handle-uploaded-file)
   hunchentoot:*dispatch-table*)

3.2.2 Validation

Example of forms validation.

Add Clavier constraints to the form. Then call VALIDATE-FORM after HANDLE-REQUEST.

  (in-package :forms.test)
   
  (forms:defform validated-form (:action "/validation-post"
                                         :client-validation nil)
    ((name :string :value "" :constraints (list (clavier:is-a-string)
                                                (clavier:not-blank)
                                                (clavier:len :max 5)))
     (single :boolean :value t)
     (sex :choice :choices (list "Male" "Female") :value "Male")
     (age :integer :constraints (list (clavier:is-an-integer)
                                      (clavier:greater-than -1)
                                      (clavier:less-than 200)))
     (email :email)
     (birth-date :date :required-p nil)
     (submit :submit :label "Create")))
   
  (defun validation-demo (&optional form)
    (forms:with-form-renderer :who
      (who:with-html-output (forms.who::*html*)
        (:h1 (who:str "Server side validation"))
        (:p (who:str "This is a demo of server side validation. Submit the form and play with the values to see how it works. Also look at field constraints in source code tab."))
        (let ((form (or form (forms::find-form 'validated-form))))
          (forms:render-form form)))))
   
  (hunchentoot:define-easy-handler (validated-form-post :uri "/validation-post"
                                                        :default-request-type :post) ()
   
    (flet ((validation-post ()
             (let ((form (forms:find-form 'validated-form)))
               (forms::handle-request form)
               (if (forms::validate-form form)
                   ;; The form is valid
                   (forms::with-form-field-values (name single sex age email birth-date) form
                     (who:with-html-output (forms.who::*html*)
                       (:ul
                        (:li (who:fmt "Name: ~A" name))
                        (:li (who:fmt "Single: ~A" single))
                        (:li (who:fmt "Sex: ~A" sex))
                        (:li (who:fmt "Age: ~A" age))
                        (:li (who:fmt "Email: ~A" email))
                        (:li (who:fmt "Birth date: ~A" birth-date)))))
                   ;; The form is not valid
                   (validation-demo form)))))
      (render-demo-page :demo #'validation-post
                        :source (asdf:system-relative-pathname :cl-forms.demo
                                                               "test/demo/validation.lisp")
                        :active-menu :validation)))
   
  (hunchentoot:define-easy-handler (validation-demo-handler :uri "/validation") ()
    (render-demo-page :demo #'validation-demo
                      :source (asdf:system-relative-pathname :cl-forms.demo
                                                             "test/demo/validation.lisp")
                      :active-menu :validation))

3.2.3 Client validation

To validate in the client, just set :client-validation to T.

  (in-package :forms.test)
   
  (forms:defform client-validated-form (:action "/client-validation-post"
                                                :client-validation t)
    ((name :string :value "" :constraints (list (clavier:is-a-string)
                                                (clavier:not-blank)
                                                (clavier:len :max 5))
           :validation-triggers '(:focusin))
     (single :boolean :value t)
     (sex :choice :choices (list "Male" "Female") :value "Male")
     (age :integer :constraints (list (clavier:is-an-integer)
                                      (clavier:greater-than -1)
                                      (clavier:less-than 200)))
     (email :email)
     (submit :submit :label "Create")))
   
  (defun client-validation (&optional form)
    (let ((form (or form (forms::find-form 'client-validated-form))))
      (forms:with-form-renderer :who
        (who:with-html-output (forms.who::*html*)
          (:h1 (who:str "Client side validation"))
          (:p (who:str "This is an example of how client side validation works. Client side validation uses parsleyjs library for validating client side."))
          (:p (who:str "The interesting thing about the implementation is that validations are specified in the form definition, and are \"compiled\" to rules in javascript. Also, this example uses the exactly the same constraints than the server side validation demo."))      
          (forms:render-form form)))))
   
  (hunchentoot:define-easy-handler (client-validation-handler
                                    :uri "/client-validation") ()
    (render-demo-page :demo #'client-validation
                      :source (asdf:system-relative-pathname :cl-forms.demo
                                                             "test/demo/client-validation.lisp")
                      :active-menu :client-validation))
   
  (hunchentoot:define-easy-handler (client-validation-post :uri "/client-validation/post" :default-request-type :post) ()
    (flet ((client-validation-post ()
             (let ((form (forms:find-form 'client-validated-form)))
               (forms::handle-request form)
               (if (forms::validate-form form)
                   ;; The form is valid
                   (forms::with-form-field-values (name single sex age email) form
                     (who:with-html-output (forms.who::*html*)
                       (:ul
                        (:li (who:fmt "Name: ~A" name))
                        (:li (who:fmt "Single: ~A" single))
                        (:li (who:fmt "Sex: ~A" sex))
                        (:li (who:fmt "Age: ~A" age))
                        (:li (who:fmt "Email: ~A" email)))))
                   ;; The form is not valid
                   (client-validation form)))))
      (render-demo-page :demo #'client-validation-post
                        :source (asdf:system-relative-pathname :cl-forms.demo
                                                               "test/demo/client-validation.lisp")
                        :active-menu :client-validation)))

3.2.4 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.

  (in-package :forms.test)
   
  (defclass person ()
    ((name :initarg :name
           :accessor person-name
           :initform nil)
     (single :initarg :single
             :accessor person-single
             :initform t)
     (sex :initarg :sex
          :accessor person-sex
          :initform :male)))
   
  (forms:defform-builder model-form (person)
    (make-instance 'forms::form
                   :name 'model-form
                   :model person
                   :action "/models-post"
                   :fields (forms::make-form-fields
                            `((name :string :label "Name"
                                            :accessor person-name)
                              (single :boolean :label "Single"
                                               :accessor person-single)
                              (sex :choice :label "Sex"
                                           :choices (:male :female)
                                           :accessor person-sex
                                           :formatter format-sex)
                              (submit :submit :label "Update")))))
   
  (defun format-sex (sex stream)
    (write-string
     (if (equalp sex :male) "Male" "Female")
     stream))
   
  (defun models-demo ()
    (who:with-html-output (forms.who::*html*)
      (:h1 (who:str "Form models"))
      (:p "Forms can be attached to model objects. Model objects are CLOS instances from where form values are read and written to.")
      (:p "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.")
      (:p "This is an example of a form attached to a person object. Please have a look at the source code to see how it is done.")
      (render-model-form)))
   
  (defun render-model-form (&optional form)
    (let ((form (or form
                    (let ((person (make-instance 'person
                                                 :name "Foo"
                                                 :single t
                                                 :sex :male)))
                      (forms::find-form 'model-form person)))))
      (forms:with-form-renderer :who
        (forms:render-form form))))
   
  (hunchentoot:define-easy-handler (model-form :uri "/models") ()
    (render-demo-page :demo #'models-demo
                      :source (asdf:system-relative-pathname :cl-forms.demo
                                                             "test/demo/models.lisp")
                      :active-menu :models))
   
  (hunchentoot:define-easy-handler (model-form-post :uri "/models-post"
                                                    :default-request-type :post) ()
    (flet ((model-post ()
             (let ((person (make-instance 'person)))
               (let ((form (forms:find-form 'model-form person)))
                 (forms::handle-request form)
                 (forms::validate-form form)
                 (who:with-html-output (forms.who::*html*)
                   (:ul
                    (:li (who:fmt "Name: ~A" (person-name person)))
                    (:li (who:fmt "Single: ~A" (person-single person)))
                    (:li (who:fmt "Sex: ~A" (person-sex person)))))))))
      (render-demo-page :demo #'model-post
                        :source (asdf:system-relative-pathname :cl-forms.demo
                                                               "test/demo/models.lisp")
                        :active-menu :models)))

3.2.5 Composition

It is possible to compose forms using the subform field type:

  (in-package :forms.test)
   
  (forms:defform member-form ()
    ((name :string :value "" :required-p nil)
     (ready :boolean :value t :required-p nil)
     (sex :choice :choices (list "Male" "Female") :value "Male")))
   
  (forms:defform composition-form (:action "/composition-post")
    (
     ;; Subforms
     (main-member :subform :subform 'member-form)
     (secondary-member :subform :subform 'member-form)
        ;; Simple list
     (todo :list :type '(:string :required-p nil)
           :empty-item-predicate (lambda (field)
                                   (let ((val (forms:field-value field)))
                                     (or (null val)
                                         (string= val "")))))
    ;; Subform list
     (members :list :type '(:subform :subform member-form)
              :empty-item-predicate (lambda (field)
                                      (let* ((subform (forms:field-value field))
                                             (val (forms:get-field-value subform 'name)))
                                        (or (null val)
                                            (string= val "")))))
     (save :submit :label "Save")))
   
  (defun form-composition-demo (&optional form)
    (let ((form (or form (find-form 'composition-form))))
      (forms:with-form-renderer :who
        (who:with-html-output (forms.who::*html*)
          (:h1 (who:str "Forms composition"))
          (:p (who:str "These are examples of subforms and the list field type"))
          (forms::render-form-start form)
          (:h2 (who:str "Subforms"))
          (:p (who:str "This is an example of subform composition. main-member and secondary-member are subforms."))
          (forms::render-field 'main-member form)
          (forms::render-field 'secondary-member form)
          (forms::render-field 'save form)
          (:h2 (who:str "List field"))
          (:p (who:str "This is an example of the list field. In this case, the list has elements of type string. To add an element to the list, fill in the input. To remove, empty the input."))
          (forms::render-field 'todo form)
          (forms::render-field 'save form)
          (:h2 (who:str "List of subforms"))
          (:p (who:str "This is the most complex example. This shows a list of subforms. Subforms are of type person. To add new persons, fill in their name. To remove, empty the name field."))
          (forms::render-field 'members form)
          (forms::render-field 'save form)
          (forms::render-form-end form)))))
   
  (hunchentoot:define-easy-handler (composition-demo :uri "/composition") ()
    (render-demo-page :demo #'form-composition-demo
                      :source (asdf:system-relative-pathname :cl-forms.demo 
                                                             "test/demo/composition.lisp")
                      :active-menu :composition))
   
  (hunchentoot:define-easy-handler (composition-demo-post :uri "/composition-post") ()
    (let ((form (forms:find-form 'composition-form)))
      (forms:handle-request form)
      (render-demo-page :demo (lambda ()
                                (form-composition-demo form))
                        :source (asdf:system-relative-pathname :cl-forms.demo 
                                                               "test/demo/composition.lisp")
                        :active-menu :composition)))

3.2.6 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.

(in-package :forms.test)

(defun form-template-demo ()
  (macrolet ((row (&body body)
               `(who:htm
                 (:div :class "row"
                       (who:htm
                        ,@body))))
             (col (&body body)
               `(who:htm
                 (:div :class "col-md-2"
                       (who:htm
                        ,@body)))))
    (forms:with-form-renderer :who
      (who:with-html-output (forms.who::*html*)
        (:div :class :container
              (:div :class :row
                    (:div :class :heading
                          (:h1 (who:str "Form templates")))
                    (:p (who:str "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."))
                    (:p (who:str "Form definition is embedded in rendering spec via with-form-template. See source for details."))                
                    (:div :class :container
                          (forms:with-form-template () template-form (:action "/template-post")
                            (row
                             (:h3 (who:str "General"))
                             (col (form-field firstname :string :value ""))
                             (col (form-field lastname :string :value "")))
                            (form-field active :boolean :value t)
                            (row
                             (:h3 (who:str "Address"))
                             (form-field address :string :value ""))
                            (row
                             (:h3 (who:str "Other"))
                             (form-field choices :choice
                                         :choices (list "Foo" "Bar")
                                         :value (list "Foo")
                                         :multiple t)
                             (form-field choices2 :choice
                                         :choices (list "Foo" "Bar")
                                         :value (list "Bar")
                                         :multiple t
                                         :expanded  t))
                            (row
                             (form-field submit :submit :label "Create"))))))))))

(hunchentoot:define-easy-handler (template-demo-handler :uri "/template") ()
  (render-demo-page :demo #'form-template-demo
                    :source (asdf:system-relative-pathname :cl-forms.demo
                                                           "test/demo/form-templates.lisp")
                    :active-menu :template))

(hunchentoot:define-easy-handler (template-form-post
                                  :uri "/template-post"
                                  :default-request-type :post) ()
  (flet ((fields-post ()
           (let ((form (forms:find-form 'template-form)))
             (forms::handle-request form)
             (if (forms::validate-form form)
                 (forms::with-form-field-values (firstname lastname active address
                                                           choices choices2) form
                   (who:with-html-output (forms.who::*html*)
                     (:ul
                      (:li (who:fmt "Firstname: ~A" firstname))
                      (:li (who:fmt "Lastname: ~A" lastname))
                      (:li (who:fmt "Active: ~A" active))
                      (:li (who:fmt "Address: ~A" address))
                      (:li (who:fmt "Choices: ~A" choices))
                      (:li (who:fmt "Choices2: ~A" choices2)))))
                 "Form is not valid"))))
    (render-demo-page :demo #'fields-post
                      :source (asdf:system-relative-pathname :cl-forms.demo
                                                             "test/demo/form-templates.lisp")
                      :active-menu :template)))

3.2.7 Renderers

  (in-package :forms.test)
   
  (hunchentoot:define-easy-handler (demo-renderers :uri "/renderers") ()
    (flet ((render ()
             (forms:with-form-renderer :who
  	     (who:with-html-output (forms.who:*html*)
  	       (:h2 "CL-WHO")
  	       (:p (who:str "Render via CL-WHO and whole form with RENDER-FORM."))
  	       (forms:render-form (forms:find-form 'fields-form))
  	       (:h2 "CL-WHO render by part")
  	       (:p (who:str "Render via CL-WHO and the individual rendering functions RENDER-FORM-START, RENDER-FORM-END, RENDER-FIELD, RENDER-FIELD-LABEL and RENDER-FIELD-WIDGET."))
  	       (forms:with-form (forms:find-form 'fields-form)
  		 (forms:render-form-start)
  		 (forms:render-field 'name)
  		 (forms:render-field-label 'ready)
  		 (forms:render-field-widget 'ready)
  		 (forms:render-field 'sex)
  		 (forms:render-field 'avatar)
  		 (forms:render-field 'disabled)
  		 (forms:render-field 'readonly)
  		 (forms:render-field 'readonly-checkbox)
  		 (forms:render-field 'disabled-checkbox)
  		 (forms:render-field 'submit)
  		 (forms:render-form-end))
  	       (:h2 "Djula")
  	       (:p (who:str "Render a form with a Djula template."))
  	       (who:str (djula:render-template* (asdf:system-relative-pathname :cl-forms.demo "test/demo/djula-form.html") nil :form (find-form 'fields-form)))
  	       (:h2 "Djula by part")
  	       (:p (who:str "Render a form with a Djula template, by parts."))
  	       (who:str (djula:render-template* (asdf:system-relative-pathname :cl-forms.demo "test/demo/djula-form-parts.html") nil :form (find-form 'fields-form)))
                 ))))
      (render-demo-page :demo #'render
                        :source (asdf:system-relative-pathname :cl-forms.demo
                                                               "test/demo/renderers.lisp")
                        :active-menu :renderers)))
   
   

Up: Usage   [Contents][Index]

3.3 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 WITH-FORM-RENDERER macro.

Similarly, to use a theme other than the default one, it needs to be bound using WITH-FORM-THEME.

3.3.1 Form rendering functions

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


Next: , Up: Form rendering   [Contents][Index]

3.3.2 CL-WHO renderer

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

Needs cl-forms.who ASDF system loaded.

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

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

Next: , Previous: , Up: Form rendering   [Contents][Index]

3.3.3 Bootstrap theme

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

Needs cl-forms.who.bootstrap ASDF system loaded.

Select the theme via WITH-FORM-THEME:

  (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)))))

Previous: , Up: Form rendering   [Contents][Index]

3.3.4 Djula

CL-FORMS integrates with Djula template system.

Needs cl-forms.djula ASDF system loaded.

Djula tags:

Make sure to {% 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:

{% form form %}

A Djula template that renders a form by parts:

{% 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 %}

Next: , Previous: , Up: Top   [Contents][Index]

4 API


Up: API   [Contents][Index]

4.1 CL-FORMS package

PACKAGE: CL-FORMS

External definitions

Variables

CL-FORMS: *BASE64-ENCODE*

Whether to encode form parameters in base64 or not.

Macros

Macro: CL-FORMS:DEFFORM-BUILDER (form-name args &body body)

Registers a function with arguments ARGS and body BODY as a form builder.

BODY is expected to instantiate a FORM object using ARGS in some way.

FORM-NAME is the symbol under which the FORM is registered.

Use FIND-FORM with FORM-NAME and expected arguments to obtain the registered form.

Macro: CL-FORMS:WITH-FORM-FIELD-VALUES (fields form &body body)

Bind the value of FIELDS in FORM.

Example:

  (with-form-field-values (name) form
     (print name))
Macro: CL-FORMS:WITH-FORM-FIELDS (fields form &body body)

Bind FIELDS to the form fields in FORM.

Example:

  (with-form-fields (name) form
     (print (field-value name)))

Also see: WITH-FORM-FIELD-VALUES

Macro: CL-FORMS:DEFFORM (form-name args fields)

Define a form at top-level.

ARGS are the arguments passed to FORM class via MAKE-INSTANCE. FIELDS are the form field specs.

  (forms:defform client-validated-form (:action "/client-validation-post"
                                                :client-validation t)
    ((name :string :value "" :constraints (list (clavier:is-a-string)
                                                (clavier:not-blank)
                                                (clavier:len :max 5))
           :validation-triggers '(:focusin))
     (single :boolean :value t)
     (sex :choice :choices (list "Male" "Female") :value "Male")
     (age :integer :constraints (list (clavier:is-an-integer)
                                      (clavier:greater-than -1)
                                      (clavier:less-than 200)))
     (email :email)
     (submit :submit :label "Create")))
Macro: CL-FORMS:WITH-FORM-TEMPLATE ((&optional form-var) form-name args &body body)

Define a FORM named FORM-NAME and render it at the same time.

Macro: CL-FORMS:WITH-FORM-THEME (form-theme &body body)

Bind *FORM-THEME* to FORM-THEME and evaluate BODY in that context.

Macro: CL-FORMS:WITH-FORM (form &body body)

Bind *FORM* to FORM and evaluate BODY in that context.

Macro: CL-FORMS:WITH-FORM-RENDERER (renderer &body body)

Bind *FORM-RENDERER* to RENDERER and evaluate BODY in that context.

Generic functions

Generic-Function: CL-FORMS:FIELD-READER (field)
Generic-Function: CL-FORMS:FIELD-VALID-P (form-field &optional (form))
Generic-Function: CL-FORMS:FORM-FIELDS (sb-pcl::object)
Generic-Function: CL-FORMS:FIELD-ACCESSOR (sb-pcl::object)
Generic-Function: CL-FORMS:FORMAT-FIELD-VALUE (form-field field-value &optional stream)
Generic-Function: CL-FORMS:FIELD-WRITER (field)
Generic-Function: CL-FORMS:FIELD-PARSER (sb-pcl::object)
Generic-Function: CL-FORMS:FORM-ERRORS (sb-pcl::object)
Generic-Function: CL-FORMS:FIELD-VALUE (field)
Generic-Function: CL-FORMS:FIELD-LABEL (sb-pcl::object)
Generic-Function: CL-FORMS:FIELD-FORMATTER (sb-pcl::object)

Functions

Function: CL-FORMS:ADD-FIELD (form field)
Function: CL-FORMS:RENDER-FORM-END (&optional (form *form*))

Render the end of the web form FORM.

Function: CL-FORMS:GET-FORM (&rest args)
Function: CL-FORMS:SET-FIELD-VALUE (form field-name value)
Function: CL-FORMS:HANDLE-REQUEST (&optional (form *form*) (request hunchentoot:*request*))

Populates FORM from parameters in HTTP request. After this, the form field contains values, but they are not validated. To validate call VALIDATE-FORM after.

Function: CL-FORMS:VALIDATE-FORM (&optional (form *form*))

Validates a form. Usually called after HANDLE-REQUEST. Returns multiple values; first value is true if the form is valid; second value a list of errors. The list of errors is an association list with elements (<field> . <field errors strings list>).

Function: CL-FORMS:MAKE-FORMATTER (symbol)

Create a field formatter. SYMBOL is the function to call.

Function: CL-FORMS:FORMAT-FIELD-VALUE-TO-STRING (form-field &optional (field-value (field-value form-field)))
Function: CL-FORMS:RENDER-FIELD (field &optional (form *form*) &rest args)

Render form FIELD, both label and widget.

Function: CL-FORMS:FIND-FORM (name &rest args)

Get the form named NAME.

ARGS is the list of arguments to pass to a possible form builder function.

See: DEFFORM-BUILDER macro.

Function: CL-FORMS:RENDER-FORM (&optional (form *form*) &rest args)

Top level function to render the web form FORM. *FORM-RENDERER* and *FORM-THEME* need to be bound. See: WITH-FORM-RENDERER, WITH-FORM-THEME

Function: CL-FORMS:FILL-FORM-FROM-MODEL (form model)

Fill a FORM from a MODEL. Read MODEL using FORM accessors and set the FORM field values.

Function: CL-FORMS:RENDER-FORM-START (&optional (form *form*) &rest args)

Render only the beggining of the web form FORM. Use RENDER-FIELD, RENDER-FIELD-LABEL, etc manually, after.

Function: CL-FORMS:FORM-VALID-P (form)
Function: CL-FORMS:RENDER-FIELD-WIDGET (field &optional (form *form*) &rest args)

Render FIELD widget.

Function: CL-FORMS:ADD-FORM-ERROR (field error-msg &optional (form *form*))

Add an error on FIELD

Function: CL-FORMS:FILL-MODEL-FROM-FORM (form model)

Set a MODEL’s values from FORM field values.

Function: CL-FORMS:GET-FIELD-VALUE (form field-name &optional (error-p t))
Function: CL-FORMS:GET-FIELD (form field-name &optional (error-p t))
Function: CL-FORMS:RENDER-FORM-ERRORS (&optional (form *form*) &rest args)

Render a section for displaying form validation errors.

Function: CL-FORMS:RENDER-FIELD-ERRORS (field &optional (form *form*) &rest args)

Render the validation errors associated with FIELD.

Function: CL-FORMS:RENDER-FIELD-LABEL (field &optional (form *form*) &rest args)

Render the label of FIELD.

Function: CL-FORMS:REMOVE-FIELD (form field)

Classes

Class: CL-FORMS:PASSWORD-FORM-FIELD

A password input field

Class precedence list: password-form-field, form-field, standard-object, t

Class: CL-FORMS:BOOLEAN-FORM-FIELD

A boolean input

Class precedence list: boolean-form-field, form-field, standard-object, t

Class: CL-FORMS:STRING-FORM-FIELD

A string input field

Class precedence list: string-form-field, form-field, standard-object, t

Class: CL-FORMS:DATE-FORM-FIELD

A date input field

Class precedence list: date-form-field, form-field, standard-object, t

Class: CL-FORMS:DATETIME-FORM-FIELD

A date input field

Class precedence list: datetime-form-field, form-field, standard-object, t

Class: CL-FORMS:FORM

A form

Class precedence list: form, standard-object, t

Slots:

Class: CL-FORMS:FORM-FIELD

A form field

Class precedence list: form-field, standard-object, t

Slots:

Class: CL-FORMS:TEXT-FORM-FIELD

A text field. Renders as a text area

Class precedence list: text-form-field, string-form-field, form-field, standard-object, t

Class: CL-FORMS:LIST-FORM-FIELD

A field that contains a list of elements (either other fields or subforms)

Class precedence list: list-form-field, form-field, standard-object, t

Slots:

Class: CL-FORMS:SUBFORM-FORM-FIELD

A field that contains a form (subform)

Class precedence list: subform-form-field, form-field, standard-object, t

Class: CL-FORMS:HIDDEN-FORM-FIELD

A hidden form field

Class precedence list: hidden-form-field, form-field, standard-object, t

Class: CL-FORMS:INTEGER-FORM-FIELD

An integer input field

Class precedence list: integer-form-field, form-field, standard-object, t

Class: CL-FORMS:EMAIL-FORM-FIELD

A string input field

Class precedence list: email-form-field, form-field, standard-object, t

Class: CL-FORMS:CHOICE-FORM-FIELD

A multi-purpose field used to allow the user to "choose" one or more options. It can be rendered as a select tag, radio buttons, or checkboxes. NOTE: the defaults of this field type are too complicated for just working with string choices. STRING-CHOICE-FIELD is more convenient for that.

Class precedence list: choice-form-field, form-field, standard-object, t

Slots:

Class: CL-FORMS:SUBMIT-FORM-FIELD

A submit input button

Class precedence list: submit-form-field, form-field, standard-object, t

Class: CL-FORMS:URL-FORM-FIELD

An url input field

Class precedence list: url-form-field, form-field, standard-object, t

Class: CL-FORMS:FILE-FORM-FIELD

A file input field

Class precedence list: file-form-field, form-field, standard-object, t

Slots:


Previous: , Up: Top   [Contents][Index]

5 Index

Jump to:   C  
Index Entry  Section

C
cl-forms: Top

Jump to:   C  
Jump to:   *  
C  
Index Entry  Section

*
*BASE64-ENCODE*: CL-FORMS package

C
CL-FORMS:*BASE64-ENCODE*: CL-FORMS package

Jump to:   *  
C  
Jump to:   C  
Index Entry  Section

C
CL-FORMS:ADD-FIELD: CL-FORMS package
CL-FORMS:ADD-FIELD: CL-FORMS package
CL-FORMS:ADD-FORM-ERROR: CL-FORMS package
CL-FORMS:ADD-FORM-ERROR: CL-FORMS package
CL-FORMS:DEFFORM: CL-FORMS package
CL-FORMS:DEFFORM: CL-FORMS package
CL-FORMS:DEFFORM-BUILDER: CL-FORMS package
CL-FORMS:DEFFORM-BUILDER: CL-FORMS package
CL-FORMS:FIELD-ACCESSOR: CL-FORMS package
CL-FORMS:FIELD-ACCESSOR: CL-FORMS package
CL-FORMS:FIELD-FORMATTER: CL-FORMS package
CL-FORMS:FIELD-FORMATTER: CL-FORMS package
CL-FORMS:FIELD-LABEL: CL-FORMS package
CL-FORMS:FIELD-LABEL: CL-FORMS package
CL-FORMS:FIELD-PARSER: CL-FORMS package
CL-FORMS:FIELD-PARSER: CL-FORMS package
CL-FORMS:FIELD-READER: CL-FORMS package
CL-FORMS:FIELD-READER: CL-FORMS package
CL-FORMS:FIELD-VALID-P: CL-FORMS package
CL-FORMS:FIELD-VALID-P: CL-FORMS package
CL-FORMS:FIELD-VALUE: CL-FORMS package
CL-FORMS:FIELD-VALUE: CL-FORMS package
CL-FORMS:FIELD-WRITER: CL-FORMS package
CL-FORMS:FIELD-WRITER: CL-FORMS package
CL-FORMS:FILL-FORM-FROM-MODEL: CL-FORMS package
CL-FORMS:FILL-FORM-FROM-MODEL: CL-FORMS package
CL-FORMS:FILL-MODEL-FROM-FORM: CL-FORMS package
CL-FORMS:FILL-MODEL-FROM-FORM: CL-FORMS package
CL-FORMS:FIND-FORM: CL-FORMS package
CL-FORMS:FIND-FORM: CL-FORMS package
CL-FORMS:FORM-ERRORS: CL-FORMS package
CL-FORMS:FORM-ERRORS: CL-FORMS package
CL-FORMS:FORM-FIELDS: CL-FORMS package
CL-FORMS:FORM-FIELDS: CL-FORMS package
CL-FORMS:FORM-VALID-P: CL-FORMS package
CL-FORMS:FORM-VALID-P: CL-FORMS package
CL-FORMS:FORMAT-FIELD-VALUE: CL-FORMS package
CL-FORMS:FORMAT-FIELD-VALUE: CL-FORMS package
CL-FORMS:FORMAT-FIELD-VALUE-TO-STRING: CL-FORMS package
CL-FORMS:FORMAT-FIELD-VALUE-TO-STRING: CL-FORMS package
CL-FORMS:GET-FIELD: CL-FORMS package
CL-FORMS:GET-FIELD: CL-FORMS package
CL-FORMS:GET-FIELD-VALUE: CL-FORMS package
CL-FORMS:GET-FIELD-VALUE: CL-FORMS package
CL-FORMS:GET-FORM: CL-FORMS package
CL-FORMS:GET-FORM: CL-FORMS package
CL-FORMS:HANDLE-REQUEST: CL-FORMS package
CL-FORMS:HANDLE-REQUEST: CL-FORMS package
CL-FORMS:MAKE-FORMATTER: CL-FORMS package
CL-FORMS:MAKE-FORMATTER: CL-FORMS package
CL-FORMS:REMOVE-FIELD: CL-FORMS package
CL-FORMS:REMOVE-FIELD: CL-FORMS package
CL-FORMS:RENDER-FIELD: CL-FORMS package
CL-FORMS:RENDER-FIELD: CL-FORMS package
CL-FORMS:RENDER-FIELD-ERRORS: CL-FORMS package
CL-FORMS:RENDER-FIELD-ERRORS: CL-FORMS package
CL-FORMS:RENDER-FIELD-LABEL: CL-FORMS package
CL-FORMS:RENDER-FIELD-LABEL: CL-FORMS package
CL-FORMS:RENDER-FIELD-WIDGET: CL-FORMS package
CL-FORMS:RENDER-FIELD-WIDGET: CL-FORMS package
CL-FORMS:RENDER-FORM: CL-FORMS package
CL-FORMS:RENDER-FORM: CL-FORMS package
CL-FORMS:RENDER-FORM-END: CL-FORMS package
CL-FORMS:RENDER-FORM-END: CL-FORMS package
CL-FORMS:RENDER-FORM-ERRORS: CL-FORMS package
CL-FORMS:RENDER-FORM-ERRORS: CL-FORMS package
CL-FORMS:RENDER-FORM-START: CL-FORMS package
CL-FORMS:RENDER-FORM-START: CL-FORMS package
CL-FORMS:SET-FIELD-VALUE: CL-FORMS package
CL-FORMS:SET-FIELD-VALUE: CL-FORMS package
CL-FORMS:VALIDATE-FORM: CL-FORMS package
CL-FORMS:VALIDATE-FORM: CL-FORMS package
CL-FORMS:WITH-FORM: CL-FORMS package
CL-FORMS:WITH-FORM: CL-FORMS package
CL-FORMS:WITH-FORM-FIELD-VALUES: CL-FORMS package
CL-FORMS:WITH-FORM-FIELD-VALUES: CL-FORMS package
CL-FORMS:WITH-FORM-FIELDS: CL-FORMS package
CL-FORMS:WITH-FORM-FIELDS: CL-FORMS package
CL-FORMS:WITH-FORM-RENDERER: CL-FORMS package
CL-FORMS:WITH-FORM-RENDERER: CL-FORMS package
CL-FORMS:WITH-FORM-TEMPLATE: CL-FORMS package
CL-FORMS:WITH-FORM-TEMPLATE: CL-FORMS package
CL-FORMS:WITH-FORM-THEME: CL-FORMS package
CL-FORMS:WITH-FORM-THEME: CL-FORMS package

Jump to:   C