- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-27-2019 12:15 PM
In our new Service Portal, we have a standard SP Header. When we load a Catalog item or other content which takes inputs, and you add input, the navigation lets you leave immediately without asking if you want to leave. Whereas the browser asks if you want to leave the site.
I looked at converting the menu items from type page to type url and then using the full url. That does not seem ideal.
Anyone have a suggestion on how to make the navigation ask you if you want to leave when you have added input to a ServiceNow form?
Solved! Go to Solution.
- 4,543 Views
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-09-2019 09:46 AM
OK, I think I understand now your main problem. My short answer: the usage of iframe seems only good at the first glance. On the other side it's really the worst implementation for Service Portal integration because of security of iframes. One have to port the application to Service Portal using built-in or custom widgets.
Try to open https://dev77499.service-now.com/sp?sys_id=85071a1347c12200e0ef563dbb9a71c1&id=form&table=incident or https://dev77499.service-now.com/sp?sys_id=1c741bd70b2322007518478d83673af3&id=form&table=incident, which uses Service Portal page with id=form, which uses Form widget. It loads incidents, apply UI Policy and Client Scripts of incidents. Some applied scripts are common for "classical" UI and Service Portal. Other are different for both platforms (Desktop and Mobile/Service Portal). It's the recommended way.
I understand that migration of large application created for "classical" UI to Service Portal isn't easy. But I see no alternatives. We made the work for our custom application. It took time, but it was really required for creating Service Portal application, which can be used really productive. It's my personal opinion. Sorry, for the bad news.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-28-2019 02:33 AM
The functionality, which you described works as following. Before changing of the URL angular-route, $location or $browser broadcast "$locationChangeStart" event. There are some places in new code of ServiceNow, where the handler of the event will be registered and in the way the warning can be displayed, which allows to stop URL change.
The handler looks about the following
$rootScope.$on('$locationChangeStart', function(event, next) {
event.preventDefault();
if (someTestForDirtyData()) {
spModal.open({
buttons: [{
label: i18n.getMessage("Cancel"),
value: "cancel"
}, {
label: i18n.getMessage("Leave"),
primary: true,
value: "leave"
}]
}).then(function(confirm) {
if (confirm.value == "leave") {
...;
$window.location = next;
}
});
} else
$window.location = next;
});
There are exist two implementations of the functionality: one - in SC Catalog Item [widget-sc-cat-item-v2] and SC Order Guide [widget-sc-order-guide-v2] widget (by usage of factory spSCNavStateManager) and another in Form widget [widget-form] und Data Table [widget-data-table] (by usage of factory spNavStateManager). In both cases one use GlideForm internally to track dirty fields.
It's important to know that GlideForm hold internally the list of fields, which were changed (inside of internal _userModifiedFields object). If you know that changes in the form should be ignored then you can call $private.userState.clearModifiedFields() method of g_form.
To make my answer more full I append it with the current code of spSCNavStateManager and spNavStateManager directives (from Madrid patch 4):
/*! RESOURCE: /scripts/app.$sp/service_catalog/service.spSCNavStateManager.js */
angular.module('sn.$sp').factory('spSCNavStateManager', function($rootScope, $window, spModal, i18n) {
'use strict';
var registeredForms = {};
function registerForm(form) {
registeredForms[form.getSysId()] = form;
}
function unregisterForms(sysIds) {
sysIds.forEach(function(sysId) {
delete registeredForms[sysId];
});
}
function clearUserModifiedFields() {
var includedForms = Object.keys(registeredForms);
includedForms.forEach(function(includedForm) {
if (registeredForms[includedForm].isUserModified())
registeredForms[includedForm].$private.userState.clearModifiedFields();
});
}
function checkForDirtyForms() {
var isFormDirty = false;
var includedForms = Object.keys(registeredForms);
for (var i in includedForms)
if (registeredForms[includedForms[i]].isUserModified()) {
isFormDirty = true;
break;
}
return isFormDirty;
}
$rootScope.$on('$locationChangeStart', function(event, next) {
event.preventDefault();
if (checkForDirtyForms()) {
spModal.open({
title: i18n.getMessage("Leave page?"),
headerStyle: {
border: 'none',
'padding-bottom': 0
},
footerStyle: {
border: 'none',
'padding-top': 0
},
message: i18n.getMessage("Changes you made will be lost."),
buttons: [{
label: i18n.getMessage("Cancel"),
value: "cancel"
}, {
label: i18n.getMessage("Leave"),
primary: true,
value: "leave"
}]
}).then(function(confirm) {
if (confirm.value == "leave") {
clearUserModifiedFields();
$window.location = next;
}
});
} else
$window.location = next;
});
$window.onbeforeunload = function(event) {
if (checkForDirtyForms())
event.returnValue = "";
}
return {
register: registerForm,
unregisterForms: unregisterForms
}
});
and
/*! RESOURCE: /scripts/app.$sp/service.spNavStateManager.js */
angular.module('sn.$sp').factory('spNavStateManager', function($rootScope, $window, spConf, $q, spModal, i18n) {
'use strict';
var registeredForms = {};
function registerForm(tableName, saveFunction, g_form) {
registeredForms[tableName] = {
'saveFunc': saveFunction,
'g_form': g_form
};
return registeredForms[tableName];
}
function onRecordChange() {
var dirtyFormsArray = _getDirtyArrayFromArgs();
if (dirtyFormsArray.length !== 0) {
return showModal(dirtyFormsArray);
} else {
return $q.when(true);
}
}
function showModal(dirtyTableNames) {
return spModal.open({
title: i18n.getMessage("Save Changes"),
headerStyle: {
border: 'none',
'padding-bottom': 0
},
footerStyle: {
border: 'none',
'padding-top': 0
},
message: i18n.getMessage("Do you want to save your changes before leaving this page?"),
buttons: [{
label: i18n.getMessage("Discard"),
value: "discard"
}, {
label: i18n.getMessage("Save"),
primary: true,
value: "save"
}]
}).then(function(confirm) {
if (confirm.value == "save") {
for (var tn in dirtyTableNames) {
var tableName = dirtyTableNames[tn];
registeredForms[tableName].saveFunc();
}
}
for (var i in dirtyTableNames) {
var tableName = dirtyTableNames[i];
registeredForms[tableName].g_form.$private.userState.clearModifiedFields();
}
});
}
function _getDirtyArrayFromArgs() {
var dirtyFormsArray = [];
var tables = Array.prototype.slice.call(arguments, 0);
if (tables.length === 0) {
tables = Object.keys(registeredForms);
}
for (var t in tables) {
var tableName = tables[t];
if (registeredForms[tableName] && registeredForms[tableName].g_form.isUserModified()) {
dirtyFormsArray.push(tableName);
}
}
return dirtyFormsArray;
}
function _getDirtyTables() {
var dirtyTables = [];
Object.keys(registeredForms).forEach(function(tableName) {
var form = registeredForms[tableName];
if (form.g_form.isUserModified()) {
dirtyTables.push(tableName);
}
});
return dirtyTables;
}
$rootScope.$on('$locationChangeStart', function(event, next) {
var dirtyTables = _getDirtyTables();
if (dirtyTables.length > 0) {
event.preventDefault();
showModal(dirtyTables).then(function() {
window.location = next;
});
}
});
$window.onbeforeunload = function(event) {
var dirtyTables = _getDirtyTables();
if (dirtyTables.length > 0) {
event.returnValue = "";
}
}
return {
_getDirtyArrayFromArgs: _getDirtyArrayFromArgs,
onRecordChange: onRecordChange,
register: registerForm
}
});
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-09-2019 06:37 AM
Hi OlegKi, thank you for detailed response! So I understand what you have said but I dont know exactly how to fix it in our system.
So I have
- a home page
- a link on the home page to a portal page with an iframe holding an native ServiceNow app & the portal navigation.
- In there, when something is done, we want the portal navigation to ask "are you sure you want to leave"
- So what do i have to modify to make that work?
- The navigation? Where would I put the code? I am kind of lost, thank you for feedback.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-09-2019 07:34 AM
Sorry, but I'm not sure that I understand you correctly. Your original question was about Service Portal development. In your last post you write about "a home page", which has some link to portal page. What you mean under the "home page"? Do you have some external web site, which reference Service Portal or you mean the home page of the service portal? Another thing sound very strange: "iframe holding an native ServiceNow app". What you mean under native ServiceNow app? Is it native mobile application created with Mobile studio or you mean "classical" UI Page of ServiceNow? "Apps" and especially "native apps" will be used practically only in context of applications created for mobile devices.
In any way why you load something in iframe? The goal of iframe is maximal separation the content of iframe from the main page. So the application from iframe have almost no chance to access to the parent page (the home page). In the same way access to content of iframe is very restrictive from the parent page. What kind of information you need to display in the iframe? If the "home page" is the home page of Service Portal then probably some service portal widget exists, which can display the same information without usage iframes?
Could you describe more exact the scenario which you have? Where some data will be modified: in home page (in Service Portal or somewhere else) or inside of iframe? Where you want to display the warning "Leave Site: Changes you made may not be saved."? (inside of iframe or on the main frame)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-09-2019 08:36 AM
Please do these things:
1) Go here: https://dev77499.service-now.com/sp
2) Logon: U: olegki P: olegki
3) Click "Click here please" icon link.
The following page will be a portal page, with an widget that embeds an iframe. I landed on that design because our actual custom application(in this example i put in incident) has many advanced functionality and the form widget could not fulfill the need. We are also on a short timeline. I am open to other design ideas if you have one.
4) To see the problem, update some of the fields in the embedded iframe, and then click the banner nav (knowledge, catalog, system status, ect....)