- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
on 10-02-2020 04:11 AM
In this article I want to share one of the frequent and complex requirement we usually get in Service Catalog forms(Service Portal).
which is Dynamic HTML tables on Catalog forms with Add and Remove rows.
Requirement:
We need to display a table (ex: multi row variable set ) on a catalog form and when user fills a variable (select item) and provides its quantity we need to calculate the price and update the total value as show in below screenshot.
For the above scenario we can
Possibility 1 (Using Custom widget):
1.For Service Portal we can, Develop custom widget and embed it on the catalog form with all the functionalities that are available with Multi row variable set.
2. For ServiceNow Native UI we need to create a macro to re render all the information provided by the user
3. It will take certain time to develop and we need to handle lot of things. But we will have flexibility in doing multiple things as it is custom built.
Possibility 2 (Using Multi row variable set):
For mentioned use case, Using multi row variable set might not be that fruitful due to below issues
1. It wont support capability of accessing other form variables and we cant display separate row with Total as shown in above screenshot
2. When user delete rows by either clicking Remove All or individual remove row buttons,we cannot track those deletes and update our calculations accordingly.
Refer: ServiceNow Docs
My Approach
I tried to figure out an approach which uses the combination of Multi row variable set and a custom widget to achieve mentioned functionality without complex logic or any DOM manipulations.
Solution:
1. Create a multi row variable set with all the necessary variables, In our case Select Item, quantity and price.
2. Create widget and add it to the catalog item as a variable of type Macro
3. In the client script of the widget, try to watch for the changes in the multi row variable set using angular js component ($scope.$watch) as shown below
$scope.$watch(function(){
/* item_prices - Internal Name of multi row variable set */
return $scope.page.g_form.getValue('item_prices');
},function(val){
// Business Logic
});
4. When there are changes , We can calculate the total price by retrieving the value of entire multi row variable set.
example:
[
{
"quantity":"10",
"select_item":"laptop",
"price":"10000"
},
{
"quantity":"30",
"select_item":"keyboard",
"price":"600"
},
{
"quantity":"2",
"select_item":"mouse",
"price":"20"
}
]
5. We can populate the calculated price in any variable out side multi row variable set
OR
We can bind the value to any html element on the widget.
Refer to end result in below screenshot.
Widget Reference:
Body HTML:
<div>
<div ng-if="c.totalPrice" class="total_class pull-right">
<div class="total_text"> Total: </div>
<div> <span class="badge badge-secondary total_value">{{c.totalPrice }} </span> </div>
</div>
</div>
Client Script:
api.controller=function($scope) {
/* widget controller */
var c = this;
c.totalPrice ='';
$scope.$watch(function(){
return $scope.page.g_form.getValue('item_prices');
},function(val){
c.totalPrice ='';
$scope.page.g_form.setValue('total_price','');
if(val !=''){
var obj = JSON.parse(val);
var tPrice=0;
for(var i in obj)
tPrice =parseInt(tPrice)+parseInt(obj[i].price);
if(tPrice!='' && tPrice>0){
c.totalPrice =tPrice;
$scope.page.g_form.setValue('total_price',tPrice);
}
}
});
};
Attached working demo for reference.
- 5,014 Views

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi Shravani,
Thank you very much for this post!
But for some reason, I can't make it work for my needs (I'm in New York version). When I pasted the code you shared for the client script side on the Widget, SN throws me an error message:
Any idea how can I fix it?
Thanks in advance!
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
hi @Ramiro Rincon Barraza,
Sorry, I missed you comment. It didn't work because of the client controller function call format. "api.controller" pattern is used in Paris. Please try to change the code as below.
function() {
/* widget controller */
var c = this;
c.totalPrice ='';
$scope.$watch(function(){
return $scope.page.g_form.getValue('item_prices');
},function(val){
c.totalPrice ='';
$scope.page.g_form.setValue('total_price','');
if(val !=''){
var obj = JSON.parse(val);
var tPrice=0;
for(var i in obj)
tPrice =parseInt(tPrice)+parseInt(obj[i].price);
if(tPrice!='' && tPrice>0){
c.totalPrice =tPrice;
$scope.page.g_form.setValue('total_price',tPrice);
}
}
});
}
Let me know if you are still facing any issues.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
This is awesome, how would you make it work, if you need it to work the other way too. Not only the portal side?