- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-02-2023 08:37 AM
Dear Team,
I am working on Portal, when a user opens the Service Portal> Catalog, then I want to display the 'All Items' just beside the My Recent Items and Popular Items (as marked in the attached screenshot). So when user clicks on 'All Items' they can see all catalog items and further click on them and raise request.
This is coming from a widget named 'Recent & Popular Items', I cloned this and tried to modify the server scripts and all but no luck. Can anyone please help to modify this widget script, so that all items will be displayed in the section as shown in the attached screenshot?
*Body HTML Template-
<div class="recent-widget" ng-if="!c.hideWidget">
<div class="panel panel-default">
<div class="panel-heading pad-bottom">
<ul class="nav nav-tabs" style="border-bottom : 0px" role="tablist">
<li class="item" id="tab-recent-items" ng-if="data.recentItems.length > 0"
ng-click="c.changePanel('recent')" ng-keydown="c.switchTab($event, 'recent')"
role="presentation">
<div ng-class="{'sc-tab-div' : c.show_recent}" class="recent"
ng-attr-tabindex="{{!c.show_recent ? '-1': 0}}"
ng-attr-aria-controls="{{c.show_recent ? 'tabpanel-recent-items' : undefined}}"
aria-selected="{{c.show_recent}}" role="tab">
<span> ${My Recent Items} </span>
</div>
</li>
<li class="item" id="tab-popular-items" ng-if="data.popularItems.length > 0"
ng-keydown="c.switchTab($event, 'popular')" ng-click="c.changePanel('popular')"
role="presentation">
<div ng-class="{'sc-tab-div' : !c.show_recent}" class="popular"
ng-attr-tabindex="{{c.show_recent ? '-1': 0}}"
ng-attr-aria-controls="{{!c.show_recent ? 'tabpanel-popular-items' : undefined}}"
aria-selected="{{!c.show_recent}}" role="tab">
<span> ${Popular Items} </span>
</div>
</li>
</ul>
</div>
<div id="tabpanel-recent-items" class="panels-container" ng-if="c.show_recent" role="tabpanel"
aria-labelledby="tab-recent-items">
<span class="sr-only" role="heading" aria-level="2"> ${My Recent Items} </span>
<ul class="row item-list-style-type-none item-card-row" role="list" aria-labelledby="tab-recent-items">
<li class="item-card-column" ng-repeat="item in data.recentItems track by item.sys_id" role="listitem" ng-init="startItemList()">
<div class="panel-default item-card b" data-original-title="{{::item.name}}">
<a href="?id={{::item.page}}&sys_id={{::item.sys_id}}&referrer=recent_items"
class="panel-body block height-100"
onclick='window.GlideWebAnalytics.trackEvent("Service Catalog", "Recent Item", "Item Clicked");'>
<div>
<h3 class="h4 m-t-none m-b-xs text-overflow-ellipsis catalog-item-name" title="{{::item.name}}">{{::item.name}}</h3>
<img ng-src="{{::item.picture}}" ng-if="::item.picture"
class="m-r-sm m-b-sm item-image pull-left" alt="" aria-hidden="true"/>
<div class="text-muted item-short-desc catalog-text-wrap">{{::item.short_description}}
</div>
</div>
</a>
</div>
<div class="panel-footer b">
<a href="?id={{::item.page}}&sys_id={{item.sys_id}}&referrer=recent_items"
class="pull-left text-muted" aria-label="${View Details} {{::item.name}}"
onclick='window.GlideWebAnalytics.trackEvent("Service Catalog", "Recent Item", "Item Clicked");'>${View Details}</a>
<span ng-if="data.showPrices && item.hasPrice" class="pull-right item-price font-bold">{{::item.price}}</span>
</div>
</li>
</ul>
</div>
<div id="tabpanel-popular-items" class="panels-container" ng-if="!c.show_recent" role="tabpanel"
aria-labelledby="tab-popular-items">
<span class="sr-only" role="heading" aria-level="2"> ${Popular Items} </span>
<ul class="row item-list-style-type-none item-card-row" role="list" aria-labelledby="tab-popular-items">
<li class="item-card-column"
ng-repeat="item in data.popularItems | orderBy: 'order' | limitTo: data.limit track by item.sys_id"
role="listitem" ng-init="startItemList()">
<div class="panel-default item-card b" data-original-title="{{::item.name}}">
<a href="?id={{::item.page}}&sys_id={{::item.sys_id}}&referrer=popular_items"
class="panel-body block height-100"
onclick='window.GlideWebAnalytics.trackEvent("Service Catalog", "Popular Item", "Item Clicked");'>
<div>
<h3 class="h4 m-t-none m-b-xs text-overflow-ellipsis catalog-item-name" title="{{::item.name}}">{{::item.name}}</h3>
<img ng-src="{{::item.picture}}" ng-if="::item.picture"
class="m-r-sm m-b-sm item-image pull-left" alt="" aria-hidden="true"/>
<div class="text-muted item-short-desc catalog-text-wrap">{{::item.short_description}}
</div>
</div>
</a>
</div>
<div class="panel-footer b">
<a href="?id={{::item.page}}&sys_id={{item.sys_id}}&referrer=popular_items"
class="pull-left text-muted" aria-label="${View Details} {{::item.name}}"
onclick='window.GlideWebAnalytics.trackEvent("Service Catalog", "Popular Item", "Item Clicked");'>${View Details}</a>
<span ng-if="data.showPrices && item.hasPrice" class="pull-right item-price font-bold">{{::item.price}}</span>
</div>
</li>
</ul>
</div>
</div>
</div>
*Server Script:
(function() {
/* populate the 'data' object */
data.limit = options.limit || 8;
var recent_by = options.recent_by || 'view';
var recent = new GlideRecord('sp_log');
if (recent_by === 'view')
recent.addEncodedQuery('userDYNAMIC90d1921e5f510100a9ad2572f2b477fe^type=Cat Item View^sys_created_onONThis quarter@javascript:gs.beginningOfThisQuarter()@javascript:gs.endOfThisQuarter()');
else
recent.addEncodedQuery('userDYNAMIC90d1921e5f510100a9ad2572f2b477fe^type=Cat Item Request^sys_created_onONThis quarter@javascript:gs.beginningOfThisQuarter()@javascript:gs.endOfThisQuarter()');
recent.orderByDesc('sys_created_on');
recent.query();
var recentItems = [];
var catalogArr = ($sp.getCatalogs().value + "").split(",");
data.showPrices = $sp.showCatalogPrices();
while (recent.next() && recentItems.length < data.limit) {
if (isAlreadyAdded(recent.getValue('id')))
continue;
var catalogItemJS = new sn_sc.CatItem(recent.getValue('id'));
if (!catalogItemJS.canView(gs.isMobile()) || !catalogItemJS.isVisibleServicePortal())
continue;
var item = {};
var catItemDetails = catalogItemJS.getItemSummary(true);
if(!catItemDetails.visible_standalone)
continue;
var inCatalog = false;
for (var i=0; i<catItemDetails.catalogs.length; i++) {
if (catalogArr.indexOf(catItemDetails.catalogs[i].sys_id + "") >= 0) {
inCatalog = true;
break;
}
}
if (inCatalog) {
item.name = catItemDetails.name;
item.short_description = catItemDetails.short_description;
item.picture = catItemDetails.picture;
item.price = catItemDetails.price;
item.sys_id = catItemDetails.sys_id;
item.hasPrice = item.price != 0;
item.page = catItemDetails.type == 'order_guide'? 'sc_cat_item_guide' : 'sc_cat_item';
recentItems.push(item);
}
}
function isAlreadyAdded(catItemSysId) {
for (var i=0;i<recentItems.length;i++) {
if (catItemSysId==recentItems[i].sys_id)
return true;
}
return false;
}
data.recentItems = recentItems;
data.popularItems = getPopularItems();
function getPopularItems() {
return new SCPopularItems().useOptimisedQuery(gs.getProperty('glide.sc.portal.popular_items.optimize', true) + '' == 'true')
.baseQuery(options.popular_items_created + '')
.allowedItems($sp.getAllowedItems())
.visibleStandalone(true)
.visibleServicePortal(true)
.itemsLimit(options.limit || 8)
.restrictedItemTypes(['sc_cat_item_guide', 'sc_cat_item_wizard', 'sc_cat_item_content', 'sc_cat_item_producer'])
.itemValidator(function(item, itemDetails) {
if (!item.canView(gs.isMobile()) || !item.isVisibleServicePortal())
return false;
return true;
})
.responseObjectFormatter(function(item, itemType, itemCount) {
return {
order: 0 - itemCount,
name: item.name,
short_description: item.short_description,
picture: item.picture,
price: item.price,
sys_id: item.sys_id,
hasPrice: item.price != 0,
page: itemType == 'sc_cat_item_guide' ? 'sc_cat_item_guide' : 'sc_cat_item'
};
})
.generate();
}
})();
*Client script/Controller:
function($scope, $timeout, $window) {
/* widget controller */
var c = this;
c.show_recent = false;
c.KEYS = {
DOWN:40,
LEFT:37,
RIGHT:39,
TAB:9,
UP:38
}
var tabSelectors = ['div.recent', 'div.popular'];
function focusTab(recent) {
var ele;
if (recent)
ele = $(tabSelectors[0]);
else
ele = $(tabSelectors[1]);
if (ele)
ele.focus();
}
if (c.data.recentItems.length > 0)
c.show_recent = true;
c.changePanel = function(panel_name) {
if (c.show_recent) {
if (panel_name == 'popular')
c.show_recent = false;
} else {
if (panel_name == 'recent')
c.show_recent = true;
}
};
c.switchTab = function($event, tab) {
var key = null;
if (event.keyCode === c.KEYS.LEFT || event.keyCode === c.KEYS.UP)
key = c.KEYS.LEFT;
else if (event.keyCode === c.KEYS.RIGHT || event.keyCode === c.KEYS.DOWN)
key = c.KEYS.RIGHT;
else if (event.keyCode === c.KEYS.TAB) {
return;
}
if (key === null)
return;
$event.preventDefault();
c.show_recent = !c.show_recent;
focusTab(c.show_recent);
}
c.hideWidget = (c.data.recentItems.length === 0 && c.data.popularItems.length === 0);
function handleTooltip() {
if(!$scope.isTouchDevice()) {
$timeout(function() {
$(".item-card").each(function(index) {
var itemNameElement = $(this).find(".catalog-item-name");
if(itemNameElement && itemNameElement[0] && itemNameElement.width() < itemNameElement[0].scrollWidth) {
$(this).attr('data-toggle','tooltip');
}
});
});
}
}
$scope.isTouchDevice = function() {
return ('ontouchstart' in $window);
}
$scope.startItemList = function() {
handleTooltip();
}
}
Requesting help on this.
@Nia McCash or any one who can help on this.
Thanks in adavnce
Solved! Go to Solution.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-02-2023 11:59 AM
Add the new tab HTML:
<div class="recent-widget" ng-if="!c.hideWidget">
<div class="panel panel-default">
<div class="panel-heading pad-bottom">
<ul class="nav nav-tabs" style="border-bottom : 0px" role="tablist">
<li class="item" id="tab-recent-items" ng-if="data.recentItems.length > 0"
ng-click="c.changePanel('recent')" ng-keydown="c.switchTab($event, 'recent')"
role="presentation">
<div ng-class="{'sc-tab-div' : c.show_recent}" class="recent"
ng-attr-tabindex="{{!c.show_recent ? '-1': 0}}"
ng-attr-aria-controls="{{c.show_recent ? 'tabpanel-recent-items' : undefined}}"
aria-selected="{{c.show_recent}}" role="tab">
<span> ${My Recent Items} </span>
</div>
</li>
<li class="item" id="tab-popular-items" ng-if="data.popularItems.length > 0"
ng-keydown="c.switchTab($event, 'popular')" ng-click="c.changePanel('popular')"
role="presentation">
<!--
##########################################
START CUSTOM CODE
##########################################
Original DIV
<div ng-class="{'sc-tab-div' : !c.show_recent}" class="popular"
ng-attr-tabindex="{{c.show_recent ? '-1': 0}}"
ng-attr-aria-controls="{{!c.show_recent ? 'tabpanel-popular-items' : undefined}}"
aria-selected="{{!c.show_recent}}" role="tab">
<span> ${Popular Items} </span>
</div>
-->
<div ng-class="{'sc-tab-div' : !c.show_recent}" class="popular"
ng-attr-tabindex="{{c.show_popular ? '-1': 1}}"
ng-attr-aria-controls="{{c.show_popular ? 'tabpanel-popular-items' : undefined}}"
aria-selected="{{c.show_popular}}" role="tab">
<span> ${Popular Items} </span>
</div>
</li>
<li class="item" id="tab-all-items" ng-if="data.allItems.length > 0"
ng-keydown="c.switchTab($event, 'allitems')" ng-click="c.changePanel('allitems')"
role="presentation">
<div ng-class="{'sc-tab-div' : c.show_allitems}" class="allitems"
ng-attr-tabindex="{{c.show_allitems ? '-1': 2}}"
ng-attr-aria-controls="{{c.show_allitems ? 'tabpanel-all-items' : undefined}}"
aria-selected="{{c.show_allitems}}" role="tab">
<span> ${All Items} </span>
</div>
<!--
##########################################
END CUSTOM CODE
##########################################
-->
</li>
</ul>
</div>
<div id="tabpanel-recent-items" class="panels-container" ng-if="c.show_recent" role="tabpanel"
aria-labelledby="tab-recent-items">
<span class="sr-only" role="heading" aria-level="2"> ${My Recent Items} </span>
<ul class="row item-list-style-type-none item-card-row" role="list" aria-labelledby="tab-recent-items">
<li class="item-card-column" ng-repeat="item in data.recentItems track by item.sys_id" role="listitem" ng-init="startItemList()">
<div class="panel-default item-card b" data-original-title="{{::item.name}}">
<a href="?id={{::item.page}}&sys_id={{::item.sys_id}}&referrer=recent_items"
class="panel-body block height-100"
onclick='window.GlideWebAnalytics.trackEvent("Service Catalog", "Recent Item", "Item Clicked");'>
<div>
<h3 class="h4 m-t-none m-b-xs text-overflow-ellipsis catalog-item-name" title="{{::item.name}}">{{::item.name}}</h3>
<img ng-src="{{::item.picture}}" ng-if="::item.picture"
class="m-r-sm m-b-sm item-image pull-left" alt="" aria-hidden="true"/>
<div class="text-muted item-short-desc catalog-text-wrap">{{::item.short_description}}
</div>
</div>
</a>
</div>
<div class="panel-footer b">
<a href="?id={{::item.page}}&sys_id={{item.sys_id}}&referrer=recent_items"
class="pull-left text-muted" aria-label="${View Details} {{::item.name}}"
onclick='window.GlideWebAnalytics.trackEvent("Service Catalog", "Recent Item", "Item Clicked");'>${View Details}</a>
<span ng-if="data.showPrices && item.hasPrice" class="pull-right item-price font-bold">{{::item.price}}</span>
</div>
</li>
</ul>
</div>
<!--
##########################################
START CUSTOM CODE
##########################################
Original DIV
<div id="tabpanel-popular-items" class="panels-container" ng-if="!c.show_recent" role="tabpanel"
aria-labelledby="tab-popular-items">
-->
<div id="tabpanel-popular-items" class="panels-container" ng-if="c.show_popular" role="tabpanel"
aria-labelledby="tab-popular-items">
<!--
##########################################
END CUSTOM CODE
##########################################
-->
<span class="sr-only" role="heading" aria-level="2"> ${Popular Items} </span>
<ul class="row item-list-style-type-none item-card-row" role="list" aria-labelledby="tab-popular-items">
<li class="item-card-column"
ng-repeat="item in data.popularItems | orderBy: 'order' | limitTo: data.limit track by item.sys_id"
role="listitem" ng-init="startItemList()">
<div class="panel-default item-card b" data-original-title="{{::item.name}}">
<a href="?id={{::item.page}}&sys_id={{::item.sys_id}}&referrer=popular_items"
class="panel-body block height-100"
onclick='window.GlideWebAnalytics.trackEvent("Service Catalog", "Popular Item", "Item Clicked");'>
<div>
<h3 class="h4 m-t-none m-b-xs text-overflow-ellipsis catalog-item-name" title="{{::item.name}}">{{::item.name}}</h3>
<img ng-src="{{::item.picture}}" ng-if="::item.picture"
class="m-r-sm m-b-sm item-image pull-left" alt="" aria-hidden="true"/>
<div class="text-muted item-short-desc catalog-text-wrap">{{::item.short_description}}
</div>
</div>
</a>
</div>
<div class="panel-footer b">
<a href="?id={{::item.page}}&sys_id={{item.sys_id}}&referrer=popular_items"
class="pull-left text-muted" aria-label="${View Details} {{::item.name}}"
onclick='window.GlideWebAnalytics.trackEvent("Service Catalog", "Popular Item", "Item Clicked");'>${View Details}</a>
<span ng-if="data.showPrices && item.hasPrice" class="pull-right item-price font-bold">{{::item.price}}</span>
</div>
</li>
</ul>
</div>
<!--
##########################################
START CUSTOM CODE
##########################################
-->
<div id="tabpanel-all-items" class="panels-container" ng-if="c.show_allitems" role="tabpanel"
aria-labelledby="tab-all-items">
<span class="sr-only" role="heading" aria-level="2"> ${All Items} </span>
<ul class="row item-list-style-type-none item-card-row" role="list" aria-labelledby="tab-all-items">
<li class="item-card-column" ng-repeat="item in data.allItems track by item.sys_id" role="listitem" ng-init="startItemList()">
<div class="panel-default item-card b" data-original-title="{{::item.name}}">
<a href="?id={{::item.page}}&sys_id={{::item.sys_id}}&referrer=all_items"
class="panel-body block height-100"
onclick='window.GlideWebAnalytics.trackEvent("Service Catalog", "All Items", "Item Clicked");'>
<div>
<h3 class="h4 m-t-none m-b-xs text-overflow-ellipsis catalog-item-name" title="{{::item.name}}">{{::item.name}}</h3>
<img ng-src="{{::item.picture}}" ng-if="::item.picture"
class="m-r-sm m-b-sm item-image pull-left" alt="" aria-hidden="true"/>
<div class="text-muted item-short-desc catalog-text-wrap">{{::item.short_description}}
</div>
</div>
</a>
</div>
<div class="panel-footer b">
<a href="?id={{::item.page}}&sys_id={{item.sys_id}}&referrer=all_items"
class="pull-left text-muted" aria-label="${View Details} {{::item.name}}"
onclick='window.GlideWebAnalytics.trackEvent("Service Catalog", "All Items", "Item Clicked");'>${View Details}</a>
<span ng-if="data.showPrices && item.hasPrice" class="pull-right item-price font-bold">{{::item.price}}</span>
</div>
</li>
</ul>
</div>
<!--
##########################################
END CUSTOM CODE
##########################################
-->
</div>
</div>
Add the class in the CSS:
.nav > li.item {
text-align: center;
}
.recent-widget {
.panel-heading {
padding-bottom: 0px;
}
.row {
margin-left: 0;
margin-right: 0;
padding-top: 15px;
padding-left: 10px;
padding-right: 10px;
}
}
@media (min-width: 768px) {
.item-card-column {
-ms-flex: 0 0 50%;
-moz-box-flex: 0 0 50%;
-moz-flex: 0 0 50%;
-webkit-box-flex: 0 0 50%;;
-webkit-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%;
width: 50%;
}
}
@media (min-width: 992px) {
.item-card-column {
-ms-flex: 0 0 25%;
-moz-box-flex: 0 0 25%;
-moz-flex: 0 0 25%;
-webkit-box-flex: 0 0 25%;
-webkit-flex: 0 0 25%;
flex: 0 0 25%;
max-width: 25%;
width: 25%;
}
}
//##########################################
//START CUSTOM CODE
//##########################################
//.popular, .recent {
.popular, .recent, .allitems {
//##########################################
//END CUSTOM CODE
//##########################################
position: relative;
display: block;
padding: $nav-link-padding;
line-height: $line-height-base;
margin-right: 2px;
border-radius: $border-radius-base $border-radius-base 0 0;
color: $brand-primary-darker;
&:hover {
border-color: $nav-tabs-link-hover-border-color $nav-tabs-link-hover-border-color $nav-tabs-border-color;
}
&:focus {
box-shadow: 0 0 0 1.5px $input-border-focus;
outline: 4px solid transparent;
}
&:hover, &:focus {
text-decoration: none;
background-color: $nav-link-hover-bg;
}
&.sc-tab-div {
border: 0;
border-bottom: 3px solid $primary;
background-color: transparent;
color: $primary;
&:hover, &:focus {
background-color: $nav-link-hover-bg;
}
&:hover {
border-color: $nav-tabs-link-hover-border-color $nav-tabs-link-hover-border-color $nav-tabs-border-color;
}
}
}
.catalog-item-name {
text-decoration: underline;
}
Rework the Client Script for more than 2 tabs:
function($scope, $timeout, $window) {
/* widget controller */
var c = this;
console.dir(c.data.allItems);
c.show_recent = false;
//##########################################
//START CUSTOM CODE
//##########################################
c.show_popular = false;
c.show_allitems = false;
//##########################################
//END CUSTOM CODE
//##########################################
c.KEYS = {
DOWN:40,
LEFT:37,
RIGHT:39,
TAB:9,
UP:38
}
//##########################################
//START CUSTOM CODE
//##########################################
// var tabSelectors = ['div.recent', 'div.popular'];
var tabSelectors = ['div.recent', 'div.popular', 'div.allitems'];
// function focusTab(recent) {
function focusTab(x) {
var ele;
// if (recent)
if (x == 'recent')
ele = $(tabSelectors[0]);
// else
else if (x == 'popular')
ele = $(tabSelectors[1]);
else
ele = $(tabSelectors[2]);
if (ele)
ele.focus();
}
// if (c.data.recentItems.length > 0)
// c.show_recent = true;
// c.changePanel = function(panel_name) {
// if (c.show_recent) {
// if (panel_name == 'popular')
// c.show_recent = false;
// } else {
// if (panel_name == 'recent')
// c.show_recent = true;
// }
// };
if (c.data.recentItems.length > 0)
c.show_recent = true;
c.changePanel = function(panel_name) {
if (panel_name == 'recent') {
c.show_recent = true;
c.show_popular = false;
c.show_allitems = false;
} else if (panel_name == 'popular'){
c.show_recent = false;
c.show_popular = true;
c.show_allitems = false;
} else if (panel_name == 'allitems'){
c.show_recent = false;
c.show_popular = false;
c.show_allitems = true;
} else {
c.show_recent = true;
c.show_popular = false;
c.show_allitems = false;
}
};
//##########################################
//END CUSTOM CODE
//##########################################
c.switchTab = function($event, tab) {
var key = null;
if (event.keyCode === c.KEYS.LEFT || event.keyCode === c.KEYS.UP)
key = c.KEYS.LEFT;
else if (event.keyCode === c.KEYS.RIGHT || event.keyCode === c.KEYS.DOWN)
key = c.KEYS.RIGHT;
else if (event.keyCode === c.KEYS.TAB) {
return;
}
if (key === null)
return;
$event.preventDefault();
//##########################################
//START CUSTOM CODE
//##########################################
// c.show_recent = !c.show_recent;
// focusTab(c.show_recent);
if (panel_name == 'recent') {
focusTab('recent');
} else if (panel_name == 'popular'){
focusTab('popular');
} else if (panel_name == 'allitems'){
focusTab('allitems');
}
//##########################################
//END CUSTOM CODE
//##########################################
}
//##########################################
//START CUSTOM CODE
//##########################################
// c.hideWidget = (c.data.recentItems.length === 0 && c.data.popularItems.length === 0);
c.hideWidget = (c.data.recentItems.length === 0 && c.data.popularItems.length === 0 && c.data.allItems.length === 0);
//##########################################
//END CUSTOM CODE
//##########################################
function handleTooltip() {
if(!$scope.isTouchDevice()) {
$timeout(function() {
$(".item-card").each(function(index) {
var itemNameElement = $(this).find(".catalog-item-name");
if(itemNameElement && itemNameElement[0] && itemNameElement.width() < itemNameElement[0].scrollWidth) {
$(this).attr('data-toggle','tooltip');
}
});
});
}
}
$scope.isTouchDevice = function() {
return ('ontouchstart' in $window);
}
$scope.startItemList = function() {
handleTooltip();
}
}
Pull the data in the Server Script:
(function() {
/* populate the 'data' object */
data.limit = options.limit || 8;
var recent_by = options.recent_by || 'view';
var recent = new GlideRecord('sp_log');
if (recent_by === 'view')
recent.addEncodedQuery('userDYNAMIC90d1921e5f510100a9ad2572f2b477fe^type=Cat Item View^sys_created_onONThis quarter@javascript:gs.beginningOfThisQuarter()@javascript:gs.endOfThisQuarter()');
else
recent.addEncodedQuery('userDYNAMIC90d1921e5f510100a9ad2572f2b477fe^type=Cat Item Request^sys_created_onONThis quarter@javascript:gs.beginningOfThisQuarter()@javascript:gs.endOfThisQuarter()');
recent.addEncodedQuery('active=true^sys_class_name!=sc_cat_item_wizard^sys_class_name!=sc_cat_item_guide^sys_class_name!=sc_cat_item_content^sys_class_name!=sc_cat_item_producer^categoryISNOTEMPTY^hide_sp!=true');
recent.orderByDesc('sys_created_on');
recent.query();
var recentItems = [];
var catalogArr = ($sp.getCatalogs().value + "").split(",");
data.showPrices = $sp.showCatalogPrices();
while (recent.next() && recentItems.length < data.limit) {
if (isAlreadyAdded(recent.getValue('id')))
continue;
var catalogItemJS = new sn_sc.CatItem(recent.getValue('id'));
if (!catalogItemJS.canView(gs.isMobile()) || !catalogItemJS.isVisibleServicePortal())
continue;
var item = {};
var catItemDetails = catalogItemJS.getItemSummary(true);
if(!catItemDetails.visible_standalone)
continue;
var inCatalog = false;
for (var i=0; i<catItemDetails.catalogs.length; i++) {
if (catalogArr.indexOf(catItemDetails.catalogs[i].sys_id + "") >= 0) {
inCatalog = true;
break;
}
}
if (inCatalog) {
item.name = catItemDetails.name;
item.short_description = catItemDetails.short_description;
item.picture = catItemDetails.picture;
item.price = catItemDetails.price;
item.sys_id = catItemDetails.sys_id;
item.hasPrice = item.price != 0;
item.page = catItemDetails.type == 'order_guide'? 'sc_cat_item_guide' : 'sc_cat_item';
recentItems.push(item);
}
}
function isAlreadyAdded(catItemSysId) {
for (var i=0;i<recentItems.length;i++) {
if (catItemSysId==recentItems[i].sys_id)
return true;
}
return false;
}
data.recentItems = recentItems;
data.popularItems = getPopularItems();
function getPopularItems() {
return new SCPopularItems().useOptimisedQuery(gs.getProperty('glide.sc.portal.popular_items.optimize', true) + '' == 'true')
.baseQuery(options.popular_items_created + '')
.allowedItems($sp.getAllowedItems())
.visibleStandalone(true)
.visibleServicePortal(true)
.itemsLimit(options.limit || 8)
.restrictedItemTypes(['sc_cat_item_guide', 'sc_cat_item_wizard', 'sc_cat_item_content', 'sc_cat_item_producer'])
.itemValidator(function(item, itemDetails) {
if (!item.canView(gs.isMobile()) || !item.isVisibleServicePortal())
return false;
return true;
})
.responseObjectFormatter(function(item, itemType, itemCount) {
return {
order: 0 - itemCount,
name: item.name,
short_description: item.short_description,
picture: item.picture,
price: item.price,
sys_id: item.sys_id,
hasPrice: item.price != 0,
page: itemType == 'sc_cat_item_guide' ? 'sc_cat_item_guide' : 'sc_cat_item'
};
})
.generate();
}
//##########################################
//START CUSTOM CODE
//##########################################
var gr = new GlideRecord('sc_cat_item');
gr.addEncodedQuery('active=true^sys_class_name!=sc_cat_item_wizard^sys_class_name!=sc_cat_item_guide^sys_class_name!=sc_cat_item_content^sys_class_name!=sc_cat_item_producer^categoryISNOTEMPTY^hide_sp!=true');
gr.orderBy('name');
gr.query();
var allItems = [];
while (gr.next()) {
var item2 = {};
$sp.getRecordDisplayValues(item2, gr, 'name,short_description,picture,price,sys_id,tags,sys_class_name,sc_catalogs');
item2.hasPrice = item2.price != 0;
item2.page = 'sc_cat_item';
allItems.push(item2);
}
data.allItems = allItems;
//##########################################
//END CUSTOM CODE
//##########################################
})();
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-02-2023 11:47 PM
This is issue is because the code is copied from the editor in community. Community editor have issue with showing colon symbol.
This should fix the recent tab issue.
Please mark as correct answer.
ServiceNow Community Rising Star, Class of 2023
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-02-2023 11:55 PM
@rishabh31 And the below updated client script to fix the popular items issue.
ServiceNow Community Rising Star, Class of 2023

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-02-2023 09:02 AM
I created a separate widget for this that I'm happy to share the code for, but I'll look through your code and see if I can identify the issue.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-02-2023 09:24 AM
@sethhumphrey Thank you sir for your response, requesting that if you can modify the script for cloned 'Recent & Popular items' widget that would be great otherwise I will be grateful if you share the details of the Separate Widget.
Thanks

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-02-2023 11:59 AM
Add the new tab HTML:
<div class="recent-widget" ng-if="!c.hideWidget">
<div class="panel panel-default">
<div class="panel-heading pad-bottom">
<ul class="nav nav-tabs" style="border-bottom : 0px" role="tablist">
<li class="item" id="tab-recent-items" ng-if="data.recentItems.length > 0"
ng-click="c.changePanel('recent')" ng-keydown="c.switchTab($event, 'recent')"
role="presentation">
<div ng-class="{'sc-tab-div' : c.show_recent}" class="recent"
ng-attr-tabindex="{{!c.show_recent ? '-1': 0}}"
ng-attr-aria-controls="{{c.show_recent ? 'tabpanel-recent-items' : undefined}}"
aria-selected="{{c.show_recent}}" role="tab">
<span> ${My Recent Items} </span>
</div>
</li>
<li class="item" id="tab-popular-items" ng-if="data.popularItems.length > 0"
ng-keydown="c.switchTab($event, 'popular')" ng-click="c.changePanel('popular')"
role="presentation">
<!--
##########################################
START CUSTOM CODE
##########################################
Original DIV
<div ng-class="{'sc-tab-div' : !c.show_recent}" class="popular"
ng-attr-tabindex="{{c.show_recent ? '-1': 0}}"
ng-attr-aria-controls="{{!c.show_recent ? 'tabpanel-popular-items' : undefined}}"
aria-selected="{{!c.show_recent}}" role="tab">
<span> ${Popular Items} </span>
</div>
-->
<div ng-class="{'sc-tab-div' : !c.show_recent}" class="popular"
ng-attr-tabindex="{{c.show_popular ? '-1': 1}}"
ng-attr-aria-controls="{{c.show_popular ? 'tabpanel-popular-items' : undefined}}"
aria-selected="{{c.show_popular}}" role="tab">
<span> ${Popular Items} </span>
</div>
</li>
<li class="item" id="tab-all-items" ng-if="data.allItems.length > 0"
ng-keydown="c.switchTab($event, 'allitems')" ng-click="c.changePanel('allitems')"
role="presentation">
<div ng-class="{'sc-tab-div' : c.show_allitems}" class="allitems"
ng-attr-tabindex="{{c.show_allitems ? '-1': 2}}"
ng-attr-aria-controls="{{c.show_allitems ? 'tabpanel-all-items' : undefined}}"
aria-selected="{{c.show_allitems}}" role="tab">
<span> ${All Items} </span>
</div>
<!--
##########################################
END CUSTOM CODE
##########################################
-->
</li>
</ul>
</div>
<div id="tabpanel-recent-items" class="panels-container" ng-if="c.show_recent" role="tabpanel"
aria-labelledby="tab-recent-items">
<span class="sr-only" role="heading" aria-level="2"> ${My Recent Items} </span>
<ul class="row item-list-style-type-none item-card-row" role="list" aria-labelledby="tab-recent-items">
<li class="item-card-column" ng-repeat="item in data.recentItems track by item.sys_id" role="listitem" ng-init="startItemList()">
<div class="panel-default item-card b" data-original-title="{{::item.name}}">
<a href="?id={{::item.page}}&sys_id={{::item.sys_id}}&referrer=recent_items"
class="panel-body block height-100"
onclick='window.GlideWebAnalytics.trackEvent("Service Catalog", "Recent Item", "Item Clicked");'>
<div>
<h3 class="h4 m-t-none m-b-xs text-overflow-ellipsis catalog-item-name" title="{{::item.name}}">{{::item.name}}</h3>
<img ng-src="{{::item.picture}}" ng-if="::item.picture"
class="m-r-sm m-b-sm item-image pull-left" alt="" aria-hidden="true"/>
<div class="text-muted item-short-desc catalog-text-wrap">{{::item.short_description}}
</div>
</div>
</a>
</div>
<div class="panel-footer b">
<a href="?id={{::item.page}}&sys_id={{item.sys_id}}&referrer=recent_items"
class="pull-left text-muted" aria-label="${View Details} {{::item.name}}"
onclick='window.GlideWebAnalytics.trackEvent("Service Catalog", "Recent Item", "Item Clicked");'>${View Details}</a>
<span ng-if="data.showPrices && item.hasPrice" class="pull-right item-price font-bold">{{::item.price}}</span>
</div>
</li>
</ul>
</div>
<!--
##########################################
START CUSTOM CODE
##########################################
Original DIV
<div id="tabpanel-popular-items" class="panels-container" ng-if="!c.show_recent" role="tabpanel"
aria-labelledby="tab-popular-items">
-->
<div id="tabpanel-popular-items" class="panels-container" ng-if="c.show_popular" role="tabpanel"
aria-labelledby="tab-popular-items">
<!--
##########################################
END CUSTOM CODE
##########################################
-->
<span class="sr-only" role="heading" aria-level="2"> ${Popular Items} </span>
<ul class="row item-list-style-type-none item-card-row" role="list" aria-labelledby="tab-popular-items">
<li class="item-card-column"
ng-repeat="item in data.popularItems | orderBy: 'order' | limitTo: data.limit track by item.sys_id"
role="listitem" ng-init="startItemList()">
<div class="panel-default item-card b" data-original-title="{{::item.name}}">
<a href="?id={{::item.page}}&sys_id={{::item.sys_id}}&referrer=popular_items"
class="panel-body block height-100"
onclick='window.GlideWebAnalytics.trackEvent("Service Catalog", "Popular Item", "Item Clicked");'>
<div>
<h3 class="h4 m-t-none m-b-xs text-overflow-ellipsis catalog-item-name" title="{{::item.name}}">{{::item.name}}</h3>
<img ng-src="{{::item.picture}}" ng-if="::item.picture"
class="m-r-sm m-b-sm item-image pull-left" alt="" aria-hidden="true"/>
<div class="text-muted item-short-desc catalog-text-wrap">{{::item.short_description}}
</div>
</div>
</a>
</div>
<div class="panel-footer b">
<a href="?id={{::item.page}}&sys_id={{item.sys_id}}&referrer=popular_items"
class="pull-left text-muted" aria-label="${View Details} {{::item.name}}"
onclick='window.GlideWebAnalytics.trackEvent("Service Catalog", "Popular Item", "Item Clicked");'>${View Details}</a>
<span ng-if="data.showPrices && item.hasPrice" class="pull-right item-price font-bold">{{::item.price}}</span>
</div>
</li>
</ul>
</div>
<!--
##########################################
START CUSTOM CODE
##########################################
-->
<div id="tabpanel-all-items" class="panels-container" ng-if="c.show_allitems" role="tabpanel"
aria-labelledby="tab-all-items">
<span class="sr-only" role="heading" aria-level="2"> ${All Items} </span>
<ul class="row item-list-style-type-none item-card-row" role="list" aria-labelledby="tab-all-items">
<li class="item-card-column" ng-repeat="item in data.allItems track by item.sys_id" role="listitem" ng-init="startItemList()">
<div class="panel-default item-card b" data-original-title="{{::item.name}}">
<a href="?id={{::item.page}}&sys_id={{::item.sys_id}}&referrer=all_items"
class="panel-body block height-100"
onclick='window.GlideWebAnalytics.trackEvent("Service Catalog", "All Items", "Item Clicked");'>
<div>
<h3 class="h4 m-t-none m-b-xs text-overflow-ellipsis catalog-item-name" title="{{::item.name}}">{{::item.name}}</h3>
<img ng-src="{{::item.picture}}" ng-if="::item.picture"
class="m-r-sm m-b-sm item-image pull-left" alt="" aria-hidden="true"/>
<div class="text-muted item-short-desc catalog-text-wrap">{{::item.short_description}}
</div>
</div>
</a>
</div>
<div class="panel-footer b">
<a href="?id={{::item.page}}&sys_id={{item.sys_id}}&referrer=all_items"
class="pull-left text-muted" aria-label="${View Details} {{::item.name}}"
onclick='window.GlideWebAnalytics.trackEvent("Service Catalog", "All Items", "Item Clicked");'>${View Details}</a>
<span ng-if="data.showPrices && item.hasPrice" class="pull-right item-price font-bold">{{::item.price}}</span>
</div>
</li>
</ul>
</div>
<!--
##########################################
END CUSTOM CODE
##########################################
-->
</div>
</div>
Add the class in the CSS:
.nav > li.item {
text-align: center;
}
.recent-widget {
.panel-heading {
padding-bottom: 0px;
}
.row {
margin-left: 0;
margin-right: 0;
padding-top: 15px;
padding-left: 10px;
padding-right: 10px;
}
}
@media (min-width: 768px) {
.item-card-column {
-ms-flex: 0 0 50%;
-moz-box-flex: 0 0 50%;
-moz-flex: 0 0 50%;
-webkit-box-flex: 0 0 50%;;
-webkit-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%;
width: 50%;
}
}
@media (min-width: 992px) {
.item-card-column {
-ms-flex: 0 0 25%;
-moz-box-flex: 0 0 25%;
-moz-flex: 0 0 25%;
-webkit-box-flex: 0 0 25%;
-webkit-flex: 0 0 25%;
flex: 0 0 25%;
max-width: 25%;
width: 25%;
}
}
//##########################################
//START CUSTOM CODE
//##########################################
//.popular, .recent {
.popular, .recent, .allitems {
//##########################################
//END CUSTOM CODE
//##########################################
position: relative;
display: block;
padding: $nav-link-padding;
line-height: $line-height-base;
margin-right: 2px;
border-radius: $border-radius-base $border-radius-base 0 0;
color: $brand-primary-darker;
&:hover {
border-color: $nav-tabs-link-hover-border-color $nav-tabs-link-hover-border-color $nav-tabs-border-color;
}
&:focus {
box-shadow: 0 0 0 1.5px $input-border-focus;
outline: 4px solid transparent;
}
&:hover, &:focus {
text-decoration: none;
background-color: $nav-link-hover-bg;
}
&.sc-tab-div {
border: 0;
border-bottom: 3px solid $primary;
background-color: transparent;
color: $primary;
&:hover, &:focus {
background-color: $nav-link-hover-bg;
}
&:hover {
border-color: $nav-tabs-link-hover-border-color $nav-tabs-link-hover-border-color $nav-tabs-border-color;
}
}
}
.catalog-item-name {
text-decoration: underline;
}
Rework the Client Script for more than 2 tabs:
function($scope, $timeout, $window) {
/* widget controller */
var c = this;
console.dir(c.data.allItems);
c.show_recent = false;
//##########################################
//START CUSTOM CODE
//##########################################
c.show_popular = false;
c.show_allitems = false;
//##########################################
//END CUSTOM CODE
//##########################################
c.KEYS = {
DOWN:40,
LEFT:37,
RIGHT:39,
TAB:9,
UP:38
}
//##########################################
//START CUSTOM CODE
//##########################################
// var tabSelectors = ['div.recent', 'div.popular'];
var tabSelectors = ['div.recent', 'div.popular', 'div.allitems'];
// function focusTab(recent) {
function focusTab(x) {
var ele;
// if (recent)
if (x == 'recent')
ele = $(tabSelectors[0]);
// else
else if (x == 'popular')
ele = $(tabSelectors[1]);
else
ele = $(tabSelectors[2]);
if (ele)
ele.focus();
}
// if (c.data.recentItems.length > 0)
// c.show_recent = true;
// c.changePanel = function(panel_name) {
// if (c.show_recent) {
// if (panel_name == 'popular')
// c.show_recent = false;
// } else {
// if (panel_name == 'recent')
// c.show_recent = true;
// }
// };
if (c.data.recentItems.length > 0)
c.show_recent = true;
c.changePanel = function(panel_name) {
if (panel_name == 'recent') {
c.show_recent = true;
c.show_popular = false;
c.show_allitems = false;
} else if (panel_name == 'popular'){
c.show_recent = false;
c.show_popular = true;
c.show_allitems = false;
} else if (panel_name == 'allitems'){
c.show_recent = false;
c.show_popular = false;
c.show_allitems = true;
} else {
c.show_recent = true;
c.show_popular = false;
c.show_allitems = false;
}
};
//##########################################
//END CUSTOM CODE
//##########################################
c.switchTab = function($event, tab) {
var key = null;
if (event.keyCode === c.KEYS.LEFT || event.keyCode === c.KEYS.UP)
key = c.KEYS.LEFT;
else if (event.keyCode === c.KEYS.RIGHT || event.keyCode === c.KEYS.DOWN)
key = c.KEYS.RIGHT;
else if (event.keyCode === c.KEYS.TAB) {
return;
}
if (key === null)
return;
$event.preventDefault();
//##########################################
//START CUSTOM CODE
//##########################################
// c.show_recent = !c.show_recent;
// focusTab(c.show_recent);
if (panel_name == 'recent') {
focusTab('recent');
} else if (panel_name == 'popular'){
focusTab('popular');
} else if (panel_name == 'allitems'){
focusTab('allitems');
}
//##########################################
//END CUSTOM CODE
//##########################################
}
//##########################################
//START CUSTOM CODE
//##########################################
// c.hideWidget = (c.data.recentItems.length === 0 && c.data.popularItems.length === 0);
c.hideWidget = (c.data.recentItems.length === 0 && c.data.popularItems.length === 0 && c.data.allItems.length === 0);
//##########################################
//END CUSTOM CODE
//##########################################
function handleTooltip() {
if(!$scope.isTouchDevice()) {
$timeout(function() {
$(".item-card").each(function(index) {
var itemNameElement = $(this).find(".catalog-item-name");
if(itemNameElement && itemNameElement[0] && itemNameElement.width() < itemNameElement[0].scrollWidth) {
$(this).attr('data-toggle','tooltip');
}
});
});
}
}
$scope.isTouchDevice = function() {
return ('ontouchstart' in $window);
}
$scope.startItemList = function() {
handleTooltip();
}
}
Pull the data in the Server Script:
(function() {
/* populate the 'data' object */
data.limit = options.limit || 8;
var recent_by = options.recent_by || 'view';
var recent = new GlideRecord('sp_log');
if (recent_by === 'view')
recent.addEncodedQuery('userDYNAMIC90d1921e5f510100a9ad2572f2b477fe^type=Cat Item View^sys_created_onONThis quarter@javascript:gs.beginningOfThisQuarter()@javascript:gs.endOfThisQuarter()');
else
recent.addEncodedQuery('userDYNAMIC90d1921e5f510100a9ad2572f2b477fe^type=Cat Item Request^sys_created_onONThis quarter@javascript:gs.beginningOfThisQuarter()@javascript:gs.endOfThisQuarter()');
recent.addEncodedQuery('active=true^sys_class_name!=sc_cat_item_wizard^sys_class_name!=sc_cat_item_guide^sys_class_name!=sc_cat_item_content^sys_class_name!=sc_cat_item_producer^categoryISNOTEMPTY^hide_sp!=true');
recent.orderByDesc('sys_created_on');
recent.query();
var recentItems = [];
var catalogArr = ($sp.getCatalogs().value + "").split(",");
data.showPrices = $sp.showCatalogPrices();
while (recent.next() && recentItems.length < data.limit) {
if (isAlreadyAdded(recent.getValue('id')))
continue;
var catalogItemJS = new sn_sc.CatItem(recent.getValue('id'));
if (!catalogItemJS.canView(gs.isMobile()) || !catalogItemJS.isVisibleServicePortal())
continue;
var item = {};
var catItemDetails = catalogItemJS.getItemSummary(true);
if(!catItemDetails.visible_standalone)
continue;
var inCatalog = false;
for (var i=0; i<catItemDetails.catalogs.length; i++) {
if (catalogArr.indexOf(catItemDetails.catalogs[i].sys_id + "") >= 0) {
inCatalog = true;
break;
}
}
if (inCatalog) {
item.name = catItemDetails.name;
item.short_description = catItemDetails.short_description;
item.picture = catItemDetails.picture;
item.price = catItemDetails.price;
item.sys_id = catItemDetails.sys_id;
item.hasPrice = item.price != 0;
item.page = catItemDetails.type == 'order_guide'? 'sc_cat_item_guide' : 'sc_cat_item';
recentItems.push(item);
}
}
function isAlreadyAdded(catItemSysId) {
for (var i=0;i<recentItems.length;i++) {
if (catItemSysId==recentItems[i].sys_id)
return true;
}
return false;
}
data.recentItems = recentItems;
data.popularItems = getPopularItems();
function getPopularItems() {
return new SCPopularItems().useOptimisedQuery(gs.getProperty('glide.sc.portal.popular_items.optimize', true) + '' == 'true')
.baseQuery(options.popular_items_created + '')
.allowedItems($sp.getAllowedItems())
.visibleStandalone(true)
.visibleServicePortal(true)
.itemsLimit(options.limit || 8)
.restrictedItemTypes(['sc_cat_item_guide', 'sc_cat_item_wizard', 'sc_cat_item_content', 'sc_cat_item_producer'])
.itemValidator(function(item, itemDetails) {
if (!item.canView(gs.isMobile()) || !item.isVisibleServicePortal())
return false;
return true;
})
.responseObjectFormatter(function(item, itemType, itemCount) {
return {
order: 0 - itemCount,
name: item.name,
short_description: item.short_description,
picture: item.picture,
price: item.price,
sys_id: item.sys_id,
hasPrice: item.price != 0,
page: itemType == 'sc_cat_item_guide' ? 'sc_cat_item_guide' : 'sc_cat_item'
};
})
.generate();
}
//##########################################
//START CUSTOM CODE
//##########################################
var gr = new GlideRecord('sc_cat_item');
gr.addEncodedQuery('active=true^sys_class_name!=sc_cat_item_wizard^sys_class_name!=sc_cat_item_guide^sys_class_name!=sc_cat_item_content^sys_class_name!=sc_cat_item_producer^categoryISNOTEMPTY^hide_sp!=true');
gr.orderBy('name');
gr.query();
var allItems = [];
while (gr.next()) {
var item2 = {};
$sp.getRecordDisplayValues(item2, gr, 'name,short_description,picture,price,sys_id,tags,sys_class_name,sc_catalogs');
item2.hasPrice = item2.price != 0;
item2.page = 'sc_cat_item';
allItems.push(item2);
}
data.allItems = allItems;
//##########################################
//END CUSTOM CODE
//##########################################
})();
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-02-2023 10:43 PM
Hello @sethhumphrey Sir,
Thank you so much for the kind of support provided.
I pasted the script in the cloned widget, this is working but with very few flaws which I think you can fix as you brought up too close to getting this fulfilled.
Please refer to the below screenshots for the results of what currently showing:
#All Items are now displaying accurately:
#But when the page reloads or opened, the 'Popular Items' Tab is not displaying popular Cat Items, but yes when I move the cursor to the 'Popular Items' tab and click on it, this starts displaying popular Cat Items as usual.
-------
#As you see, the 'My Recent Items' Tab is not appearing (hidden), this needs to be displayed.
Requesting you to please look into it to resolve them as you bring them too close. Just need further help🤗.
Thank You