validierung
Version:
Vue composition function for form validation
2 lines (1 loc) • 9.6 kB
JavaScript
"use strict";var f=require("./internal/shared.min.cjs"),o=require("vue-demi");const j=r=>f.isRecord(r)?"$value"in r:!1,F=r=>f.isRecord(r)?"$uid"in r&&"$value"in r:!1;function m(r,e){for(const t of Object.values(e)){if(F(t)){r.dispose(t.$uid);continue}f.isObject(t)&&m(r,t)}}function S(r,e,t,s){let a=0;for(const[i,l]of Object.entries(r)){const n=f.isArray(r)?+i-a:i,u=F(l),d=u?f.deepCopy(l.$value):o.unref(l);if(s({key:i,value:d,path:t}))e[n]=d;else{++a;continue}u||f.isObject(l)&&(e[n]=f.isArray(l)?[]:{},S(l,e[n],[...t,i],s))}}function D(r,e=()=>!0){const t=f.isArray(r)?[]:{};return S(r,t,[],e),t}function O(r,e,t){Object.entries(e).forEach(([s,a])=>{const i=t[s];if(F(i)){r.getField(i.$uid).reset(a);return}f.isObject(a)&&O(r,a,t[s])})}class x{constructor(){this.defaultValidationBehavior=null,this.vbfMap=new Map}getDefaultVbf(){return this.defaultValidationBehavior===null?()=>!0:this.vbfMap.get(this.defaultValidationBehavior)}}const g=new x;function I(r){const e=g.getDefaultVbf();return r.map(t=>{if(typeof t=="function")return{vbf:e,rule:t};if(!f.isArray(t))return{vbf:e,rule:t};const[s,a,i]=t;if(typeof a=="number")return{vbf:e,rule:s,debounce:a};if(typeof s=="function")return{vbf:s,rule:a,debounce:i};const l=g.vbfMap.get(s);if(l!==void 0)return{vbf:l,rule:a,debounce:i}})}function K(r,e,t){const{$value:s,$rules:a,...i}=t,l=a?I(a):[],n=f.uid(),u=r.registerField(n,e,s,l);return{...i,$uid:n,$value:u.modelValue,$errors:u.errors,$hasError:u.hasError,$validating:u.validating,$dirty:u.dirty,$touched:u.touched,async $validate({setTouched:d,force:h}={}){d!=null||(d=!0),h!=null||(h=!0),d&&(u.touched.value=!0),await r.validate(n,h)}}}function M(r,e){for(const[t,s]of Object.entries(e)){if(j(s)){const a=K(r,t,s);e[t]=a;continue}f.isObject(s)&&M(r,s)}}const B=r=>typeof r=="function",$=r=>B(r)?[r,void 0]:[r.rule,r.key];class P{constructor(e,t,s,a,i){this.touched=o.ref(!1),this.dirty=o.ref(!1),this.rulesValidating=o.ref(0),this.validating=o.computed(()=>this.rulesValidating.value>0),this.errors=o.computed(()=>this.rawErrors.value.filter(f.isDefined)),this.hasError=o.computed(()=>this.hasErrors.value.some(l=>l)),this.simpleValidators=[],this.keyedValidators=new Map,this.form=e,this.uid=t,this.name=s,this.modelValue=o.ref(a),this.rawErrors=o.ref(i.map(()=>null)),this.hasErrors=o.ref(i.map(()=>!1)),this.initialModelValue=f.deepCopy(this.modelValue.value),this.ruleInfos=i.map((l,n)=>{const[u,d]=$(l.rule);if(!u){d&&!this.keyedValidators.has(d)&&this.keyedValidators.set(d,[]);return}let h,c;h=c=(p,k,b=this.modelValue,w=!1)=>{if(w||this.shouldValidate(n,p,k))return this.validate(n,b)};let v,y=0,V;l.debounce&&(v=f.debounce(p=>{V(this.validate(n,p)),this.rulesValidating.value-=y,this.form.rulesValidating.value-=y,y=0},{wait:l.debounce}),h=(p,k,b=this.modelValue,w=!1)=>{if(w||this.shouldValidate(n,p,k))return++y,++this.rulesValidating.value,++this.form.rulesValidating.value,new Promise(A=>{V==null||V(),V=A,v(b)})});const E={ruleNumber:n,validator:h,validatorNotDebounced:c};if(d){const p=this.keyedValidators.get(d);p===void 0?this.keyedValidators.set(d,[E]):p.push(E)}else this.simpleValidators.push(E);return{buffer:new f.LinkedList,rule:u,validator:h,validatorNotDebounced:c,vbf:l.vbf,cancelDebounce:v?()=>{y=0,v.cancel(),V==null||V()}:()=>{}}}),this.watchStopHandle=this.setupWatcher()}async validate(e,t){var s;const{rule:a,buffer:i}=this.ruleInfos[e];let l;const n=o.isRef(t)?a(t.value):a(...t.map(u=>u.value));if((s=i.last)!=null&&s.value&&(i.last.value=!1,this.rulesValidating.value--,this.form.rulesValidating.value--),f.isPromise(n)){const u=i.addLast(!0);this.rulesValidating.value++,this.form.rulesValidating.value++;try{l=await n}catch(d){l=d}if(i.remove(u),u.value)this.rulesValidating.value--,this.form.rulesValidating.value--,this.setError(e,l);else if(typeof l=="string"||typeof l=="symbol")throw l}else l=n,this.setError(e,l)}reset(e=this.initialModelValue){this.watchStopHandle(),this.touched.value=!1,this.dirty.value=!1,this.modelValue.value=f.deepCopy(e),this.rulesValidating.value=0,this.form.rulesValidating.value=0;for(let t=0;t<this.ruleInfos.length;++t){o.isVue3?(this.rawErrors.value[t]=null,this.hasErrors.value[t]=!1):(o.set(this.rawErrors.value,t,null),o.set(this.hasErrors.value,t,!1));const s=this.ruleInfos[t];if(s){s.cancelDebounce();for(const a of s.buffer)a.value=!1}}this.watchStopHandle=this.setupWatcher()}dispose(){o.isVue3&&(this.errors.effect.stop(),this.validating.effect.stop(),this.hasError.effect.stop()),this.watchStopHandle()}shouldValidateForKey(e,t,s){const a=this.keyedValidators.get(e);for(let i=0;i<a.length;++i)if(!this.shouldValidate(a[i].ruleNumber,t,s))return!1;return!0}shouldValidate(e,t,s){return this.ruleInfos[e].vbf({hasError:this.rawErrors.value[e]!==null,touched:this.touched.value,dirty:this.dirty.value,force:t,submit:s,value:this.modelValue.value})}setError(e,t){const s=typeof t=="string",a=typeof t=="symbol";if(o.isVue3){if(s)throw this.rawErrors.value[e]=t,this.hasErrors.value[e]=!0,t;if(a)throw this.hasErrors.value[e]=!0,t;this.rawErrors.value[e]=null,this.hasErrors.value[e]=!1}else{if(s)throw o.set(this.rawErrors.value,e,t),o.set(this.hasErrors.value,e,!0),t;if(a)throw o.set(this.hasErrors.value,e,!0),t;o.set(this.rawErrors.value,e,null),o.set(this.hasErrors.value,e,!1)}}setupWatcher(){return o.watch(this.modelValue,()=>{this.dirty.value=!0,this.form.validate(this.uid)},{deep:!0})}}class R extends Error{constructor(){super("One or more validation errors occurred")}}class C{constructor(){this.simpleMap=new Map,this.keyedMap=new Map,this.reactiveErrors=o.shallowReactive({}),this.rulesValidating=o.ref(0),this.submitting=o.ref(!1),this.validating=o.computed(()=>this.rulesValidating.value>0),this.hasError=o.computed(()=>this.errors.value.length>0),this.errors=o.computed(()=>Object.values(this.reactiveErrors).flatMap(e=>e.value))}registerField(e,t,s,a){const i=new P(this,e,t,s,a),l={rollbacks:[],field:i},n=new Set;for(let u=0;u<a.length;++u){const d=$(a[u].rule)[1];if(d===void 0)continue;let h=this.keyedMap.get(d);h===void 0?this.keyedMap.set(d,{fields:[i],modelValues:[i.modelValue]}):n.has(d)||(h.fields.push(i),h.modelValues.push(i.modelValue)),h=this.keyedMap.get(d),n.add(d);const c=()=>{for(let v=0;v<h.fields.length;++v)if(h.fields[v]===i){h.fields.splice(v,1),h.modelValues.splice(v,1);break}h.fields.length===0&&this.keyedMap.delete(d)};l.rollbacks.push(c)}return this.simpleMap.set(e,l),o.isVue3?this.reactiveErrors[e]=i.errors:o.set(this.reactiveErrors,e,i.errors),i}validate(e,t=!1){const{field:s}=this.simpleMap.get(e);return Promise.allSettled([...s.simpleValidators.map(({validator:a})=>a(t,!1)),...this.collectValidatorResultsForKeys(s,t,!1)])}async validateAll(e){const t=await Promise.allSettled(this.collectValidatorResultsForNames(e));for(let s=0;s<t.length;++s)if(t[s].status==="rejected")throw new R}dispose(e){const t=this.simpleMap.get(e);t&&(t.field.dispose(),t.rollbacks.forEach(s=>s())),this.simpleMap.delete(e),o.isVue3?delete this.reactiveErrors[e]:o.del(this.reactiveErrors,e)}resetFields(){for(const{field:e}of this.simpleMap.values())e.reset()}getField(e){const t=this.simpleMap.get(e);return t==null?void 0:t.field}*collectValidatorResultsForKeys(e,t,s,a=e.keyedValidators.keys()){for(const i of a){const{fields:l,modelValues:n}=this.keyedMap.get(i);if(!(this.isEveryOtherFieldTouched(e,l)&&e.shouldValidateForKey(i,t,s)))return;for(let d=0;d<l.length;++d){const h=l[d].keyedValidators.get(i);for(let c=0;c<h.length;++c)yield s?h[c].validatorNotDebounced(t,s,n,l[c]===e):h[c].validator(t,s,n,l[c]===e)}}}*collectValidatorResultsForNames(e){if(e===void 0){for(const{field:a}of this.simpleMap.values()){a.touched.value=!0;for(let i=0;i<a.simpleValidators.length;++i)yield a.simpleValidators[i].validatorNotDebounced(!1,!0)}for(const[a,{fields:i,modelValues:l}]of this.keyedMap.entries())for(let n=0;n<i.length;++n){const u=i[n].keyedValidators.get(a);for(let d=0;d<u.length;++d)yield u[d].validatorNotDebounced(!1,!0,l)}return}if(e.length===0)return;const t=new Set(e),s=new Set;for(const{field:a}of this.simpleMap.values())t.has(a.name)&&(a.touched.value=!0);for(const{field:a}of this.simpleMap.values())if(t.has(a.name)){for(let i=0;i<a.simpleValidators.length;++i)yield a.simpleValidators[i].validatorNotDebounced(!1,!0);for(const i of a.keyedValidators.keys())s.has(i)||(s.add(i),yield*this.collectValidatorResultsForKeys(a,!1,!0,[i]))}}isEveryOtherFieldTouched(e,t){for(let s=0;s<t.length;++s)if(e!==t[s]&&!t[s].touched.value)return!1;return!0}}function T(r){const e=new C,t=new f.PromiseCancel;M(e,r);const s=o.reactive(r);return{form:s,submitting:e.submitting,validating:e.validating,hasError:e.hasError,errors:e.errors,async validateFields({names:a,predicate:i}={}){e.submitting.value=!0;const l=D(s,i);try{await t.race(e.validateAll(a))}finally{e.submitting.value=!1}return l},resetFields(a){t.cancelReject(new R),a===void 0?e.resetFields():O(e,a,s)},add(a,i){const l=a[a.length-1];if(l!==void 0){const n={[l]:i};M(e,n);let u=n[l];const d=f.path(a,s);o.isVue2&&(u=o.reactive(u)),Array.isArray(d)?d.push(u):f.set(s,a,u)}},remove(a){const i=a.pop();if(i!==void 0)if(a.length===0)m(e,s[i]),o.isVue3?delete s[i]:o.del(s,i);else{const l=f.path(a,s);if(Array.isArray(l)){const n=l.splice(+i,1);m(e,n)}else m(e,l[i]),o.isVue3?delete l[i]:o.del(l,i)}}}}function H(r){return{install(){var e;for(const[t,s]of Object.entries((e=r.validationBehavior)!=null?e:{}))g.vbfMap.set(t,s);g.vbfMap.has(r.defaultValidationBehavior)&&(g.defaultValidationBehavior=r.defaultValidationBehavior)}}}exports.ValidationError=R,exports.createValidation=H,exports.useValidation=T;