Skip to content
Closed
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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 = '<div class="validation-summary">';
html += '<p>Please correct the following fields:</p>';
html += '<ul>';

invalidFields.forEach(function(fieldName) {
var label = g_form.getLabel(fieldName);
html += '<li>' + label + '</li>';
});

html += '</ul></div>';
dialog.renderWithContent(html);
},

type: 'SmartFormValidator'
};
Original file line number Diff line number Diff line change
@@ -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'
};
Loading