updates
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
import './validator.css';
|
||||
|
||||
import { Ut } from '../Ut';
|
||||
import { El } from '../dom';
|
||||
import { Boot } from '../../core/Boot';
|
||||
import { Validator } from './Validator';
|
||||
|
||||
////////////////////////////
|
||||
// Class Form Validator
|
||||
////////////////////////////
|
||||
|
||||
export class FormValidator extends Validator {
|
||||
|
||||
constructor(target, opts) {
|
||||
|
||||
super(target);
|
||||
|
||||
this.target = target;
|
||||
|
||||
if (this.target.dxData('form')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.target.dxData('form', this);
|
||||
|
||||
this.opts = { ... {
|
||||
submit : false,
|
||||
display_err: true,
|
||||
reset_form : false
|
||||
}, ... opts };
|
||||
|
||||
Ut.trigger(this.opts.onInit, this.target.elements, this);
|
||||
|
||||
this.setEvents();
|
||||
}
|
||||
|
||||
setEvents() {
|
||||
|
||||
// Click on error UI
|
||||
|
||||
this.target.dxOn('click.ui', '.sp-has-error', (_, t) => this.target.dxFind('input[name="' + t.dxFind('input').name + '"', item => item.parentNode?.classList.remove('sp-has-error')) );
|
||||
|
||||
// Input Focus - Handler
|
||||
|
||||
this.target.dxOn('click.form', 'input, select, textarea', (_, t) => {
|
||||
|
||||
if (this.opts.display_err) {
|
||||
|
||||
let fieldWrapper = t.dxParents('.form-field');
|
||||
|
||||
fieldWrapper?.classList.remove('form-field-error');
|
||||
|
||||
fieldWrapper?.dxFind('.sp-label-error')?.remove();
|
||||
}
|
||||
|
||||
Ut.trigger(this.opts.onClick);
|
||||
});
|
||||
|
||||
// Submit - Handler
|
||||
this.target.dxOn('submit.form', e => {
|
||||
|
||||
if (!this.opts.submit) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
this.entry = this.getData();
|
||||
|
||||
if (Ut.isFn(this.opts.valid) && !this.valid()) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Ut.isFn(this.opts.onSubmit) && !this.opts.onSubmit(this.entry, this)) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.opts.submit) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Ut.isFn(this.opts.onSubmit)) {
|
||||
this.opts.onSubmit.call(this);
|
||||
}
|
||||
|
||||
if (this.opts.reset_form) {
|
||||
this.target.dxResetForm();
|
||||
}
|
||||
});
|
||||
|
||||
// Fix keydown enter bug
|
||||
|
||||
let delayed = false;
|
||||
|
||||
this.target.dxOn('keydown.form', e => {
|
||||
|
||||
if (e.key == 'Enter' && e.target.tagName.toLowerCase() != 'textarea') {
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
if (!e.target.readOnly && !this.pendingRequest && !delayed) {
|
||||
|
||||
delayed = true;
|
||||
|
||||
setTimeout(() => delayed = false, 200);
|
||||
this.target.dxTrigger('submit');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
valid () {
|
||||
|
||||
let pass = true;
|
||||
|
||||
this.target.dxFind('.form-field-error', item => item.classList.remove('form-field-error', 'sp-has-error'));
|
||||
this.target.dxFind('.sp-label-error', item => item.remove());
|
||||
|
||||
let rules = this.opts.valid(this.entry);
|
||||
|
||||
for (let name in rules) {
|
||||
|
||||
if (!(name in this.target.elements)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let el = this.target.elements[ name ];
|
||||
|
||||
if (!('length' in el ) || el.nodeName.toLowerCase() == 'select') {
|
||||
el = [ el ];
|
||||
}
|
||||
|
||||
for (let input of el) {
|
||||
|
||||
let val = (input.type && input.type == 'checkbox')
|
||||
? (input.checked ? input.value : '')
|
||||
: El('p', {}, input.value).textContent.trim();
|
||||
|
||||
let ruleList = rules[ name ].split('|');
|
||||
|
||||
for (let tokenRule of ruleList) {
|
||||
|
||||
let chunks = tokenRule.split(':');
|
||||
|
||||
let args = chunks[1] ?? null;
|
||||
|
||||
let rule = chunks[0];
|
||||
|
||||
if (!this.handlers[rule]) {
|
||||
throw new Error(`The rule "{${rule}}" does not exist`);
|
||||
}
|
||||
|
||||
let format = { value: Validator.INPUT_STR };
|
||||
|
||||
let result = this.handlers[ rule ] (val ?? null, !!input, args, format);
|
||||
|
||||
if (result === false) {
|
||||
|
||||
pass = false;
|
||||
|
||||
let emsg = this.renderRuleMsg(format, rule, args);
|
||||
|
||||
if (this.opts.display_err) {
|
||||
this.inputError(input, emsg);
|
||||
}
|
||||
|
||||
Ut.trigger(this.opts.onError, emsg);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (result === null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
renderRuleMsg(format, rule, args) {
|
||||
|
||||
args = args ? args.split(',') : null;
|
||||
|
||||
return Boot.intData.error[ this.formatName(format.value) + '_' + rule ].replace(/\$(\d+)/g, (_, i) => args[i - 1] ?? '')
|
||||
}
|
||||
|
||||
formatName(val) {
|
||||
|
||||
switch (val) {
|
||||
case Validator.INPUT_STR: return 'str';
|
||||
case Validator.INPUT_NUM: return 'num';
|
||||
case Validator.INPUT_FILE: return 'file';
|
||||
default: return 'str';
|
||||
}
|
||||
}
|
||||
|
||||
inputError(input, emsg) {
|
||||
|
||||
if (input.type == 'checkbox' || input.type == 'radio') {
|
||||
input.parentNode.classList.add('sp-has-error');
|
||||
}
|
||||
else {
|
||||
|
||||
let fieldWrapper = input.dxParents('.form-field');
|
||||
|
||||
if (!fieldWrapper) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fieldWrapper.classList.add('form-field-error');
|
||||
|
||||
fieldWrapper.append( El('div', { className: 'sp-label-error' }, emsg ) );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.target.dxOff('submit.form click.form keydown.form click.ui');
|
||||
this.target.dxRemoveData('form');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user