Kieran Anson
Kilo Patron

Session: CCE5048 - Enhance your Catalog Items with Contextual Popups!

Hey There!

If you've wound up here after watching my session and wanted to see the nitty-gritty of how you can improve your service portal experience, keep reading. If you've found this article from the community, check out the session link above to see how you can use the spModal directive within catalogue client scripts to improve your end-user experience of your service portal.

 

The Bad & The Ugly

Calling this bad/ugly is probably a little harsh and hypocritical as I've used this option myself several times and it does the job. But that doesn't meet our needs of our customer nor does it fit in with the portal theming. 

The window.alert() and window.confirm() options just don't allow for a good customer journey as they depend on the browsers' implementation of the functions to which you could have additional text added which adds confusion for the user.

find_real_file.png

The Good

spModal is a lightweight wrapper for Angular UI bootstrap $uibModal that is used frequently in Service portal widgets to present various forms of popups. But did you know it can be used within a catalog client script!

 

spModal Alert

This is one of the simplest implementations and is a direct replacement for the window.alert() method. It takes in a message and will display a popup with the instance theming. 

function onLoad() {

	spModal.alert("You're not authorised to do this")
		.then(function(answer){
		console.log(answer);
	});
}

What's this .then() all about? All modals instantiate a promise which invokes the function within the .then() parameter on the closure of the modal. This allows you to do some funky things with the other spModal versions. For spModal.alert() this just returns true if the user clicks the ok button.

find_real_file.png

 

spModal Confirm

Similar to spModal.alert(), the confirm option has the same basic setup but with a cancel button in addition to the ok button. 

function onLoad() {

	spModal.confirm("You're not authorised to do this")
		.then(function(answer){
		//If they pressed ok
		console.log(answer);
	} , function(answer){
		//If they pressed cancel
		console.log("didn't accept/select ok");
	});
}

 

spModal Prompt

As the name might suggest, this option gives you the ability to prompt the user for a value which is then passed back to the promise for you to use. You can pass a default value that the user can overtype.

function onLoad() {

	spModal.prompt("What is your favourite colour?" , "Blue")
		.then(function(answer){
		//If they pressed ok - returns the prompt value or default value
		console.log(answer);
	} , function(answer){
		//If they pressed cancel
		console.log("didn't accept/select ok");
	});
}

 

spModal Open

This is by far my favourite option as it gives you direct access to the functionality available in $uibModal and greater flexibility. It might seem a little intimidating due to the number of options but it's definitely worth it.

 

KeyPurposeDefault
titleA string that can be HTML that goes in the header of the modalempty
messageA string that can be HTML that goes in the body.empty
buttonsAn array that contains the buttons to show on the modalCancel & Ok
inputBoolean - whether an input box should appear similar to confirmfalse 
valueA string default to appear in the inputempty
widgetA string or sys_id of a widget that is embedded into the modalempty
widgetInputAn object to send to the embedded widget as the input parameter available in its server scriptnull
sharedA client-side object to share data with the embedded widget client script 
sizeSize of the modal window. Options are here 'sm' or 'lg'calculated based on content
animatespecify whether the modal should ease in from the top of the windowtrue
backdropControls the presence of a backdrop.
  • true (Darkens the screen to focus the modal)
  • false (No backdrop)
  • static (Disables the ability to exit the modal by clicking outside its area)
true
keyboardIndicates whether the dialog should be closable by hitting the ESC keytrue
noDismissRemoves the header containing the X button to close the modal. Setting this to true also removes the header titlefalse

 

License Confirmation

An example used in the session demonstration was the ability to ask the user to actively confirm whether a licensed product is required. This spModal open uses the following object key:value pairs:

  • title - This sets the modal header text.
  • message - Sets the descriptive value in the body of the modal. In this example, I'm passing in the newValue of the license_type variable.
  • buttons - Ok & Cancel don't provide context and can be misinterpreted. The buttons used contain natural language along with using the primary option to set the button we want users to click.
  • backdrop - Using static prevents the user from clicking outside the modal and dismissing it.
  • keyboard - Setting this to false prevents the ESC key from closing the modal.

