ClojureScript library to validate forms.
spec
and fn
."foo"
, {:level :warn :msg "foo"}
or whatever.on-blur
/ on-change
/ immediately after load page / ...re-frame
, fulcro
or whatever.Why? I need it myself. But I didn't find any library which satisfy me, so I wrote my own.
Read my article form validation to learn more rationales.
Discover it naturally by real code: https://kwladyka.github.io/form-validator-cljs/
The best to do after this readme. But if you are inpatient take a look and back here.
{:deps {github-kwladyka/form-validator {:git/url "https://github.com/kwladyka/form-validator-cljs" :sha "0fec75c9046bff3b5c5f37a0bd7f821c1e8edbe0"}}}
The recommended way to do it is to use lein-tool-deps.
Basicly add this plugin and make deps.edn
file like above.
(:require [form-validator.core :as form-validator])
To be compatible with reagent library, needs to use reagent.core/atom
instead clojure.core/atom
.
(ns app.core
(:require [reagent.core :as r]
[form-validator.core :as form-validator]))
;; First line in core ns or dedicated init fn is a right place
(swap! form-validator/conf #(merge % {:atom r/atom}))
Init form
(-> {:names->value {:email ""
:password ""}
:form-spec ::spec/form
(form-validator/init-form))
return atom
contained map:
{:form-spec :app.spec/form
:names->value {:email "" :password ""}
:names->invalid {:email [:app.spec/form :app.spec/email] :password [:app.spec/form :app.spec/password :app.spec/password-not-empty]}
:names->show #{}
:names->validators {}}
Then you can use functions from ns form-validator.core
:
event->names->value!
- With on-change
/ on-blur
input event to update values.event->show-message
- With on-blur
/ on-change
input event to trigger when show messages in UI.?show-message
- Get message to show in UI for input. Also to know if mark input as not valid in UI.form-valid?
- true / falsevalidate-form-and-show?
- Call validate-form
and show all messages. Use with submit button.
;; clojure.spec.alpha
(s/def ::email (s/and string? (partial re-matches #"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$")))
(s/def ::password-not-empty not-empty)
(s/def ::password-length #(<= 6 (count %)))
(s/def ::password (s/and string? ::password-not-empty ::password-length))
(s/def ::form (s/keys :req-un [::email ::password]
:opt-un [::password-repeat]))
;; form-valiadtor
(-> {:names->value {:email ""
:password ""}
:form-spec ::form
:names->validators {:email [email-exist?]
:password-repeat [password-repeat? ::spec-key]}}
(form-validator/init-form))
:names->value
- Form inputs with values to initialize.:opt-un
it can be ommited.:form-spec
- Spec to validate whole form.:names->validators
- Vector of spec keywords and fn. Order matter.spec
for form / if checkbox "accept terms" is checked / fn
to compare password-repeat / check if user already exist by API during registration.:names->show
- #{}
with names of inputs to show error messages on start.
Use cases: Form with already filled values.You can use :form-spec
and :names->validators
together. :form-spec
is checked first.
(init-form ...)
return atom
:
{:form-spec :app.spec/form
:names->value {:email "", :password ""}
:names->invalid {:email [::form ::form-map ::email]
:password [::form ::form-map ::password ::password-not-empty]}
:names->show #{}
:names->validators {:email #object[cljs$core$sp1], :password-repeat #object[cljs$core$sp1]}}
:form-spec
- Init form value without any change.:names->value
- Values of the form.:names->invalid
- Invalid inputs with reasons of validation fail.:names->show
- Add name of the input here, when you want to show message in UI.:names->validators
- All validators converted to one fn which works similar to some
. Check all validators for specific input one by one, unless fail or return nil
.:names->validators
can contain ::spec-key
and fn
.:cljs.spec.alpha/problems
:via
. For example [::form ::form-map ::password ::password-not-empty]
. It means spec ::form
refer to spec ::form-map
, which refer to spec ::password
, which refer to spec ::password-not-empty
, which failed.fn
can return vectors like spec, but also strings, map or any value.;; Check error for input name "password"
(->> {::email "Typo? It doesn't look valid."
::password "Minimum 6 characters and one special character !@#$%^&*."
:password-not-equal "Repeat password has to be the same."}
(form-validator/?show-message form :password))
Based on [::form ::form-map ::password ::password-not-empty]
it is trying to find ::password-not-empty
message. Map not contain message for this spec. Then try to find ::password
and return message. If not find, going deeper. If not find any, return true
.
If reason of fail is not a vector, then it is returning as it is. For example "cutom message"
or {:level :warn :msg "This is only warning."}
. This is dedicated for fn
validators.
atom
returned by form-init
, add-watch
on atom
, add functions on top of core functions, use your own functions instead of core ones. It is designed to let you make custom things. In most of cases you really don't need to do it. It could be useful if you want to make your module based on this one.form-valid?
function or make two form-init
(first for errors and second for warnings). I decided to not make it as part of this library, because it is individual thing for project.Everything below this line is mainly for myself as a maintainer of this library.
Library has to be always check with web browsers manually! Not only automated tests. The reasons are differences between web browsers and practical aspects of usability vs imagination :)
To do it use doc
branch from this repository.
After all make a commit to readme with new sha hash for deps.edn.
clj -A:test:test-once
clj -A:test:test-watch
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close