Skip to content
Merged
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,114 @@
# Auto Save Draft Feature for Catalog Items

This snippet provides automatic draft saving functionality for ServiceNow Catalog Items, helping prevent data loss by automatically saving form data at regular intervals.

## Overview

The feature includes two implementations:
1. Basic Implementation (`basic_implementation.js`)
2. Advanced Implementation (`advanced_implementation.js`)

## Basic Implementation

### Features
- Auto-saves form data every minute
- Stores single draft in sessionStorage
- Provides draft restoration on form load
- Basic error handling and user feedback

### Usage
```javascript
// Apply in Catalog Client Script
// Select "onLoad" for "Client script runs"
// Copy content from basic_implementation.js
```

## Advanced Implementation

### Enhanced Features
- Multiple draft support (keeps last 3 drafts)
- Advanced draft management
- Draft selection dialog
- Detailed metadata tracking
- Improved error handling
- User-friendly notifications

### Usage
```javascript
// Apply in Catalog Client Script
// Select "onLoad" for "Client script runs"
// Copy content from advanced_implementation.js
```

## Technical Details

### Dependencies
- ServiceNow Platform UI Framework
- GlideForm API
- GlideModal (advanced implementation only)

### Browser Support
- Modern browsers with sessionStorage support
- ES5+ compatible

### Security Considerations
- Uses browser's sessionStorage (cleared on session end)
- No sensitive data transmission
- Instance-specific storage

## Implementation Guide

1. Create a new Catalog Client Script:
- Table: Catalog Client Script [catalog_script_client]
- Type: onLoad
- Active: true

2. Choose implementation:
- For basic needs: Copy `basic_implementation.js`
- For advanced features: Copy `advanced_implementation.js`

3. Apply to desired Catalog Items:
- Select applicable Catalog Items
- Test in dev environment first

## Best Practices

1. Testing:
- Test with various form states
- Verify draft restoration
- Check browser storage limits

2. Performance:
- Default 60-second interval is recommended
- Adjust based on form complexity
- Monitor browser memory usage

3. User Experience:
- Clear feedback messages
- Confirmation dialogs
- Error notifications

## Limitations

- Browser session dependent
- Storage size limits
- Form field compatibility varies

## Troubleshooting

Common issues and solutions:
1. Draft not saving
- Check browser console for errors
- Verify sessionStorage availability
- Check form modification detection

2. Restoration fails
- Validate stored data format
- Check browser storage permissions
- Verify form field compatibility

## Version Information

- Compatible with ServiceNow: Rome and later
- Browser Requirements: Modern browsers with ES5+ support
- Last Updated: October 2025
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/**
* Advanced Auto-save Draft Implementation with Enhanced Features
* This version adds multi-draft support and advanced error handling
*/

function onLoad() {
var autosaveInterval = 60000; // 1 minute
var maxDrafts = 3; // Maximum number of drafts to keep

// Initialize draft manager
initializeDraftManager();

// Try to restore previous draft
restoreLastDraft();

// Set up auto-save interval
setInterval(function() {
if (g_form.isModified()) {
saveAdvancedDraft();
}
}, autosaveInterval);
}

function initializeDraftManager() {
window.draftManager = {
maxDrafts: 3,
draftPrefix: 'catalogDraft_' + g_form.getUniqueValue() + '_',

getAllDrafts: function() {
var drafts = [];
for (var i = 0; i < sessionStorage.length; i++) {
var key = sessionStorage.key(i);
if (key.startsWith(this.draftPrefix)) {
drafts.push({
key: key,
data: JSON.parse(sessionStorage.getItem(key))
});
}
}
return drafts.sort((a, b) => b.data.timestamp - a.data.timestamp);
},

cleanup: function() {
var drafts = this.getAllDrafts();
if (drafts.length > this.maxDrafts) {
drafts.slice(this.maxDrafts).forEach(function(draft) {
sessionStorage.removeItem(draft.key);
});
}
}
};
}

function saveAdvancedDraft() {
try {
var draftData = {};
g_form.serialize(draftData);

// Add metadata
var draftKey = window.draftManager.draftPrefix + new Date().getTime();
var draftInfo = {
timestamp: new Date().getTime(),
data: draftData,
user: g_user.userName,
catalog_item: g_form.getTableName(),
fields_modified: g_form.getModifiedFields()
};

sessionStorage.setItem(draftKey, JSON.stringify(draftInfo));
window.draftManager.cleanup();

// Show success message with draft count
var remainingDrafts = window.draftManager.getAllDrafts().length;
g_form.addInfoMessage('Draft saved. You have ' + remainingDrafts + ' saved draft(s).');

} catch (e) {
console.error('Error saving draft: ' + e);
g_form.addErrorMessage('Failed to save draft: ' + e.message);
}
}

