From d0425d1a2c988d6e5d0de4907da31a2df5a0f02a Mon Sep 17 00:00:00 2001 From: Josha von Gizycki Date: Tue, 11 Sep 2018 15:00:24 +0200 Subject: [PATCH] add possibility to render forms with a single function --- src/wanijo/attribute/routes.clj | 2 +- src/wanijo/framework/form.clj | 80 +++++++++++++++++++++++++++++++-- src/wanijo/schema/view.clj | 17 ++++--- 3 files changed, 87 insertions(+), 12 deletions(-) diff --git a/src/wanijo/attribute/routes.clj b/src/wanijo/attribute/routes.clj index 23d1d3c..cbb0375 100644 --- a/src/wanijo/attribute/routes.clj +++ b/src/wanijo/attribute/routes.clj @@ -9,7 +9,7 @@ (defn new! [req] (let [schema-uuid (get-in req [:params :schema])] - (if (form/valid? view-schema/new-attr-form req) + (if (form/valid? view-schema/attr-form req) (do (domain/create-new! (get-in req [:params :name]) diff --git a/src/wanijo/framework/form.clj b/src/wanijo/framework/form.clj index 35e384e..897e407 100644 --- a/src/wanijo/framework/form.clj +++ b/src/wanijo/framework/form.clj @@ -1,18 +1,22 @@ (ns wanijo.framework.form (:require [clojure.spec.alpha :as spec] [hiccup.form :as hform] - [wanijo.framework.view :as view])) + [wanijo.framework.view :as view] + [ring.util.anti-forgery :refer [anti-forgery-field]])) (spec/def ::label string?) (spec/def ::required boolean?) (spec/def ::spec keyword?) (spec/def ::options (spec/coll-of (spec/tuple string? string?))) +(spec/def ::widget + #(some (partial = %) [:input :select :checkbox :textarea])) (spec/def ::field (spec/keys :req-un [::label ::required ::spec] - :opt-un [::options])) + :opt-un [::options + ::widget])) (spec/def ::fields (spec/and map? (fn [map] @@ -34,8 +38,10 @@ (:clojure.spec.alpha/problems (spec/explain-data spec-key field-value))))) -(defn field-valid? [value spec-key req] - (or (empty? (:form-params req)) +(defn field-valid?[value spec-key req] + (or (or (and (contains? req :form-params) + (empty? (:form-params req))) + (empty? req)) (spec/valid? spec-key value))) (defn field @@ -102,3 +108,69 @@ result)) true (:params req))) + +(defn render-input [id {:keys [label required spec]} values] + (let [value (id values)] + (list (when-not (field-valid? value spec values) + (spec-to-errmsg label spec value)) + (hform/label id label) + (hform/text-field {:required (when required "required")} + id + value)))) + +(defn render-checkbox [id {:keys [label spec]} values] + (let [value (id values)] + (list + (when-not (field-valid? value spec values) + (spec-to-errmsg label spec value)) + (hform/label id label) + (hform/check-box id (some? value) value)))) + +(defn render-textarea [id {:keys [label spec]} values] + (let [value (id values)] + (list + (when-not (field-valid? value spec values) + (spec-to-errmsg label spec value)) + (hform/label id label) + (hform/text-area id value)))) + +(defn render-select [id {:keys [label spec options]} values] + (let [value (id values)] + (list + (when-not (field-valid? value spec values) + (spec-to-errmsg label spec value)) + (hform/label id label) + (hform/drop-down id options value)))) + +(def render-mapping + {:input render-input + :checkbox render-checkbox + :textarea render-textarea + :select render-select}) + +(defn cleaned-values + "Removes keys from value map that are not configured in the form" + [form-def values] + (reduce-kv + (fn [agg value-key value] + (if (contains? (:fields form-def) value-key) + (assoc agg value-key value) + agg)) + {} + values)) + +(defn render-form [form-def values] + {:pre [(spec/valid? ::form form-def)]} + (let [cleaned-values (cleaned-values form-def values)] + (conj (map + (fn [[field-id field-def]] + (let [{:keys [options widget]} field-def + widget (cond (some? widget) widget + (some? options) :select + :else :input) + widget-renderer (widget render-mapping)] + (widget-renderer field-id + field-def + cleaned-values))) + (:fields form-def)) + (anti-forgery-field)))) diff --git a/src/wanijo/schema/view.clj b/src/wanijo/schema/view.clj index 6e88445..454b9e9 100644 --- a/src/wanijo/schema/view.clj +++ b/src/wanijo/schema/view.clj @@ -13,7 +13,7 @@ :required true :spec ::domain/name}}}) -(def new-attr-form +(def attr-form {:fields {:name {:label "Name" :required true :spec ::attr-domain/name} @@ -26,7 +26,8 @@ ["File" "file"]]} :required {:label "Required" :required false - :spec ::attr-domain/required}}}) + :spec ::attr-domain/required + :widget :checkbox}}}) (defn overview! [req] (let [session (:session req) @@ -65,14 +66,16 @@ [:h2 "Attributes"] [:ul (for [attr attrs] - [:li (:name attr)])] + [:li + (hform/form-to [:post ""] + (form/render-form attr-form attr) + (hform/submit-button "Save")) + (hform/form-to [:delete ""] + (hform/submit-button "Delete!"))])] [:h3 "New attribute"] (hform/form-to [:post (path :attribute-new)] - (anti-forgery-field) + (form/render-form attr-form (:params req)) (hform/hidden-field "schema" (:uuid schema)) - (form/field new-attr-form :name req) - (form/drop-down new-attr-form :type req) - (form/check-box new-attr-form :required req) (hform/submit-button "Save")) [:h2 "Actions"] (hform/form-to {:class "inline"}