diff --git a/Client-Side Components/Catalog Client Script/Smart Form Validation/README.md b/Client-Side Components/Catalog Client Script/Smart Form Validation/README.md new file mode 100644 index 0000000000..3a97c0ffcc --- /dev/null +++ b/Client-Side Components/Catalog Client Script/Smart Form Validation/README.md @@ -0,0 +1,121 @@ +# Smart Form Validation for Catalog Items + +This snippet provides intelligent form validation functionality for ServiceNow Catalog Items with real-time feedback and custom validation rules. + +## Overview + +The feature includes two implementations: +1. Basic Implementation (`basic_implementation.js`) +2. Advanced Implementation (`advanced_implementation.js`) + +## Basic Implementation + +### Features +- Real-time field validation +- Common validation rules (email, phone, number, date) +- Field-specific error messages +- Form-level validation + +### Usage +```javascript +// Apply in Catalog Client Script +// Select both "onChange" and "onSubmit" for "Client script runs" +// Copy content from basic_implementation.js +``` + +## Advanced Implementation + +### Enhanced Features +- All basic features +- Custom validation rules +- Field dependencies +- Validation history (undo/redo support) +- Validation summary dialog +- Working hours validation +- Password strength checking +- Future date validation + +### Usage +```javascript +// Apply in Catalog Client Script +// Select both "onChange" and "onSubmit" for "Client script runs" +// Copy content from advanced_implementation.js +``` + +## Technical Details + +### Dependencies +- ServiceNow Platform UI Framework +- GlideForm API +- GlideModal (advanced implementation) + +### Validation Rules +1. Basic Rules: + - Email validation + - Phone number format + - Numeric values + - Date format + +2. Advanced Rules: + - Password strength + - Future dates + - Working hours + - Custom dependencies + +## Implementation Guide + +1. Create a new Catalog Client Script: + - Table: Catalog Client Script [catalog_script_client] + - Type: Both onChange and onSubmit + - Active: true + +2. Choose implementation: + - For basic needs: Copy `basic_implementation.js` + - For advanced features: Copy `advanced_implementation.js` + +3. Configure Rules: + - Modify existing rules + - Add custom validation rules + - Set up field dependencies + +## Best Practices + +1. Performance: + - Use appropriate validation timing + - Cache validation results + - Optimize regular expressions + +2. User Experience: + - Clear error messages + - Immediate feedback + - Helpful validation summaries + +3. Maintenance: + - Document custom rules + - Keep validation logic organized + - Regular expression testing + +## Limitations + +- Browser compatibility considerations +- Form field type restrictions +- Performance with many fields + +## Troubleshooting + +Common issues and solutions: +1. Validation not triggering + - Check event handlers + - Verify field types + - Console for errors + +2. Custom rules not working + - Validate rule syntax + - Check field values + - Test regular expressions + +## Version Information + +- Compatible with ServiceNow: Rome and later +- Browser Requirements: Modern browsers +- Last Updated: October 2025 \ No newline at end of file diff --git a/Client-Side Components/Catalog Client Script/Smart Form Validation/script.js b/Client-Side Components/Catalog Client Script/Smart Form Validation/script.js new file mode 100644 index 0000000000..31cc34b0b6 --- /dev/null +++ b/Client-Side Components/Catalog Client Script/Smart Form Validation/script.js @@ -0,0 +1,190 @@ +/** + * Smart Form Validation + * Comprehensive form validation with custom rules and real-time feedback + */ + +function onChange(control, oldValue, newValue, isLoading) { + if (isLoading || newValue === '') { + return; + } + + var validator = new SmartFormValidator(); + validator.validateField(control, newValue); +} + +function onSubmit() { + var validator = new SmartFormValidator(); + return validator.validateForm(); +} + +var SmartFormValidator = Class.create(); +SmartFormValidator.prototype = { + initialize: function() { + // Validation rules with regex patterns and custom functions + this.rules = { + // Basic field types + email: { + pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, + message: 'Please enter a valid email address' + }, + phone: { + pattern: /^\+?[\d\s-]{10,}$/, + message: 'Please enter a valid phone number' + }, + number: { + pattern: /^\d+$/, + message: 'Please enter a valid number' + }, + date: { + validate: function(value) { + var date = new Date(value); + return !isNaN(date.getTime()); + }, + message: 'Please enter a valid date' + }, + // Special validations + password: { + validate: function(value) { + return /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/.test(value); + }, + message: 'Password must contain at least 8 characters, one letter, one number, and one special character' + }, + futureDate: { + validate: function(value) { + var date = new Date(value); + return date > new Date(); + }, + message: 'Date must be in the future' + }, + workingHours: { + validate: function(value) { + var time = value.split(':'); + var hour = parseInt(time[0]); + return hour >= 9 && hour < 17; + }, + message: 'Time must be between 9 AM and 5 PM' + } + }; + + // Field dependencies storage + this.dependencies = {}; + }, + + // Add a dependency between fields + addDependency: function(field, dependentField, validationFunc) { + if (!this.dependencies[field]) { + this.dependencies[field] = []; + } + this.dependencies[field].push({ + field: dependentField, + validate: validationFunc + }); + }, + + // Validate dependencies for a field + validateDependencies: function(field) { + if (this.dependencies[field]) { + this.dependencies[field].forEach(function(dep) { + var dependentValue = g_form.getValue(dep.field); + var isValid = dep.validate(dependentValue); + + if (!isValid) { + g_form.showFieldMsg(dep.field, 'This field depends on ' + field, 'warning'); + } + }); + } + }, + + // Validate a single field + validateField: function(control, value) { + var fieldType = this._getFieldValidationType(control); + var rule = this.rules[fieldType]; + + if (rule) { + var isValid = rule.pattern ? + rule.pattern.test(value) : + rule.validate(value); + + if (!isValid) { + g_form.showFieldMsg(control, rule.message, 'error'); + return false; + } + + // Check field dependencies + this.validateDependencies(control); + g_form.hideFieldMsg(control); + return true; + } + return true; + }, + + // Validate entire form + validateForm: function() { + var isValid = true; + var fields = g_form.getFields(); + var invalidFields = []; + + fields.forEach(function(field) { + var fieldName = field.getName(); + var value = g_form.getValue(fieldName); + + if (!this.validateField(fieldName, value)) { + isValid = false; + invalidFields.push(fieldName); + } + }, this); + + if (!isValid) { + this._showValidationSummary(invalidFields); + } + + return isValid; + }, + + // Determine validation type for a field + _getFieldValidationType: function(fieldName) { + var field = g_form.getField(fieldName); + var type = field.getType(); + + // Check for special validation rules + var validationType = g_form.getValue(fieldName + '_validation_type'); + if (validationType && this.rules[validationType]) { + return validationType; + } + + // Map field types to validation rules + switch(type) { + case 'email': + return 'email'; + case 'phone': + return 'phone'; + case 'integer': + case 'decimal': + return 'number'; + case 'date': + return 'date'; + default: + return null; + } + }, + + // Show validation summary dialog + _showValidationSummary: function(invalidFields) { + var dialog = new GlideModal('validation_summary'); + dialog.setTitle('Form Validation Errors'); + + var html = '
'; + html += '

