Created
February 9, 2020 20:51
-
-
Save thosmos/95cdcb5480c9144f7b29d97d5475238b to your computer and use it in GitHub Desktop.
Fulcro Validator with custom error strings
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn make-validator | |
"Create a form/field validation function using a supplied field checker. The field checker will be given | |
the entire form (denormalized) and a single field key that is to be checked. It must return | |
a boolean indicating if that given field is valid or not, or a custom error map if invalid. | |
During a recursive check for a form, the validation function will be in the correct context (e.g. the form supplied will contain | |
the field. There is no need to search for it in subforms). | |
make-validator returns a three arity function: | |
- `(fn [form] ...)` - Calling this version will return :unchecked, :valid, or :invalid for the entire form. | |
- `(fn [form field] ...)` - Calling this version will return :unchecked, :valid, or :invalid for the single field. | |
- `(fn [form field return-map?] ...)` - Calling this version will return :unchecked, :valid, :invalid, or a custom error map for the single field. | |
Typical usage would be to show messages around the form fields: | |
``` | |
(defn field-valid? [form field] true) ; just return true | |
(def my-validator (make-validator field-valid?)) | |
(defn valid? [form field] | |
(= :valid (my-validator form field))) | |
(defn checked? [form field] | |
(not= :unchecked (my-validator form field))) | |
``` | |
A more complex example with custom error messages: | |
``` | |
(defn field-valid? [form field] | |
(case field | |
:my-field | |
(let [field-val (get form field)] | |
(cond | |
(= field-val :foo) | |
{:msg \"must not be :foo\"} | |
(= field-val :bar) | |
{:msg \":bar is an invalid value\"} | |
(= field-val :baz) | |
false | |
:else | |
true)))) | |
(def map-validator (make-validator field-valid?)) | |
(defn custom-msg? [form field] | |
(let [valid? (map-validator form field true)] | |
(when (map? valid?) | |
(:msg valid?))) | |
``` | |
" | |
[field-valid?] | |
(fn custom-get-validity* | |
([ui-entity-props field return-map?] | |
(let [{{complete? ::fs/complete?} ::fs/config} ui-entity-props | |
complete? (or complete? #{})] | |
(if (not (complete? field)) | |
:unchecked | |
(let [valid? (field-valid? ui-entity-props field)] | |
(if (or (map? valid?) (not valid?)) | |
(if return-map? | |
valid? | |
:invalid) | |
:valid))))) | |
([ui-entity-props field] | |
(custom-get-validity* ui-entity-props field false)) | |
([ui-entity-props] | |
(let [{{:keys [::fs/fields ::fs/subforms]} ::fs/config} ui-entity-props | |
immediate-subforms (fs/immediate-subforms ui-entity-props (-> subforms keys set)) | |
field-validity (fn [current-validity k] (fs/merge-validity current-validity (custom-get-validity* ui-entity-props k))) | |
subform-validities (map custom-get-validity* immediate-subforms) | |
subform-validity (reduce fs/merge-validity :valid subform-validities) | |
this-validity (reduce field-validity :valid fields)] | |
(fs/merge-validity this-validity subform-validity))))) | |
(s/def ::range number?) | |
(s/def ::rsd number?) | |
(s/def ::threshold number?) | |
(s/def :parameter/precisionCode (s/nilable (s/keys :opt-un [::range ::rsd ::threshold]))) | |
(defn parameter-valid [form field] | |
(let [v (get form field)] | |
(case field | |
:parameter/precisionCode | |
(let [edn-val (try (cljs.reader/read-string v) (catch js/Object ex nil)) | |
{:keys [threshold range rsd]} edn-val] | |
(cond | |
(and (some? threshold) (or (not range) (not rsd))) | |
{:msg "If Threshold is set, then both Range and RSD must also be set"} | |
(and (not threshold) (some? range) (some? rsd)) | |
{:msg "If Threshold is not set, then either Range or RSD can be set, but not both"} | |
(not (s/valid? field edn-val)) | |
{:msg (str "All values must be numbers")} | |
:else | |
true))))) | |
(def validator (make-validator parameter-valid)) | |
(defsc my-comp [this props] | |
{} | |
(div :.field | |
(dom/label {} "Precision") | |
(let [valid? (validator props :parameter/precisionCode true)] | |
(when (map? valid?) | |
(div (:msg valid?)))))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment