‎04-30-2022 03:25 AM - edited ‎06-26-2023 02:12 PM
To enable Client Side UI Actions, the following has to be considered:
api.controller=function() { /* widget controller */ var c = this; c.buttonClicked = function (action, g_form) { if (action.action_name.includes('delete')) { return confirm('Are you sure you want to delete this record?'); } else if (action.sys_id == '<sys_id_of_my_ui_action') { // do stuff like spModal.open } }; };
Link:
function link(scope, element, attrs, controller) { var formWidgetSysId = 'fd1f4ec347730200ba13a5554ee490c0'; var formElement = element .parent() .closest('div') // column .find('.v' + formWidgetSysId); if (formElement.length === 0) { // form widget not yet loaded, try again in 50ms setTimeout(link.apply.bind(link, null, arguments), 50); return; } // save the OOTB triggerUIAction and replace it with our own (that calls client side code) var formScope = formElement.scope(); var ootbTriggerUIAction = formScope.triggerUIAction; formScope.triggerUIAction = function (action) { var g_form = getGForm(formElement); var result = controller.buttonClicked(action, g_form); if (result || result === undefined) { return ootbTriggerUIAction(action); } }; function getGForm(formElement) { return formElement .find('div[form_model]') // this contains the actual form (sp-model) .children() .last() // execItemScripts() div which contains the getGlideForm-function .scope() .getGlideForm(); } }
Enable the Buttons on Service Portal Form Page:
The Page Record should now look like this:
The Client Controller is prepared with an example that shows you how to add code, GlideAjax is also supported, however if you want to add asynchronous calls, you have to do a few modifications to the Widget (hint: use $q).
If you want the whole "Batteries included"-Package, please take a look at the "Swiss Tool" in the official ServiceNow App Store, which includes (just a small extract):
Thank you for this write-up!
Have a question about this part
The Client Controller is prepared with an example that shows you how to add code, GlideAjax is also supported, however if you want to add asynchronous calls, you have to do a few modifications to the Widget (hint: use $q).
I would have thought that GlideAjax is not possible in a widget due to this KB article.
Would you mind providing a short example of how to include GlideAjax in the Client Controller? I know how GlideAjax looks like otherwise so an example of the server-side Script Include is not needed.
GlideAjax calls to scoped script includes are affected by this, the workaround is quite simple:
var ga = new GlideAjax('SCOPE.SCRIPTINCLUDE').setScope('SCOPE');
Using a simple .setScope makes your GlideAjax call work also to scoped script includes 🙂
Thank you Markus Kraus for that tip. Another question was this you wrote above: hint: use $q
How can $q be used with GlideAjax?
You'd have to do some modifications to the link-function:
var $q = formElement.injector().get('$q');
formScope.triggerUIAction = function (action) {
var g_form = getGForm(formElement);
var result = controller.buttonClicked(action, g_form);
if (result || result === undefined) {
$q.resolve(result)
.then(ootbTriggerUIAction.bind(formScope, action));
}
};
and in the buttonClicked function you can then do an GlideAjax call like this:
c.buttonClicked = function (action, g_form) {
var deferred = $q.defer();
var ga = new GlideAjax('MyScriptInclude');
// ...
ga.getAnswerXML(function (result) {
deferred.resolve();
});
return deferred.promise;
};
Note: The whole widget is just a very primitive way of implementing support for client side UI Actions in the service portal.
This post does not give a "batteries included" solution to it. This is by design, if you want the batteries, you have to buy them (take a look at Swiss Tool - it has a lot more features than just Client Side UI Actions in the portal).
The goal of this post is to give developers an alternative approach to cloning the OOTB widget.
The reason why i posted this is, that the cloning is a potential security and maintenance risk that most developers are not aware of. When using the technique described here, you still have to some parts of the implementation on your own.
If you are not able to implement it, you are probably also not able to maintain the item (the same applies to the cloning the form widget). In this case you are well advised to use tools that are maintained by an external supplier.
Very cool, thank you for the explanation!
Thank you for the write-up! Does this work in Tokyo and Utah? I created my sibling widget exactly as you described and added it to the Form by going through sp_form.list and then sp_instance.list --> create new instance. I see the Client Side Buttons widget is now on my Form page, just below the Form widget.
Unfortunately, when I open the form in the Portal, none of my client-side buttons appear. I added an "alert" call to both the Client Controller and Link function. I see both the Client Controller and Link functions are running, and no errors are thrown in the browser JavaScript console.
Unfortunately, only my non-client buttons are appearing, which was the case before implementing this widget. Is there anything I may have missed? I also tried implementing in both the Global scope and a custom app scope, and the behavior is the same. Our ServiceNow instances are currently Tokyo; my PDI is Utah so I tested there as well without success.
Your example above only shows Client Controller and Link functions; do I need to add anything to the HTML Template or CSS? My thought was that since this is a sibling widget it "piggybacks" off the default Form widget.
Matt
Thank you for the write-up! Does this work in Tokyo and Utah? I created my sibling widget exactly as you described and added it to the Form by going through sp_form.list and then sp_instance.list --> create new instance. I see the Client Side Buttons widget is now on my Form page, just below the Form widget.
Unfortunately, when I open the form in the Portal, none of my client-side buttons appear. I added an "alert" call to both the Client Controller and Link function. I see both the Client Controller and Link functions are running, and no errors are thrown in the browser JavaScript console.
Unfortunately, only my non-client buttons are appearing, which was the case before implementing this widget. Is there anything I may have missed? I also tried implementing in both the Global scope and a custom app scope, and the behavior is the same. Our ServiceNow instances are currently Tokyo; my PDI is Utah so I tested there as well without success.
Your example above only shows Client Controller and Link functions; do I need to add anything to the HTML Template or CSS? My thought was that since this is a sibling widget it "piggybacks" off the default Form widget.
If I create non-client versions of my UI Actions, with just server-side code in them, then your widget works perfectly! My UI Actions run in native with a mix of client and server-side code, hence my desire to have the same functionality in Service Portal.
Matt
Hi @Matt S6 Please undo all changes, and fork the following repository:
https://github.com/kr4uzi/ServiceNow-Portal-Experience
EDIT: In regards to your original question: This technology runs purely on the OOTB Form Widget - and the OOTB form widget simply doesn't load any UI Actions with "client = true". There is also no way to make certain Classic UI technology like GlideModal work on the Service Portal. This means you *must* go with client = false (and "portal = true" if you're using the repository above).
You will only need to fork the repository and import it "from source control" using the ServiceNow studio.
No need to do any modifications to a page, create widgets and so on. The following will be available:
1.) Client Side UI Actions (there will be a new UI Section called "Portal" and you can choose one of many templates (just enable the form tables by clicking the three dots on the form header)
2.) Form Annotations: Annotations will also load
3.) Data Table Field Styles (Note: Also works with a new Section "Portal" on the Field Style Form)
4.) Data Table UI Actions (same as form, but for Data Table Widgets)
Note: No customization have to be done on any OOTB artefacts. This repository has been tested with Utah and is proven to work. You can also download the Portal Experience Verification repository on my github if you want automated verification of the features above.
More details on:
Thank you for the feedback. Indeed I was able to get this working by using "client = false". I had to duplicate my buttons (UI Actions) because I have in the native UI the actions run both client and server code. The new UI Action buttons, which are set to "client = false", run server-side code only. The client-side code I've put into the client controller in the c.buttonClicked function.
Basically what I'm doing is providing the user confirmation based on their button clicks, asking them to confirm what they're doing. I've tweaked the conditions of the UI Actions to examine the URL to determine whether the user is interacting with the Portal UI vs. the native UI, so only the appropriate buttons are displayed.
Kindest regards,
Matt