The promise sets the license_type to free if the primary button is selected. You can add another function to capture if the cancel/dismiss button is selected to perform another action.

function onChange(control, oldValue, newValue, isLoading) {
   if (isLoading || newValue == '') {
      return;
   }

	if(newValue == 'free') return;
	
	spModal.open({
		"title" : "Licensed Product",
		"message" : "Power BI " + newValue + " is a paid for product and will require approval",
		"buttons" : [
			{label: "✘ Downgrade to Free" , primary: true},
			{label: "✔ I understand the cost" , cancel: true}
		],
		"backdrop" : "static",
		"keyboard": false
	}).then(function(){
		g_form.setValue('license_type' , 'free');
	});
   
}

 

find_real_file.png

 

Terms & Conditions

I often see people add a checkbox to a form to ask people to confirm whether they agree to some sort of T's & C's or EULA. It's functional but isn't pretty and customers aren't going to be clicking on your hyperlink to a knowledge article to read them. This method intercepts the user ordering/submitting and prompts them to read it. If they accept, it'll submit the order for them, else it'll take them back to the page.

function onSubmit() {

	if (typeof spModal != 'undefined' && !g_form._hasConfirmed) {
		
		spModal.open({
			message: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ut consequat risus, vel maximus augue. Aliquam in velit ut justo consequat pharetra lacinia a magna. Donec egestas quis massa ut lobortis. Nullam non nisi dui. In euismod nisl at bibendum malesuada. Sed iaculis felis non iaculis viverra. Duis interdum felis mi, convallis porta lectus commodo quis. Nam aliquam nulla turpis, vitae mollis nisi facilisis et. Morbi vel augue vel enim pulvinar molestie in vitae neque. Ut eu vehicula odio, a condimentum tortor. Etiam viverra tincidunt nisi sed ultrices. Sed rutrum turpis non faucibus suscipit. Nunc aliquet ante non quam ullamcorper pulvinar. Ut sed dolor nec nulla euismod facilisis sed at eros. Ut in lectus et mauris consectetur vestibulum non sed magna.', 
			title: 'Agree to Terms & Conditions',
			buttons: [ 
				{label:'✘ Disagree', cancel: true},
				{label:'✔ I Accept', primary: true}
			]
		}).then(function(confirm){
			if (confirm){
				g_form._hasConfirmed = true;
				if (typeof g_form.orderNow != 'undefined') {
					g_form.orderNow();
				} else {
					g_form.submit();
				}
			}
		});
		
		return false;
	}
}

 

 find_real_file.png

 

Popup Widget

A bit more advanced, and for those who like to create service portal widgets. spModal supports passing in a widget and input parameter that will be displayed inside the modal window. This uses a custom widget, that is attached, and is designed to make picking a hierarchy location a lot easier.

function onChange(control, oldValue, newValue, isLoading) {
	if (isLoading || newValue == '') {
		return;
	}
	_callPopup();
	function _callPopup(){
		spModal.open({
			"title" : "Select Device Location",
			"widget" : "location-picker",
			"widgetInput" : {"account" : g_form.getValue('account')},
			"shared" : shared,
			"buttons": [
				{label:'✔ Confirm', primary: true}
			]
		});
	}

}

 find_real_file.png

 

Conclusion

spModal provides a great visual enhancement to your portal with only a small amount of effort required. If you're already creating a client script, you might as well use this instead of alert() or confirm()! If you any questions, don't hesitate to add them below and I'll endeavour to help out where I can.

 

Comments
manoj_s
Giga Contributor

How does spModal work in the desktop interface ?

Kieran Anson
Kilo Patron

Hi @manoj_s this functionality is only available in the Service Portal where the angular directive is available.

