
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-09-2020 04:45 AM
Hiya Guys,
I have created a new widget on our "approvals" portal page to display a list of "My Recent Approval History (Last 15 Days)".
Currently the list looks like this...
My issue is that none of the links work on the ""My Recent Approval History (Last 15 Days)" widget.
Is there a way to remove the links from this list altogether so that they just display the refs in grey, and is there a way to add more information to the grey text below like the name of the request that they approved/rejected.
Please can you advise?
Thank you
My "Approvals Hist Simple List" widget linked to the "My Recent Approval History (Last 15 Days)" report on the portal screen shot above
HMTL BODY:
<div class="panel panel-{{::c.options.color}} b" ng-if="c.data.isValid && (c.options.always_show == 'true' || c.options.always_show == true || c.data.filterText || c.data.list.length)">
<div class="panel-heading" ng-if="::!c.options.hide_header">
<h2 class="h4 panel-title">
<span ng-if="c.options.glyph">
<fa name="{{::c.options.glyph}}" />
</span>{{::c.options.title}}</h2>
<!-- <i class="fa fa-filter" ng-click="c.toggleFilter()" ng-class="{'disabled-filter': !c.showFilter}"></i> -->
<div ng-show="c.showFilter">
<input aria-label="${Filter}" ng-model="c.data.filterText" ng-model-options="{debounce: 300}" sn-focus="c.showFilter" placeholder="{{::data.filterMsg}}" ng-change="c.update()" class="form-control input-sm filter-box">
</div>
</div>
<ul class="list-group hide-x-overflow" ng-style="::{maxHeight: c.getMaxHeight()}" style="overflow-y: auto;">
<li ng-if="c.data.list.length > 0" ng-repeat="item in c.data.list track by item.sys_id" class="list-group-item">
<a ng-click="c.onClick($event, item, item.url, {})" href="javascript:void(0)" oncontextmenu="return false;">
<span ng-repeat="action in c.data.actions" href="" ng-click="c.onClick($event, item, action.url, action)" ng-if="action.glyph"
class="list-action l-h-40 pull-right">
<fa name="{{action.glyph}}" ng-class="c.getActionColor(action)" />
</span>
<span ng-if="c.options.image_field" class="pull-left m-r"
ng-class="{'avatar': c.options.rounded_images, 'thumb-sm': c.options.rounded_images}">
<img ng-src="{{item.image_field}}" alt="..." class="img-sm" ng-class="{'img-circle': c.options.rounded_images}">
</span>
<div>
<div ng-switch on="item.display_field.type" ng-class="{'l-h-40': !item.secondary_fields.length}">
<span class="translated-html" ng-switch-when="translated_html" ng-bind-html="item.display_field.value"></span>
<div ng-switch-default>{{item.display_field.display_value}}</div>
</div>
<small class="text-muted" ng-repeat="f in item.secondary_fields">
<span ng-if="!$first"> • </span>
<span ng-switch="f.type" title="{{::f.label}}">
<span ng-switch-when="glide_date">
<span ng-if="!f.isFuture"> <sn-day-ago date="::f.value" /> </span>
<span ng-if="f.isFuture"> {{f.display_value}}</span>
</span>
<span ng-switch-when="glide_date_time">
<span ng-if="!f.isFuture"> <sn-time-ago timestamp="::f.value" /></span>
<span ng-if="f.isFuture"> {{f.display_value}}</span>
</span>
<span ng-switch-default="">{{f.display_value}}</span>
</span>
</small>
</div>
</a>
</li>
<div ng-if="!c.data.list.length" class="list-group-item">
${No records found}
</div>
</ul>
<div class="panel-footer" ng-if="!c.options.hide_footer && c.options.maximum_entries && c.data.count > c.options.maximum_entries">
<div class="h4 number-shown-label">{{c.getMaxShownLabel(c.options.maximum_entries, c.data.count)}}</div>
<a class="pull-right" ng-href="?id={{c.seeAllPage}}&table={{c.options.table}}&filter={{c.options.filter}}{{c.targetPageID}}">${View all}</a>
</div>
</div>
CSS:
.panel {
position: relative;
}
.panel-heading i {
cursor: pointer;
position: absolute;
padding: 10px;
top: 0px;
right: 0px;
cursor: pointer;
}
.disabled-filter {
color: #A0A0A0;
}
.list-group-item.ng-enter {
transition: all 1s;
-webkit-transition: all 1s;
background-color: #c0dcfa;
}
.list-group-item.ng-enter-active {
background-color: #fff;
}
.hide-x-overflow {
overflow-x: hidden;
}
.translated-html > p {
margin: 0px;
padding: 0px;
}
IMG {
max-width: 320px;
max-height: 240px;
}
IMG.img-sm {
max-height: 40px;
max-width: 40px;
}
.filter-box {
margin-top: 10px;
}
.panel-footer {
.number-shown-label {
margin-top: 0;
margin-bottom: 0;
font-size: 16px;
display: inline-block;
}
a {
color: inherit;
}
}
.list-group-item > a {
display: inline-block;
}
Server Script:
(function () {
data.filterMsg = gs.getMessage("Filter...");
if(gs.nil(options.hide_footer))
options.hide_footer = false;
options.hide_footer = (options.hide_footer == "true" || options.hide_footer == true);
options.table = $sp.getParameter('t') || options.table;
if (!options.table)
return;
var gr = new GlideRecordSecure(options.table); // does ACL checking for us
if(!gr.isValid()) {
data.isValid = false;
return;
} else
data.isValid = true;
// grTemp is used to check isValidField since using GlideRecordSecure fails for date/time fields
var grTemp = new GlideRecord(options.table);
gr.addEncodedQuery(options.filter);
options.title = options.title || gr.getPlural();
options.display_field = $sp.getParameter('f') || options.display_field;
if (!options.display_field || !grTemp.isValidField(options.display_field))
options.display_field = gr.getDisplayName();
if (input && input.filterText) {
gr.addEncodedQuery(options.display_field + "LIKE" + input.filterText)
}
options.title = options.title || gr.getPlural();
options.secondary_fields = options.secondary_fields || "";
options.secondary_fields = options.secondary_fields.split(",");
if (!options.order_by || !grTemp.isValidField(options.order_by))
options.order_by = options.display_field;
// Set ID of sp_page from option schema
if (options.list_page) {
var sp_page = GlideRecord('sp_page');
if (sp_page.get(options.list_page))
options.list_page_dv = sp_page.getDisplayValue('id');
}
// redo query with limit
if (options.order_direction == "asc")
gr.orderBy(options.order_by);
else
gr.orderByDesc(options.order_by);
data.maxCount = 500;
gr.setLimit(data.maxCount);
gr.query();
data.count = gr.getRowCount();
data.actions = getActions();
data.list = [];
var recordIdx = 0;
while (gr.next()) {
if (options.maximum_entries && recordIdx == options.maximum_entries)
break;
var record = {};
if (data.actions.length > 0) {
var fields = gr.getFields();
for (var i = 0; i < fields.size(); i++) {
var glideElement = fields.get(i);
var name = glideElement.getName();
if (name.indexOf("sys_") == -1)
record[name] = gr.getValue(name);
}
}
record.sys_id = gr.getValue('sys_id');
record.className = gr.getRecordClassName();
if (options.image_field) {
record.image_field = gr.getDisplayValue(options.image_field);
if (!record.image_field)
record.image_field = "noimage.pngx";
}
if (options.display_field)
record.display_field = getField(gr, options.display_field);
record.secondary_fields = [];
options.secondary_fields.forEach(function(f) {
record.secondary_fields.push(getField(gr, f));
});
if (options.sp_page) {
var view = "sp";
if (options.view) {
var viewGR = new GlideRecord("sys_ui_view");
viewGR.get(options.view);
view = viewGR.getValue("name");
}
record.url = {id: options.sp_page, table: record.className, sys_id: record.sys_id, view: view};
} else if (options.url != '')
record.url = options.url;
else
record.url = null;
data.list.push(record);
recordIdx++;
}
function getField(gr, name) {
var f = {};
f.display_value = gr.getDisplayValue(name);
f.value = gr.getValue(name);
var ge = gr.getElement(name);
if (ge == null)
return f;
f.type = ge.getED().getInternalType();
if (f.type == "glide_date_time")
f.isFuture = gs.dateDiff(gr.getValue(name), gs.nowNoTZ(), true) < 0;
else if (f.type == "glide_date")
f.isFuture = gs.dateDiff(gr.getValue(name), gs.now(), true) < 0;
f.label = ge.getLabel();
return f;
}
function getActions() {
var rl = GlideRecord("sp_vlist_action");
rl.addQuery("sp_rectangle_vlist",$sp.getValue("sys_id"));
rl.query();
var actions = [];
while(rl.next()) {
var action = {};
$sp.getRecordValues(action, rl, "name,glyph,hint,url,color");
actions.push(action);
}
return actions;
}
})()
Client Controller
function ($scope, $location, $rootScope, spUtil, $interpolate) {
var c = this;
this.data.filterText = "";
this.showFilter = false;
this.onClick = function($event, item, url, action) {
$event.stopPropagation();
$event.preventDefault();
if (typeof url == "string") {
var urlExp = $interpolate(url);
url = urlExp(item);
$location.url(url);
} else if (url && typeof url == "object")
$location.search(url);
else {
var evt = {};
evt.url = url;
evt.table = item.className;
evt.sys_id = item.sys_id;
evt.record = item;
evt.rectangle_id = c.options.sys_id;
evt.action = action;
// put out the selection with simple list "sl_" prefix
$location.search('sl_sys_id', evt.sys_id);
$location.search('sl_table', evt.table);
$location.search('spa', 1); // spa means "I've got this"
$rootScope.$broadcast('$sp.list.click', evt);
}
};
if (c.options.table && c.options.disable_record_watcher != 'true')
spUtil.recordWatch($scope, c.options.table, c.options.filter);
this.getMaxShownLabel = function(maxEntries, totalCount) {
if (totalCount == c.data.maxCount)
return "${First [0] of more than [1]}".replace('[0]', maxEntries).replace('[1]', totalCount);
return "${First [0] of [1]}".replace('[0]', maxEntries).replace('[1]', totalCount);
};
this.seeAllPage = c.options.list_page_dv || 'list';
this.targetPageID = (c.options.sp_page) ? "&target_page_id=" + c.options.sp_page : "";
c.getMaxHeight = function() {
return c.options.panel_body_height || 'none';
};
c.getActionColor = function(action) {
return "text-" + action.color;
};
c.update = function update() {
c.server.update();
}
c.toggleFilter = function() {
c.showFilter = !c.showFilter;
}
}
Solved! Go to Solution.
- Labels:
-
Service Catalog

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-09-2020 06:28 AM
Okay,
HTML body change below line
<a ng-click="c.onClick($event, item, item.url, {})" href="javascript:void(0)" oncontextmenu="return false;">
to
<a oncontextmenu="return false;">
This should give you expected result.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-09-2020 06:04 AM
Hi,
I could recreate the widget and links are working fine for me. Below is what I did.
Note: to achieve this, you do not need to create/ clone widget.
- I opened the page in designer by pressing CTRL+right click together on any widget of page. like below
- Dragged simple list widget below the approval widget and clicked on edit icon.
Here you can set all the needed options. Link to the page, where your links will navigate to.
Additionally,
You can go to widget on page and click ctrl+right click, you will see the same option window.
To set the title, you will have to visit sp_instance table and set title there.
Please mark correct answer if this helps.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-09-2020 06:08 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-09-2020 06:28 AM
Okay,
HTML body change below line
<a ng-click="c.onClick($event, item, item.url, {})" href="javascript:void(0)" oncontextmenu="return false;">
to
<a oncontextmenu="return false;">
This should give you expected result.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
01-28-2021 06:21 AM
I know there's an accepted solution there, but there was no mention of using the browser's dev tools (the 'inspect on a right-click', the debugging strategies within ServiceNow for determining why the links don't work (using the <pre> tag in html to output objects; console.log, gs.log, spUtil.addInfoMessage, etc.), or cloning strategies. As described in a reply, this seemed like it could be solved in the simple list widget, but sometimes you do have to clone to get where you need to go. I landed here looking for information on widget instance list actions (sp_vlist_action table), and that might even be in the ballpark for the solution here. Take a look at the following:
https://serviceportal.io/simple-list-widget/
Anyway, my two cents. Peace.