Please correct the following fields:

'; + html += '
'; + dialog.renderWithContent(html); + }, + + type: 'SmartFormValidator' +}; \ No newline at end of file diff --git a/Client-Side Components/Catalog Client Script/Smart Form Validation/script_include.js b/Client-Side Components/Catalog Client Script/Smart Form Validation/script_include.js new file mode 100644 index 0000000000..a14099df60 --- /dev/null +++ b/Client-Side Components/Catalog Client Script/Smart Form Validation/script_include.js @@ -0,0 +1,56 @@ +var SmartFormValidationUtils = Class.create(); +SmartFormValidationUtils.prototype = { + initialize: function() {}, + + isValidEmail: function(email) { + if (!email) return false; + var re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return re.test(email); + }, + + isNumeric: function(val) { + if (val === null || val === undefined) return false; + return /^-?\d+(\.\d+)?$/.test(String(val)); + }, + + isFutureDate: function(dateStr) { + if (!dateStr) return false; + try { + var date = new GlideDateTime(dateStr); + var now = new GlideDateTime(); + return date.getNumericValue() > now.getNumericValue(); + } catch (e) { + return false; + } + }, + + passwordStrengthScore: function(password) { + if (!password) return 0; + var score = 0; + if (password.length >= 8) score++; + if (/[A-Z]/.test(password)) score++; + if (/[a-z]/.test(password)) score++; + if (/[0-9]/.test(password)) score++; + if (/[@$!%*#?&]/.test(password)) score++; + return score; // 0-5 + }, + + normalizePhoneNumber: function(phone) { + if (!phone) return ''; + return phone.replace(/[^0-9+]/g, ''); + }, + + // Simple IP address validator + isValidIP: function(ip) { + if (!ip) return false; + var parts = ip.split('.'); + if (parts.length !== 4) return false; + for (var i = 0; i < 4; i++) { + var p = parseInt(parts[i], 10); + if (isNaN(p) || p < 0 || p > 255) return false; + } + return true; + }, + + type: 'SmartFormValidationUtils' +}; \ No newline at end of file