
- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
on ‎05-11-2019 11:34 AM
I saw few questions about on how to show end user their tickets that are INC and REQ on same widget so I decided to write this article.
1. Create new widget called Name: My Ticket and ID: my-tickets
HTML:
<sp-panel>
<uib-tabset>
<uib-tab index="0" heading="Active Requests" uib-tooltip="View all active requests that you opened or that were opened on your behalf."><sp-widget widget="data.all_active"></sp-widget></uib-tab>
<uib-tab index="1" heading="Closed Requests" uib-tooltip="View all closed requests that you opened or that were opened on your behalf."><sp-widget widget="data.all_closed"></sp-widget></uib-tab>
</uib-tabset>
</sp-panel>
Server: (Change filter as you like)
(function(){
// Setup data tables
var activeFilter = 'sys_class_name=incident^state=1^ORstate=2^ORstate=4^ref_incident.caller_idDYNAMIC90d1921e5f510100a9ad2572f2b477fe^ORopened_byDYNAMIC90d1921e5f510100a9ad2572f2b477fe^NQactive=true^sys_class_name=sc_request^ref_sc_request.requested_forDYNAMIC90d1921e5f510100a9ad2572f2b477fe^ORopened_byDYNAMIC90d1921e5f510100a9ad2572f2b477fe';
var taskQueryFilter = 'short_descriptionLIKE{q}^ORnumberLIKE{q}^ORrequested_for.nameLIKE{q}^ORu_user.nameLIKE{q}^ORref_incident.caller_id.nameLIKE{q}^ORdescriptionLIKE{q}';
var all_active = {
"table": "task",
"view": "mobile",
"fields": "number,short_description,sys_created_on,sys_updated_on",
"show_keywords": true,
"show_breadcrumbs": false,
"o": "sys_created_on", // order_by
"filter": activeFilter,
"storef": activeFilter,
"query_filter": taskQueryFilter
};
var closedFilter = 'active=false^sys_class_name=incident^ORsys_class_name=sc_request';
var all_closed = {
"table": "task",
"view": "mobile",
"fields": "number,short_description,sys_created_on,sys_updated_on",
"show_keywords": true,
"show_breadcrumbs": false,
"o": "sys_created_on", // order_by
"filter": closedFilter,
"storef": closedFilter,
"query_filter": taskQueryFilter
};
data.all_active = $sp.getWidget("my_widget_data_table", all_active);
data.all_closed = $sp.getWidget("my_widget_data_table", all_closed);
// Look for any parameters for setting the active tab
data.tabName = $sp.getParameter('tab_focus') || 'all_active';
})();
Client:
function ($scope, $location, $rootScope, $uibModal, spUtil, $http) {
var c = this;
this.filterText = "";
this.showFilter = false;
$scope.$on('data_table.click', function(event, mass) {
event.stopPropagation();
event.preventDefault();
$location.search('id', 'ticket');
$location.search('sys_id', mass.sys_id);
$location.search('table', mass.table);
});
}
2. Create new widget called Name: My Data Table and ID: my_widget_data_table
HTML:
<div>
<div class="panel panel-{{options.color}} b">
<div class="panel-heading form-inline" ng-hide="options.hide_header">
<span class="dropdown m-r-xs">
<span class="dropdown-toggle glyphicon glyphicon-menu-hamburger" style="line-height: 1.4em" id="optionsMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"></span>
<ul class="dropdown-menu" aria-labelledby="optionsMenu">
<li ng-repeat="t in ::exportTypes">
<a ng-href="/{{data.table}}_list.do?{{::t.value}}&sysparm_query={{data.filter}}&sysparm_view={{data.view}}" target="_new">${Export as} {{::t.label}}</a>
</li>
</ul>
</span>
<span class="panel-title"><i ng-if="options.glyph" class="fa fa-{{options.glyph}} m-r"></i>{{data.title || data.table_plural}}</span>
<button name="new" type="button" class="btn btn-primary btn-sm m-l-xs" ng-click="newRecord()" ng-if="options.show_new && data.canCreate && !data.newButtonUnsupported">${New}</button>
<div class="pull-right" ng-if="options.show_keywords">
<form ng-submit="setSearch(true)">
<div class="input-group">
<input type="text" name="datatable-search" ng-model="data.keywords" class="form-control" placeholder="${Search}">
<span class="input-group-btn">
<button name="search" class="btn btn-default" type="submit"><span class="glyphicon glyphicon-search"></span></button>
</span>
</div>
<button name="new" type="button" class="btn btn-primary" ng-click="clearSearch()">${Clear Search}</button>
</form>
</div>
<div class="clearfix"></div>
</div>
<!-- body -->
<div class="panel-body">
<div ng-if="options.show_breadcrumbs && data.filter" class="filter-breadcrumbs">
<sp-widget widget="data.filterBreadcrumbs"></sp-widget>
</div>
<div class="alert alert-info" ng-if="!data.list.length && !data.num_pages && !data.invalid_table && !loadingData">
${No records in {{data.table_label}} <span ng-if="data.filter">using that filter</span>}
</div>
<div class="alert alert-info" ng-if="loadingData">
<fa name="spinner" spin="true"></fa> ${Loading data}...
</div>
<table class="table table-striped table-responsive" ng-if="data.list.length">
<thead>
<tr>
<th ng-repeat="field in data.fields_array track by $index" ng-click="setOrderBy(field)">
<div class="th-title">{{data.column_labels[field]}}</div>
<i class="fa" ng-if="field == data.o" ng-class="{'asc': 'fa-chevron-up', 'desc': 'fa-chevron-down'}[data.d]"></i>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in data.list track by item.sys_id">
<td class="pointer" ng-class="{selected: item.selected}" ng-click="go(data.table, item)" ng-repeat="field in data.fields_array" data-field="{{field}}" data-th="{{data.column_labels[field]}}">{{item[field].display_value}}</td>
</tr>
</tbody>
</table>
<div ng-class="{'pruned-msg-filter-pad': (!options.show_breadcrumbs || !data.filter) && !data.list.length}" class="pruned-msg" ng-if="rowsWerePruned()">
<span ng-if="rowsPruned == 1">${{{rowsPruned}} row removed by security constraints}</span>
<span ng-if="rowsPruned > 1">${{{rowsPruned}} rows removed by security constraints}</span>
</div>
</div>
<!-- footer -->
<div class="panel-footer" ng-hide="options.hide_footer" ng-if="data.row_count">
<div class="btn-toolbar m-r pull-left">
<div class="btn-group">
<a ng-disabled="data.p == 1" href="javascript:void(0)" ng-click="setPageNum(data.p - 1)" class="btn btn-default"><i class="fa fa-chevron-left"></i></a>
</div>
<div ng-if="data.num_pages > 1 && data.num_pages < 20" class="btn-group">
<a ng-repeat="i in getNumber(data.num_pages) track by $index" ng-click="setPageNum($index + 1)" href="javascript:void(0)" ng-class="{active: ($index + 1) == data.p}" type="button" class="btn btn-default">{{$index + 1}}</a>
</div>
<div class="btn-group">
<a ng-disabled="data.p == data.num_pages" href="javascript:void(0)" ng-click="setPageNum(data.p + 1)" class="btn btn-default"><i class="fa fa-chevron-right"></i></a>
</div>
</div>
<div class="m-t-xs panel-title">${Rows {{data.window_start + 1}} - {{ mathMin(data.window_end,data.row_count) }} of {{data.row_count}}}</div>
<span class="clearfix"></span>
</div>
</div>
</div>
CSS:
.panel-heading {
padding-left: 8px;
}
thead {
border-bottom: 1px solid #ddd;
}
table {
margin-bottom: 0;
}
.table > thead > tr > th {
border: 1px solid #ddd;
cursor: pointer;
vertical-align: middle;
&:first-child {
border-left: none;
}
&:last-child {
border-right: none;
}
}
th i {
display: inline-block;
margin-left: 5px;
color: #A0A0A0;
}
th .disabled{
color:#ddd;
}
.th-title {
display: inline-block;
color: #428bca;
}
.panel-body {
overflow: auto;
padding: 0px;
}
.selected {
color: #fff;
background-color: #909090;
border-color: 1px solid #fff;
}
tbody tr:last-child {
border-bottom: none;
}
.pruned-msg {
padding-bottom: 10px;
padding-left: 4px;
text-align: center;
}
.pruned-msg-filter-pad {
padding-top:8px;
}
.filter-breadcrumbs {
border-bottom: 1px solid #ddd;
padding-top: 3px;
}
Server:
(function() {
if (!input) // asynch load list
return;
data.title = options.title || input.title;
/*
* data.table = the table
* data.p = the current page starting at 1
* data.o = the order by column
* data.d = the order by direction
* data.keywords = the keyword search term
* data.list = the table data as an array
* data.invalid_table = true if table is invalid or if data was not succesfully fetched
* data.table_label = the table's display name. e.g. Incident
* data.table_plural = the table's plural display name. e.g. Incidents
* data.fields = a comma delimited list of field names to show in the data table
* data.column_labels = a map of field name -> display name
* data.window_size = the number of rows to show
* data.filter = the encoded query
* data.query_filter = encoded query template to use when searching this table
*/
// copy to data[name] from input[name] || option[name]
optCopy(['table', 'p', 'o', 'd', 'filter', 'filterACLs', 'fields', 'keywords', 'view']);
optCopy(['relationship_id', 'apply_to', 'apply_to_sys_id']);
optCopy(['query_filter']); // NYT Custom: Allow custom query
if (!data.table) {
data.invalid_table = true;
data.table_label = "";
return;
}
if (!data.fields) {
if (data.view)
data.fields = $sp.getListColumns(data.table, data.view);
else
data.fields = $sp.getListColumns(data.table);
}
data.view = data.view || 'mobile';
data.table = data.table || $sp.getValue('table');
data.filter = data.filter || $sp.getValue('filter');
data.keywords = data.keywords || $sp.getValue('keywords');
data.textSearch = data.textSearch || ($sp.getValue('text_search') == 'true');
data.query_filter = data.query_filter || $sp.getValue('keywords') || '';
data.p = data.p || $sp.getValue('p') || 1;
data.p = parseInt(data.p);
data.o = data.o || $sp.getValue('o') || $sp.getValue('order_by');
data.d = data.d || $sp.getValue('d') || $sp.getValue('order_direction');
data.window_size = data.window_size || $sp.getValue('maximum_entries') || 20;
data.page_index = data.p - 1;
data.show_new = data.show_new || options.show_new;
/*
| Custom data property to store if a session is encrypted
| Used in navigation decisions to redirect or not
|
*/
data.isEncryptedSession = gs.isEdgeEncryptedSession();
var gr;
if (gs.getProperty("glide.security.ui.filter") == "true" || GlideTableDescriptor.get(data.table).getED().hasAttribute("glide.security.ui.filter")) {
gr = new FilteredGlideRecord(data.table);
gr.applyRowSecurity();
} else
gr = new GlideRecordSecure(data.table);
if (!gr.isValid()) {
data.invalid_table = true;
data.table_label = data.table;
return;
}
data.canCreate = gr.canCreate();
data.newButtonUnsupported = data.table == "sys_attachment";
data.table_label = gr.getLabel();
data.table_plural = gr.getPlural();
if (data.filter) {
if (data.filterACLs)
gr = $sp.addQueryString(gr, data.filter);
else
gr.addEncodedQuery(data.filter);
}
if (data.keywords){
data.queryString = data.query_filter.replace(/\{q\}/g, data.keywords);
gr.addEncodedQuery(data.queryString);
}
//data.filter = gr.getEncodedQuery();
if (data.relationship_id) {
var rel = GlideRelationship.get(data.relationship_id);
var target = new GlideRecord(data.table);
var applyTo = new GlideRecord(data.apply_to);
applyTo.get("sys_id", data.apply_to_sys_id);
rel.queryWith(applyTo, target); // put the relationship query into target
gr.addEncodedQuery(target.getEncodedQuery()); // get the query the relationship made for us
}
if (data.o){
if (data.d == "asc")
gr.orderBy(data.o);
else
gr.orderByDesc(data.o);
}
data.window_start = data.page_index * data.window_size;
data.window_end = (data.page_index + 1) * data.window_size;
gr.chooseWindow(data.window_start, data.window_end);
gr._query();
data.row_count = gr.getRowCount();
data.num_pages = Math.ceil(data.row_count / data.window_size);
data.column_labels = {};
data.fields_array = data.fields.split(',');
// use GlideRecord to get field labels vs. GlideRecordSecure
var grForLabels = new GlideRecord(data.table);
for (var i in data.fields_array) {
var field = data.fields_array[i];
var ge = grForLabels.getElement(field);
if (ge == null)
continue;
data.column_labels[field] = ge.getLabel();
}
data.list = [];
while (gr._next()) {
var record = {};
$sp.getRecordElements(record, gr, data.fields);
if (gr instanceof FilteredGlideRecord) {
// FilteredGlideRecord doesn't do field-level
// security, so take care of that here
for (var f in data.fields_array) {
var fld = data.fields_array[f];
if (!gr.isValidField(fld))
continue;
if (!gr[fld].canRead()) {
record[fld].value = null;
record[fld].display_value = null;
}
}
}
record.sys_id = gr.getValue('sys_id');
record.table = gr.getValue('sys_class_name');
data.list.push(record);
}
var breadcrumbWidgetParams = { table: data.table, query: data.filter };
data.filterBreadcrumbs = $sp.getWidget('widget-filter-breadcrumbs', breadcrumbWidgetParams);
// copy to data from input or options
function optCopy(names) {
names.forEach(function(name) {
data[name] = input[name] || options[name];
})
}
})();
Client:
function ($scope, $location, spUtil, amb, $http) {
var c = this;
/*
* options:
* hide_footer (bool) = true to remove the data table footer contents
* hide_header (bool) = true to remove the data table header contents
* show_new (bool) = true to show the "New" record button
* show_keywords (bool) = true to show the keyword search field
* table (string) = the table name to query
* filter (string) = the encoded query
* o (string) = the order by column
* d (string) = The order by direction: asc or desc
* p (int) = the page to jump to
* fields (string) = comma seperated list of fields that become the list columns
* view (string) = the default view to load for columns, overrides fields
*/
$scope.clearSearch = function(){
$scope.data.filter = $scope.data.storef;
$scope.data.keywords = null;
$scope.setSearch(true);
};
$scope.exportTypes = [{label:'PDF', value: 'PDF'}, {label:'Excel', value:'EXCEL'}, {label:'CSV', value:'CSV'}];
var keys = ['table', 'filter', 'p', 'o', 'd'];
var eventNames = {
click: 'data_table.click',
setFilter: 'data_table.setFilter',
setKeywords: 'data_table.setKeywords'
};
$scope.go = function(table, item) {
var parms = {};
parms.table = item.table || table;
parms.sys_id = item.sys_id;
parms.record = item;
parms.isEncryptedSession = c.data.isEncryptedSession;
$scope.ignoreLocationChange = true;
for (var x in c.data.list) {
c.data.list[x].selected = false;
}
item.selected = true;
$scope.$emit(eventNames.click, parms);
};
$scope.newRecord = function(){
var parms = {
id: 'form',
table: $scope.data.table,
sys_id: '-1'
};
if ($scope.data.filter != '')
parms.query = $scope.data.filter;
$location.search(parms);
};
function recoverStateFromUrl() {
$scope.data.fields = [];
var s = $location.search();
for (var x in keys) {
if (s[keys[x]]) {
$scope.data[keys[x]] = s[keys[x]];
}
}
$scope.server.update().then(function(data) {
if (s.sys_id) {
for (var x in data.list) {
if (data.list[x].sys_id == s.sys_id) {
$scope.go(s.table, data.list[x]);
}
}
}
});
}
if ($scope.options.fromUrl) {
$scope.$on('$locationChangeSuccess', function(e) {
if ($scope.ignoreLocationChange){
$scope.ignoreLocationChange = false;
return;
}
// Helps to recover state when using the browser's back button
recoverStateFromUrl();
});
}
$scope.getNumber = function(num) {
return new Array(num);
}
$scope.mathMin = function(v1,v2) {
return Math.min(v1,v2);
}
function getData(updateUrl) {
var f = $scope.data;
spUtil.update($scope).then(function(data) {
f.view = data.view;
if ($scope.options.fromUrl && updateUrl)
setPermalink(f.table, f.filter, f.o, f.d, f.p);
if ($scope.options.show_breadcrumbs && data.filterBreadcrumbs)
$scope.$broadcast('widget-filter-breadcrumbs.setBreadcrumbs', data.filterBreadcrumbs.data);
initRecordWatcher(f.table, f.filter);
});
}
function setPermalink(table, filter, orderBy, orderDirection, page){
$scope.ignoreLocationChange = true;
var search = $location.search();
angular.extend(search, {
spa: 1,
table: table,
filter: filter,
p: page,
o: orderBy,
d: orderDirection
});
$location.search(search);
}
var watcher;
function initRecordWatcher(table, filter){
if (watcher)
watcher.unsubscribe();
if (table && filter) {
var watcherChannel = amb.getChannelRW(table, filter);
amb.connect();
watcher = watcherChannel.subscribe(function() {
spUtil.update($scope)
});
}
}
$scope.setPageNum = function(num) {
$scope.data.p = num;
getData(true);
}
$scope.setOrderBy = function(field) {
var d = "asc";
if ($scope.data.o == field) {
if ($scope.data.d == "asc")
d = "desc";
else
d = "asc";
}
$scope.data.o = field;
$scope.data.d = d;
$scope.setSearch(true);
}
$scope.setSearch = function(updateUrl) {
$scope.data.p = 1;
getData(updateUrl);
}
$scope.$on(eventNames.setFilter, function(e, newFilter){
$scope.data.filter = newFilter;
$scope.setSearch(false);
});
$scope.$on(eventNames.setKeywords, function(e, keywords){
$scope.data.keywords = keywords;
$scope.setSearch(false);
});
$scope.$on('widget-filter-breadcrumbs.queryModified', function(e, newFilter){
$scope.data.filter = newFilter;
$scope.setSearch(true);
});
$scope.rowsWerePruned = function() {
if (!$scope.data.list)
return;
$scope.rowsPruned = $scope.mathMin($scope.data.window_end,$scope.data.row_count) - $scope.data.window_start - $scope.data.list.length;
return $scope.rowsPruned > 0;
}
$scope.showFilter = function() {
return !$scope.data.list.length && !$scope.data.num_pages && !$scope.data.invalid_table && !$scope.loadingData;
}
c.appendQuery = function(query){
if ($scope.data.filter.length > 1)
$scope.data.filter += '^';
$scope.data.filter += query;
$scope.setSearch();
}
// Makes Widget Async
var title = $scope.data.title;
if ($scope.options.use_instance_title == 'true')
title = $scope.options.title;
$scope.data = $scope.options;
$scope.loadingData = true;
$scope.server.update().then(function() {
if ($scope.data.newButtonUnsupported)
console.log("Service Portal: New button not supported for sys_attachment list");
$scope.loadingData = false;
$scope.data.title = title;
initRecordWatcher($scope.data.table, $scope.data.filter);
});
function parseQuery(table, queryString){
return $http.post('/api/now/sp/parsequery/' + table, queryString).then(function(response){
return response.data.result;
});
}
c.createQueryTerm = function(table, field, sys_id, operator){
return $http.get('/api/now/sp/getInOutQueryTerm', {
params: {
table: table,
sys_id: sys_id,
field: field,
operator: operator
}
}).then(function(response){
if (response && response.data && response.data.result)
return response.data.result.parts;
});
}
c.showMatching = function(field, newTerm) {
var queryString = $scope.data.filter;
var eq = "";
parseQuery($scope.data.table, queryString).then(function(oldTerms) {
for(var i=0; i<oldTerms.length; i++){
var term = oldTerms[i];
if (isSameField(newTerm, term))
continue;
if (eq.length)
eq += '^';
eq += getEncodedTerm(term);
}
if (eq.length)
eq += '^';
eq += getEncodedTerm(newTerm);
$scope.data.filter = eq;
$scope.setSearch();
});
};
c.filterOut = function(field, newTerm) {
var eq = $scope.data.filter;
if (eq.length)
eq += '^';
eq += getEncodedTerm(newTerm);
$scope.data.filter = eq;
$scope.setSearch();
};
function isSameField(t1, t2) {
if ('left' in t1 && 'left' in t2)
return t1.left.field === t2.left.field;
else if ('left' in t1)
return t1.left.field === t2.field;
else if ('left' in t2)
return t1.field === t2.left.field;
return t1.field === t2.field;
}
function getEncodedTerm(term) {
var eq;
if (term.left) {
eq = getEncodedTerm(term.left);
eq += '^OR';
eq += getEncodedTerm(term.right);
} else {
eq = term.field;
eq += term.operator;
eq += term.value;
}
return eq;
}
}
3. Create new page called Name: My Tickets and ID: my_tickets
Final Result:
Bonus: (Add link in header)
got to sp_rectangle_menu_item.list and click on New and make it same as below
- 20,397 Views


- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Nicely done. I know people tend to walk a fine line between Incidents and Requests so if your (the person who wants this) organization is mature enough to not get confused by this, then by all means, use it.
One small discrepancy is the "Active Requests" and "Closed Requests" tabs...not sure if I would call those tabs that since it deals with more than just Requests, but again, I'm sure that's very easy to change. Just my 2 cents on the matter.
This is awesome though Mike. Thank you for doing the hard work for us!

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Thanks & Yes, It's pretty easy to change in HTML code.
<uib-tab index="0" heading="Active Tickets"

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
What if we did not want to use the work ticket or request. Instead have a 2 separate pages one to show open and closed incidents and one to show open and closed requests. Would I need to create a widget for incidents and a widget for requests?

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
If you want 2 pages then you will need 2 widgets one for each type
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
kindly let me know to add short description for request , as of now i can isee "item name , request number" when i click on "My Requests"
now i want to see " item name , short description and request number" . kindly let me know code and where can we update that code.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Thanks Mike. This information is very helpful. How can we add breadcrumbs to this so that we can filter the query.

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
On my tickets widget server script (2nd script on this article) you will see
"show_breadcrumbs": false,
change that to true so it shows breadcrumbs.
Only issue will be any filter you might have applied will be visible to user.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Thanks for the information Mike it worked. Much Appreciated. I can see the breadcrumbs now. What i have noticed is that if change the filter to All it removes the filter and I cannot filter anything. How should i be changing this which allows use of filter on the table.

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Since breadcrumbs is set to display true then it will show all filters and user will be able to remove your stored filter.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi Mike, I have create first widhet. but no data is getting show when i preview wighet.
just buttons are coming.

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi, You need to complete whole thing. There is no preview.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
HI Mike, Thanks for your responce. If we'll completed all steps as mention on PDI Instance then information will display. Do we need to change anything.

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
I saw your other post since you are trying to get catalog item and asset info so there are some changes you need to do. I added xml file to your post.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi Mike,
thanks for the information. breadcrumbs set to true works but we cannot change the filter as there is no option available. if we click on All it removes the filter and we cannot filter anything as no option available.
How can the behavior be changed so that we can change or edit the filter.
Thanks
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
I tried to modify Font, background , lenth of backcolour for "My Watched Items" same like as "My Approvals" showing in below screenshot , i try to modify in "my-tickets" widget and "my_widget_data_table" widget CSS code , but no luck 😞
Kindly help me the code for the same

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Are you trying to change the My watched Items label color from white to some another color?
Like below
Css
.nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus {
color: red;
background-color: limegreen;
border: 1px solid #ddd;
border-bottom-color: transparent;
cursor: default;
}
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi Mike,
Thanks alot for your help , this widget output looks like "Box under box" but the image should like "my approval" single box. Request you please help on this.

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
If you want to add Title like below then you have to do it on widget instance.
Add Title
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content


- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
I can't see the image but My Approval is different widget type so it will not be the same.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Thank you Mike , when i see number , short description ...etc fields but table borders not appearing , kindly help me to add borders on table , am using below css code
.nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus {
color: black;
background-color: #a7b6d1;
border: 1px solid #ddd;
border-bottom-color: transparent;
width: 338px;
cursor: default;
}
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Kindly alter code can see like below only
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Great widget! I love how you made it so easy to scale up with more tabs!
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi,
Thank you so much for this, it is awesome and a great addition to the service portal!
I am trying to add a new tab for change requests and have added the additional code required in the body HTML, the new query in the server script but when I load the page, I'm getting the new tab but it's empty - has anyone else added more tabs and I have overlooked something else that needs to be added to make the data appear for the additional tab.
I have tried the same filtering on the "closed requests" code and it works as required, so I know the filter etc is correct, so I must have missed something that helps render the data.
Are there any changes needed in the my_widget_data_table widget as I've currently only updated the "my-tickets" widget.
Any help would be appreciated as adding the extra tabs will allow me to put everything all in one place.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi, have you managed to add more tabs? I have tried by not able to get the data to appear - thinking I've missed something somewhere.

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Can you share your code so I can look through it.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi, thanks for coming back to me.
I have added the following:
Body HTML
<uib-tab index="2" heading="Change Requests" uib-tooltip="View all Change Requests that you opened or that were opened on your behalf."><sp-widget widget="data.all_changes"></sp-widget></uib-tab>
Server script
var changeFilter = 'sys_class_name=change_request';
var all_changes = {
"table": "task",
"view": "mobile",
"fields": "number,sys_created_on,sys_updated_on",
"show_keywords": true,
"show_breadcrumbs": true,
"o": "sys_created_on", // order_by
"filter": changeFilter,
"storef": changeFilter,
"query_filter": taskQueryFilter
};
data.all_change = $sp.getwidget("my_widget_data_table", all_changes);
Does anything need to be added to the other file?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
ignore the typo in the last statement - that has been corrected and no data still happens
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi Mike -
Great article, it was very helpful!
How would we get rid of the text 'Tasks' next to the hamburger icon for the export types?
Also, how can we edit the text when there are no records found?

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
On my_widget_data_table widget removed bolded part.
<span class="panel-title"><i ng-if="options.glyph" class="fa fa-{{options.glyph}} m-r"></i>{{data.title || data.table_plural}}</span>
same widget has HTML below, That's where it comes from
<div class="alert alert-info" ng-if="!data.list.length && !data.num_pages && !data.invalid_table && !loadingData"> ${No records in {{data.table_label}} <span ng-if="data.filter">using that filter</span>} </div>
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi, did you get a chance to review my code? sorry to pester! Dan
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Thanks for this article.I have implemented this functionality but when i load the page for the first time it taking more time to load the data(data is very less). Do you have any suggestion to improve this?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
However, I am struggling to add/enable filters for End users to filter accordingly.
I have followed the below article and related community links, but no luck.
https://support.servicenow.com/kb?id=kb_article_view&sysparm_article=KB0687054
I believe you have used similar to 'data table from instance definition' widget?
Mike, can you please add filters/condition builder in your script and update here OR advise how to achieve this?
Many thanks in advance,
Sumanth
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Thanks very much for this solution
Regards,
Sivaranjani S
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
@Mike Patel thanks! this is awesome, one question though, what does that last line in the server script do?
data.tabName = $sp.getParameter('tab_focus') || 'all_active';

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
That's for the dynamic url if you want to pass the parameter that focuses onload.
if no parameter is passed then all_active tab is loaded.
In url if you pass tab_focus=all_closed then when it is loaded it will focuses on all closed tab.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi @Mike Patel Thank you so much for this, it is a brilliant piece of work. I would like to use this in the context of the cmdb_ci table to list devices by multiple different sys_class_name. How do I get the record to open with the relevant form instead of the activity form it currently opens with?
Many Thanks,
Simon.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hello @Mike Patel
Can you please tell me, where should I make changes in the code to show reqs from my custom table that does not extend from task table, it is custom table of scoped application. Kindly let me know.
Thankyou

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
@namrata23 Please share your code and I can review it.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi @Mike Patel,
Thanks for this. Is it possible to have it show Incidents and Tasks? Rather than incident and Requests?
- Sean

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
@Mike Patel This is exactly what I am looking for.
I have 5 different tables extended from Incident for 5 different departments. I want callers to have a view like you show here where they can search across those. Any other suggestions for me on how or what else I need to take into account for this? I am not a dev but have been the sole admin of our instance for 6 years ( small org).
My hope is I can mirror what you have and figure it out.

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Search results showing same as filter results but we want to check keyword search text in list view fields only
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi @Mike Patel
I have created widgets as you mentioned but for me getting like mentioned in the screen shot could you please let me know why I am not getting list view
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
- @Mike Patel @Can I call two diff page from those two tabs instead of calling two diff widget
- Actually I want to apply a page specific css to make filter read only but it is only working for the first tab ,for the second tab it is not working properly ,so I want to call two diff page from those tabs instead of widget like we call using href tag
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Do you think there is a way to adjust the server script in the my tickets widget to allow to check multiple custom tables that are not related to the Task table?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
@Mike Patel Thanks for this awesome code.
@Sumanth Now @Herin D
I managed to activate the filter widget. As Mike Patel mentioned in his posts, the filter is getting visible for end users by that. But that's another story.
I mainly used parts of the OOB Widget "Data Table" to enhance the code.
Here is the solution:
In the "My Data Table" widget
1. HTML
replace this part
<div ng-if="options.show_breadcrumbs && data.filter" class="filter-breadcrumbs">
<sp-widget widget="data.filterBreadcrumbs"></sp-widget>
</div>​
with this
<div ng-if="options.show_breadcrumbs && (data.filter || data.enable_filter)" class="filter-breadcrumbs">
<sp-widget widget="data.filterBreadcrumbs"></sp-widget>
</div>
2. Server Script
replace lines 151 & 152
var breadcrumbWidgetParams = { table: data.table, query: data.filter,enable_filter: data.enable_filter};
data.filterBreadcrumbs = $sp.getWidget('widget-filter-breadcrumbs', breadcrumbWidgetParams);​
with this
data.enable_filter = (input.enable_filter == true || input.enable_filter == "true" ||
options.enable_filter == true || options.enable_filter == "true");
var breadcrumbWidgetParams = {
table: data.table,
query: data.filter,
enable_filter: data.enable_filter
};
data.filterBreadcrumbs = $sp.getWidget('widget-filter-breadcrumbs', breadcrumbWidgetParams);​
In the "My Tickets" widget
1. Server Script
In the options for the tabs - change "show_breadcrumbs" to true and add
"enable_filter": true,​
so it looks like this:
var all_active = {
"table": "task",
"view": "mobile",
"fields": "number,short_description,sys_created_on,sys_updated_on",
"show_keywords": true,
"show_breadcrumbs": true,
"enable_filter": true,
"o": "sys_created_on", // order_by
"filter": activeFilter,
"storef": activeFilter,
"query_filter": taskQueryFilter
};​
Result: (showing the default "activeFilter" of the widget - better change it when using this)