The SN Nerd
Giga Sage
Giga Sage

Surpised you didn't show any code in your preso to highlight how simple & easy it is to implement!

I'd go further and show the UI16 example using GlideModal. The old Alert() makes for a terrible UX.

Nice work!

manoj_s
Giga Contributor

Thanks @Kieran Anson  

Is there anyway we can throw the spModal for portal and Glidemodal for U16 using the client script. We use both U16 and Portal and will need to handle for both UI. 

I can build a script include that can identify and throw the message appropriately. Is there anyway to identify portal vs desktop ?

manoj_s
Giga Contributor

Thanks for the spModal functionality . It is really helpful. 

 

I was able to show the SpModal for location when the previous field changes.

Can you please let me know how the Location popup is shown by clicking on a reference field ?

How to set the value back to the Catalog variable ?

WazzaJC
Tera Expert

@Kieran Anson Hi Kieran. Thank you so much for sharing all these, I love your work, so very helpful.

Kieran, please can I ask for your help. I have tried following exactly what you have displayed here in terms of script and also the uploaded the Location Picker xml to my instance.

But when I try it on my Catalog Item, I get the following error message on Service Portal:

'There is a JavaScript error in your browser console'

When I check the Console error details, it points to one line as follows: 

'(g_env) [SCRIPT:EXEC] Error while running Client Script "Location Picker": ReferenceError: shared is not defined'

Can you help me determine what the issue could be. I have used only your scripts and custom Location Picker xml but it doesn't work on my Service Portal.

This is for the : 'Select Device Location' one.

Thanks so much Kieran, maybe you can help me with what I am doing wrong.

Kind Regards, Warwick

Matt Hernandez
Tera Guru

@WazzaJC You can omit the "shared" property from the spModal.open() command if you're not planning to use it. Its an optional property allowing you to pass something if you need to. If you click the link to spModal.open docs, which Kieran included just above where he listed out the table of "Key, Purpose, Default" you'll see the example code doesn't even include the "shared" parameter, because (1) docs are not comprehensive enough, but (2) its only for advanced use cases.

 

I would just omit that and see how it goes.

 

 

WazzaJC
Tera Expert

@Matt Hernandez 

Thanks very much, I do appreciate your advice. I did try that but still having issues getting this script to work properly unfortunately. Hopefully oneday @Kieran Anson may respond to my query and be able to advise further.

I do appreciate all the help on this.

Kind Regards, Warwick.

MGanon
Tera Guru

Can data entered by a user into a client-side spModel window be processes server-side by the system? After a service catalog item is submitted on behalf of a user that has no rights or roles. That user without roles needs to answer a question with Yes, No, or Maybe from the Service Portal. I need ServiceNow to record the answer as a variable associated with the request item.

Modal buttons look perfect, except the user selecting the appropriate button won't have rights to update the request item record.

taofeek93
Tera Contributor

Hi @Kieran Anson  thank you for sharing this.

 

I have on questions:

 

How can I embed a link in the message? Also, a bullet point.

 

Thanks once again.

Jagadish Sanadi
Kilo Sage

I am trying to show checkbox in pop up using spmodal, can you help me

reddy8055
Tera Contributor

How can I open Survey form using spmodal ?

Tim_Clark
Kilo Sage

Keiran, such a well written article, well done.

I just thought I would add some of my tips that I gathered from reading many an article, wish I had stumbled onto this one way sooner ðŸ˜Š

 

Tip 1: You can format the header using headerStyle.

 

