cl-ds / data /repos /cl-forms /src /fields /list.lisp
j14i's picture
3375 CL macro transformation examples from 85 libraries
43203b4 verified
(in-package :forms)
(defclass list-form-field (form-field)
((type :initarg :type
:initform (error "Provide the list type via :type")
:accessor list-field-type
:type function
:documentation "The list elements type.")
(empty-item-predicate :initarg :empty-item-predicate
:initform nil
:accessor empty-item-predicate
:type (or null function)
:documentation "A predicate that tells when a list item is considered empty, and so it is removed from the list")
(add-button :initarg :add-button
:initform t
:accessor add-button-p
:type boolean
:documentation "Whether have a list 'ADD' button or not")
(remove-button :initarg :remove-button
:initform t
:accessor remove-button-p
:type boolean
:documentation "Whether add an item removal button or not"))
(:documentation "A field that contains a list of elements (either other fields or subforms)"))
(defmethod make-form-field ((field-type (eql :list)) &rest args)
(let ((list-field-type (etypecase (getf args :type)
(list
(lambda ()
(apply #'make-form-field
(first (getf args :type))
(append (list :name '||)
(rest (getf args :type))))))
(keyword (lambda ()
(make-form-field
(getf args :type)
:name '||)))
(function (getf args :type)))))
(apply #'make-instance 'list-form-field :type list-field-type args)))
(defmethod field-read-from-request ((field list-form-field) form parameters)
;; First, create a regex to filter the list-field parameters. It's those with the format <field>[<index>]
(let ((regex
(ppcre:create-scanner `(:sequence :start-anchor
,(field-request-name field form)
"["
(:register (:greedy-repetition 1 nil :digit-class))
"]"))))
;; Then, extract the indexes posted
(let ((request-list-indexes
(remove-duplicates
(loop for param in parameters
when (ppcre:scan regex (car param))
collect (ppcre:register-groups-bind (index)
(regex (car param))
(when (stringp index)
(parse-integer index)))))))
;; With the indexes posted, read the list items from the request parameters
(let ((items
(mapcar (lambda (i)
(let* ((*field-path* (cons (format nil "~a[~a]"
(string-downcase (string (field-name field)))
i)
(rest *field-path*)))
(item-field (funcall (list-field-type field))))
(field-read-from-request item-field
form
parameters)
(field-value item-field)))
request-list-indexes)))
;; Remove items with remove-predicate. For example,
(let ((items (if (empty-item-predicate field)
(remove-if (empty-item-predicate field)
items)
items)))
;; The value of a list field is a list of fields (the type of its list elements)
(setf (field-value field) items))))))