Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 76 additions & 96 deletions src/ValidationsFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class FormLoopValidations extends Validations {
return;
}
page.items.filter(item => {
if (item.component === 'FormLoop' && item.config.name === this.element.config.name) {
if (item.component === 'FormLoop' && item.config.name === this.element.config.name && item !== this.element) {
siblings.push(item);
}
});
Expand Down Expand Up @@ -240,8 +240,19 @@ class PageNavigateValidations extends Validations {
*/
class FormElementValidations extends Validations {
async addValidations(validations) {
// Disable validations if field is hidden
if (!this.isVisible()) {
if (this.insideLoop) {
// Inside loops, conditionalHide depends on per-row data and is evaluated
// correctly in the runtime closure. Skip that check here.
// However, visibleInDevice is device-level (not per-row) and is reliably
// set by the VisibilityRule extension, so we still honor it.
const visibleInDevice =
this.element.visibleInDevice === null || this.element.visibleInDevice === undefined
? true
: this.element.visibleInDevice;
if (!visibleInDevice) {
return;
}
} else if (!this.isVisible()) {
return;
}
if (this.element.config && this.element.config.readonly) {
Expand All @@ -258,12 +269,13 @@ class FormElementValidations extends Validations {
}
const fieldName = this.element.config.name;
const validationConfig = this.element.config.validation;
const conditionalHide = this.element.config.conditionalHide;
const parentVisibilityRule = this.parentVisibilityRule;
const insideLoop = this.insideLoop || false;
const deviceConfig = this.element.config.deviceVisibility
? this.element.config.deviceVisibility
: { showForDesktop: true, showForMobile: true };
const closureOptions = {
fieldName,
conditionalHide: this.element.config.conditionalHide,
parentVisibilityRule: this.parentVisibilityRule,
insideLoop: this.insideLoop || false,
deviceConfig: this.element.config.deviceVisibility || { showForDesktop: true, showForMobile: true },
};

set(validations, fieldName, get(validations, fieldName, {}));
const fieldValidation = get(validations, fieldName);
Expand All @@ -275,109 +287,77 @@ class FormElementValidations extends Validations {
}
let validationFn = validators[rule];
if (!validationFn) {
// eslint-disable-next-line no-console
return;
}
if (validation.configs instanceof Array) {
const params = [];
validation.configs.forEach((cnf) => {
params.push(cnf.value);
});
const params = validation.configs.map((cnf) => cnf.value);
params.push(fieldName);
validationFn = validationFn(...params);
}
fieldValidation[rule] = function(...props) {
const data = props[1];
const level = fieldName.split('.').length - 1;
const dataWithParent = this.getDataAccordingToFieldLevel(this.getRootScreen().addReferenceToParents(data), level);
if (parentVisibilityRule) {
const nextParentLevel = insideLoop ? 1 : 0;
const parentDataWithParent = this.getDataAccordingToFieldLevel(this.getRootScreen().addReferenceToParents(data), level + nextParentLevel);
let isParentVisible = true;
try {
isParentVisible = !!Parser.evaluate(parentVisibilityRule, parentDataWithParent);
} catch (error) {
isParentVisible = false;
}

if (!isParentVisible ) {
return true;
}
}

// Check Device Visibility
let visibleInDevice = true;
try {
const isMobileScreen = this.$root.$children[0].$refs.renderer.definition.isMobile;
visibleInDevice =
(isMobileScreen && deviceConfig.showForMobile) ||
(!isMobileScreen && deviceConfig.showForDesktop);
} catch (error) {
visibleInDevice = true;
}
if (!visibleInDevice) {
return true;
}

// Check Field Visibility
let visible = true;
if (conditionalHide) {
try {
visible = !!Parser.evaluate(conditionalHide, dataWithParent);
} catch (error) {
visible = false;
}
}
if (!visible) {
return true;
}
return validationFn.apply(this,props);
};
fieldValidation[rule] = this.buildClosure(validationFn, closureOptions);
});
} else if (typeof validationConfig === 'string' && validationConfig) {
let validationFn = validators[validationConfig];
const validationFn = validators[validationConfig];
if (!validationFn) {
// eslint-disable-next-line no-console
return;
}
fieldValidation[validationConfig] = function(...props) {
const data = props[1];
const level = fieldName.split('.').length - 1;
const dataWithParent = this.getDataAccordingToFieldLevel(this.getRootScreen().addReferenceToParents(data), level);
// Check Parent Visibility
if (parentVisibilityRule) {
const nextParentLevel = insideLoop ? 1 : 0;
const parentDataWithParent = this.getDataAccordingToFieldLevel(this.getRootScreen().addReferenceToParents(data), level + nextParentLevel);
let isParentVisible = true;
try {
isParentVisible = !!Parser.evaluate(parentVisibilityRule, parentDataWithParent);
} catch (error) {
isParentVisible = false;
}

if (!isParentVisible) {
return true;
}
}
// Check Field Visibility
let visible = true;
if (conditionalHide) {
try {
visible = !!Parser.evaluate(conditionalHide, dataWithParent);
} catch (error) {
visible = false;
}
}
if (!visible) {
return true;
}
return validationFn.apply(this,props);
};
fieldValidation[validationConfig] = this.buildClosure(validationFn, closureOptions);
}
if (this.element.items) {
ValidationsFactory(this.element.items, { screen: this.screen, data: this.data }).addValidations(validations);
}
}

buildClosure(validationFn, { fieldName, parentVisibilityRule, insideLoop, deviceConfig, conditionalHide }) {
return function (...props) {
const data = props[1];
const level = fieldName.split('.').length - 1;
const dataWithParent = this.getDataAccordingToFieldLevel(this.getRootScreen().addReferenceToParents(data), level);

if (parentVisibilityRule) {
const nextParentLevel = insideLoop ? 1 : 0;
const parentDataWithParent = this.getDataAccordingToFieldLevel(this.getRootScreen().addReferenceToParents(data), level + nextParentLevel);
let isParentVisible = true;
try {
isParentVisible = !!Parser.evaluate(parentVisibilityRule, parentDataWithParent);
} catch (error) {
isParentVisible = false;
}
if (!isParentVisible) {
return true;
}
}

// Check Device Visibility
let visibleInDevice = true;
try {
const isMobileScreen = this.$root.$children[0].$refs.renderer.definition.isMobile;
visibleInDevice =
(isMobileScreen && deviceConfig.showForMobile) ||
(!isMobileScreen && deviceConfig.showForDesktop);
} catch (error) {
visibleInDevice = true;
}
if (!visibleInDevice) {
return true;
}

// Check Field Visibility
let visible = true;
if (conditionalHide) {
try {
visible = !!Parser.evaluate(conditionalHide, dataWithParent);
} catch (error) {
visible = false;
}
}
if (!visible) {
return true;
}
return validationFn.apply(this, props);
};
}

camelCase(name) {
return name.replace(/_\w/g, m => m.substr(1, 1).toUpperCase());
}
Expand Down
Loading