function restoreLastDraft() {
try {
var drafts = window.draftManager.getAllDrafts();

if (drafts.length > 0) {
// If multiple drafts exist, show selection dialog
if (drafts.length > 1) {
showDraftSelectionDialog(drafts);
} else {
promptToRestoreDraft(drafts[0].data);
}
}
} catch (e) {
console.error('Error restoring draft: ' + e);
g_form.addErrorMessage('Failed to restore draft: ' + e.message);
}
}

function showDraftSelectionDialog(drafts) {
var dialog = new GlideModal('select_draft_dialog');
dialog.setTitle('Available Drafts');

var html = '<div class="draft-list">';
drafts.forEach(function(draft, index) {
var date = new Date(draft.data.timestamp).toLocaleString();
html += '<div class="draft-item" onclick="selectDraft(' + index + ')">';
html += '<strong>Draft ' + (index + 1) + '</strong> - ' + date;
html += '<br>Modified fields: ' + draft.data.fields_modified.join(', ');
html += '</div>';
});
html += '</div>';

dialog.renderWithContent(html);
}

function promptToRestoreDraft(draftInfo) {
var timestamp = new Date(draftInfo.timestamp);
if (confirm('A draft from ' + timestamp.toLocaleString() + ' was found. Would you like to restore it?')) {
Object.keys(draftInfo.data).forEach(function(field) {
g_form.setValue(field, draftInfo.data[field]);
});
g_form.addInfoMessage('Draft restored from ' + timestamp.toLocaleString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* Basic Auto-save Draft Implementation
* This version provides core functionality for auto-saving catalog item form data
*/

function onLoad() {
var autosaveInterval = 60000; // 1 minute

// Try to restore previous draft
restoreLastDraft();

// Set up auto-save interval
setInterval(function() {
if (g_form.isModified()) {
saveDraft();
}
}, autosaveInterval);
}

function saveDraft() {
try {
var draftData = {};
g_form.serialize(draftData);

var draftKey = 'catalogDraft_' + g_form.getUniqueValue();
sessionStorage.setItem(draftKey, JSON.stringify({
timestamp: new Date().getTime(),
data: draftData
}));

g_form.addInfoMessage('Draft saved automatically');
} catch (e) {
console.error('Error saving draft: ' + e);
}
}

function restoreLastDraft() {
try {
var draftKey = 'catalogDraft_' + g_form.getUniqueValue();
var savedDraft = sessionStorage.getItem(draftKey);

if (savedDraft) {
var draftData = JSON.parse(savedDraft);
var timestamp = new Date(draftData.timestamp);

if (confirm('A draft from ' + timestamp.toLocaleString() + ' was found. Would you like to restore it?')) {
Object.keys(draftData.data).forEach(function(field) {
g_form.setValue(field, draftData.data[field]);
});
g_form.addInfoMessage('Draft restored from ' + timestamp.toLocaleString());
} else {
sessionStorage.removeItem(draftKey);
}
}
} catch (e) {
console.error('Error restoring draft: ' + e);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Auto-save draft feature for Catalog Client Script
*
* This script automatically saves form data as a draft in the browser's sessionStorage
* every minute if changes are detected. It also provides functionality to restore
* the last saved draft when the form is loaded.
*/

// Executes when the form loads
function onLoad() {
var autosaveInterval = 60000; // 1 minute

// Try to restore previous draft
restoreLastDraft();

// Set up auto-save interval
setInterval(function() {
if (g_form.isModified()) {
saveDraft();
}
}, autosaveInterval);
}

// Saves the current form state as a draft
function saveDraft() {
try {
var draftData = {};
g_form.serialize(draftData);

var draftKey = 'catalogDraft_' + g_form.getUniqueValue();
sessionStorage.setItem(draftKey, JSON.stringify({
timestamp: new Date().getTime(),
data: draftData
}));

g_form.addInfoMessage('Draft saved automatically');
} catch (e) {
console.error('Error saving draft: ' + e);
}
}

// Restores the last saved draft if available
function restoreLastDraft() {
try {
var draftKey = 'catalogDraft_' + g_form.getUniqueValue();
var savedDraft = sessionStorage.getItem(draftKey);

if (savedDraft) {
var draftData = JSON.parse(savedDraft);
var timestamp = new Date(draftData.timestamp);

// Ask user if they want to restore the draft
if (confirm('A draft from ' + timestamp.toLocaleString() + ' was found. Would you like to restore it?')) {
Object.keys(draftData.data).forEach(function(field) {
g_form.setValue(field, draftData.data[field]);
});
g_form.addInfoMessage('Draft restored from ' + timestamp.toLocaleString());
} else {
// Clear the draft if user chooses not to restore
sessionStorage.removeItem(draftKey);
}
}
} catch (e) {
console.error('Error restoring draft: ' + e);
}
}
Loading