- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-27-2023 08:18 AM
I have a Multi Row variable set in a record producer, in this Variable set I have a variable named Total expenses.
When I create a ticket on service portal, i can set multiple rows in the variable set.
So, in a new Variable on the record producer (not in the varaiable set), I need to populate de SUM of the differents rows. For example:
I have created an "ON CHANGE" Client Script, to make this, but in this case I need another field to be changed, so I create a new "Calculate Total" Field to change.
This is the Client Script I made
The thing is that is not good looking in the form to have a Field to calculate total, so, I was thinking on another approach to calculate, may be it could be a buttom, or widget, so when I press this buttom, make the SUM.
The question is, How can I handle or get this buttom?
Thanks
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-27-2023 11:06 AM - edited 03-27-2023 11:12 AM
Unfortunately there is no real OOTB solution for this, just a workaround.
The reason why there is no easy OOTB solution are the following:
- in the Standard UI, the MRVS is a popup which is rendered via iframe and communication between MRVS (iframe) and Parent Form (parent frame) is technically only possible via events and string-data
- in the Service Portal, while you are not separated via iframes, you are still forced to communicate using events (broadcast them to $rootScope)
- there is a variable that was introduced some time ago called g_service_catalog which kind of acts as a "parent g_form", but it doesn't provide a setValue (yet)
- There are some undocumented ways of getting the parent's g_form, but those ways are inconsistent for Standard UI and Service Portal and are less-OOTB and less-clean to the workaround I present below
The easiest and cleanest workaround is based on the CustomEvent, which is exposed to even scoped Client Scripts.
You need the following three client scripts:
1.) MRVS onLoad: Listen on the "MRVS onChange" event and transmit it to the parent window
2.) MRVS onSubmit: Broadcast the "MRVS onChange"
Note: Because we need the parent window, we use a small "hack" to get a handle to the "window" variable. This is a necessary evil.
3.) Catalog Item onLoad: Register on "MRVS changed" callback and update the total
In Detail:
1.) MRVS onLoad script:
function onLoad() {
var $window;
CustomEvent.on('x_scoped_mrvs_submitted', function () {
if ($window) {
// This is where the magic happens: let the parent g_form know of the MRVS item update.
// Because we have to broadcast this event to window.parent, we need some way get a handle to
// the window object.
// Unfortunately a setTimeout in the 'onSubmit' function is too late as the iframe is already
// destroyed when the setTimeout-callback is executed.
$window.parent.CustomEvent.fire('x_scoped_update_total');
}
});
setTimeout(function () {
$window = this;
});
}
2.) MRVS onSubmit:
function onSubmit() {
CustomEvent.fire('x_scoped_mrvs_submitted');
}
3.) Catalog Item onLoad:
function onLoad() {
CustomEvent.on('x_scoped_update_total', function () {
setTimeout(function () {
var total = JSON.parse(g_form.getValue('booking_details'))
.map(function (x) { return parseFloat(x.total_expense); })
.reduce(function (a, b) { return a + b; }, 0);
g_form.setValue('grand_total', total);
});
});
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-27-2023 11:06 AM - edited 03-27-2023 11:12 AM
Unfortunately there is no real OOTB solution for this, just a workaround.
The reason why there is no easy OOTB solution are the following:
- in the Standard UI, the MRVS is a popup which is rendered via iframe and communication between MRVS (iframe) and Parent Form (parent frame) is technically only possible via events and string-data
- in the Service Portal, while you are not separated via iframes, you are still forced to communicate using events (broadcast them to $rootScope)
- there is a variable that was introduced some time ago called g_service_catalog which kind of acts as a "parent g_form", but it doesn't provide a setValue (yet)
- There are some undocumented ways of getting the parent's g_form, but those ways are inconsistent for Standard UI and Service Portal and are less-OOTB and less-clean to the workaround I present below
The easiest and cleanest workaround is based on the CustomEvent, which is exposed to even scoped Client Scripts.
You need the following three client scripts:
1.) MRVS onLoad: Listen on the "MRVS onChange" event and transmit it to the parent window
2.) MRVS onSubmit: Broadcast the "MRVS onChange"
Note: Because we need the parent window, we use a small "hack" to get a handle to the "window" variable. This is a necessary evil.
3.) Catalog Item onLoad: Register on "MRVS changed" callback and update the total
In Detail:
1.) MRVS onLoad script:
function onLoad() {
var $window;
CustomEvent.on('x_scoped_mrvs_submitted', function () {
if ($window) {
// This is where the magic happens: let the parent g_form know of the MRVS item update.
// Because we have to broadcast this event to window.parent, we need some way get a handle to
// the window object.
// Unfortunately a setTimeout in the 'onSubmit' function is too late as the iframe is already
// destroyed when the setTimeout-callback is executed.
$window.parent.CustomEvent.fire('x_scoped_update_total');
}
});
setTimeout(function () {
$window = this;
});
}
2.) MRVS onSubmit:
function onSubmit() {
CustomEvent.fire('x_scoped_mrvs_submitted');
}
3.) Catalog Item onLoad:
function onLoad() {
CustomEvent.on('x_scoped_update_total', function () {
setTimeout(function () {
var total = JSON.parse(g_form.getValue('booking_details'))
.map(function (x) { return parseFloat(x.total_expense); })
.reduce(function (a, b) { return a + b; }, 0);
g_form.setValue('grand_total', total);
});
});
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-28-2023 07:06 AM - edited 03-28-2023 08:43 AM
Great!!! Thanks!!!
Just a question:
When you´re charging records, the fields work as expected.
But when you DELETE a record in the varaible set, the GRAND TOTAL is NOT changing.
Any Idea to modify this behavior?
Thanks again!!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-29-2023 08:59 AM - edited 03-29-2023 09:01 AM
I've taken a look at the ServiceNow Standard UI + Service Portal implementation, and unfortunately it seems that we cannot do it in any upgrade safe manner.
Some observations:
- GlideForm::onUserChangeValue triggers on Row Update, Row Create on Service Portal, but not on Row Removal
- GlideForm::onUserChangeValue does not trigger at all in Standard UI
- Standard UI uses the Utility Class TableVariableService which unfortunately doesn't propagate Row Create/Update/Removal to the parent GlideForm (g_form)
The only way to properly handle this is to by repeatedly parsing the variable set:
Catalog Item onLoad Script:
setInterval(function () {
var total = JSON.parse(g_form.getValue('booking_details'))
.map(function (x) { return parseFloat(x.total_expense); })
.reduce(function (a, b) { return a + b; }, 0);
g_form.setValue('grand_total', total);
}, 50);
Note: This makes the previously posted Client Scripts obsolete...