spModal.open({
      "title": 'Close Order: ' + currentOrder.number,
      "message": 'Please confirm you wish to close this order.',
      "headerStyle": {
             'color': '#ffffff',
             'background-color': '#d9534f',
             'border-color': '#d9534f',
             'border-top-left-radius': '.6rem',
             'border-top-right-radius': '.6rem'
            },

 

  Tip 2: Add focus to a button using focus: true. By default it sets focus to the top right x. This would be OK except it has a hover message which ruins the look.

 

buttons: [
                 { label: '${Cancel}', focus: true, cancel: true, class: "btn btn-default" },
                 { label: '${Close Order}', primary: true, class: "btn btn-danger" }
               ]

 

 Output: Red Header with focus on the cancel button

image.png

The code is mine but the ideas are not, I just can't remember who I stole them from, hopefully it was not Keiran😟. Either way I think it is a good place to put this for the next reader. 

Isela Phelps
Tera Guru
Tera Guru

Thanks so much for this! 

Dave65
Tera Contributor

I tried the spModal.confirm in an onSubmit Client catalog script.

The dialog box displayed correctly but, it didn't pause the submission until one of the buttons were clicked.
I gave up and went back to the simple return confirm() I originally had.
From what read in you document above, the .then is needed because the spModal returns a promise so it's asynchronous. Is there a way to make it synchronous so it will wait for 1 of the buttons to be pressed?

 

This is my code.

    spModal.confirm('Please do not include any Personal Identifiable Information (PII) in the Brief and Detailed Description fields. <br></br> Click the OK button to submit. <br></br> Click the Cancel button to go back to the form.')
        .then(function(answer) {
                //If they pressed ok
                return answer; //true
            }, function(answer) {
                //If they pressed cancel
                return answer; //false
            });

 

Any suggestions?

Kieran Anson
Kilo Patron

@Dave65 yes you can block the submission. We use the accessible g_scratchpad object to store the validity of the submit request. Within spModal we then alter this value and submit the item programatically

 

function onSubmit() {

    //Allow submit
    if (typeof g_scratchpad.canSubmit != 'undefined' && g_scratchpad.canSubmit == true) {
        return true;
    }

    spModal.confirm(getMessage('PII_CAT_PROMPT'))
        .then(function(answer) {
			g_scratchpad.canSubmit = true;
			g_form.submit();
        }, function(answer) {
			g_scratchpad.canSubmit = false;
        });

	return false;

}

 

Dave65
Tera Contributor

Hi Kieran

Thanks for the help. You solution works great.

One more question; Is there a way to change the color of the text?
This is what I tried:

    spModal.confirm(<font color="red">Please do not include any Personal Identifiable Information (PII) in the Brief and Detailed Description fields. <br><br> Click the OK button to submit. <br> Click the Cancel button to go back to the form.</font>)

But I get the following error:
Could not save record because of a compile error: JavaScript parse error at line (7) column (22) problem = syntax error (<refname>; line 7) 

Please ignore, I finally figured it out, here's the code:

 message: '<strong><font color="#c41230" size="+1">Please confirm that no Personal Identifiable Information (PII) has been included in the Brief and Detailed Description fields</font></strong> <br><br> <font size="+1">Click the Submit button to submit the request. <br> Click the Cancel button to go back to the form.</font>',

The end result looks like this:

Dave65_0-1743605732267.png

 

 

Dave65
Tera Contributor

just noticed something about the spModel.open ('m not sure if it's also true about the spModel.confirm)
When the dialog box is open, I can click on the page and the dialog box closes.
Is there a way to prevent this?
I need the dialog box to be 508 compliant.

 

Thanks again for you help.

Please ignore, I missed the backdrop:"static" command.

Dave65
Tera Contributor

Hi Kieran

 

I tried to access your session but there doesn't seem to be a way to get to it. Do you know if ServiceNow has pulled it?

Kieran Anson
Kilo Patron

@Dave65 I believe ServiceNow only retain Knowledge presentations for a limited time. With K25 just around the corner, this is likely not available now. 

bsiva4567
Tera Contributor

How can we close this spModal automatically?

Kieran Anson
Kilo Patron

@bsiva4567 this use case isn't supported via spModal unfortunately as it doesn't expose a function to allow you to destroy the modal. 

Version history
Last update:
‎04-06-2021 08:03 AM
Updated by: