Adding addition tabindex option to open in New tab from list view portal

DB1
Tera Contributor

Hello All,

I have a cloned widge of Copy Data Instance from table "Instance with Table [sp_instance_table]" and is there a way that open in new tab could be added along with "show matching" etc

DB1_0-1669291800736.png

 

@Ankur Bawiskar @Mark Roethof @Community Alums @Mohith Devatte @Anil Lande @Jaspal Singh 

Open in New tab or ctrl option from Portal list view

Please advise 

Also, is there a way to change the backend/help text of the search from "Keyword Search" to something else?

 

I am looking for options to open any Incident record in a new window when selected from the data table cloned widget

 

Thanks,

DB

3 ACCEPTED SOLUTIONS

@DB1 Did the solution solve your problem? 

So my code  would have created a new button and when you click on the button it opens the record in new window

 

if not let me know if you need any alternative

 

If this solved the issue can you please mark the answer correct and close the thread so that it will be helpful for others in future?

View solution in original post

@DB1 but i can see the OOB code is this in my PDI .Did you change any code ?

just replace your widget id in the last line instead of test

 

(function(){
	/*  "use strict"; - linter issues */
	// populate the 'data' object
	var sp_page = $sp.getValue('sp_page');
	var pageGR = new GlideRecord('sp_page');
	pageGR.get(sp_page);
	data.page_id = pageGR.getValue("id");
	$sp.getValues(data);
	if (data.field_list) {
		data.fields_array = data.field_list.split(',');
	} else {
		data.field_list = $sp.getListColumns(data.table);
	}

	if (input) {
		data.p = input.p;
		data.o = input.o;
		data.d = input.d;
		data.q = input.q;
	}
	data.p = data.p || 1;
	data.o = data.o || $sp.getValue('order_by');
	data.d = data.d || $sp.getValue('order_direction');

	data.page_index = (data.p - 1);
	data.window_size = $sp.getValue('maximum_entries') || 10;
	data.window_start  = (data.page_index * data.window_size);
	data.window_end = (((data.page_index + 1) * data.window_size));
	data.filter = $sp.getValue("filter");

	var gr = new GlideRecordSecure(data.table);
	if (!gr.isValid()) {
		data.invalid_table = true;
		data.table_label = data.table;
		return;
	}
	data.table_label = gr.getLabel();

	options.table = data.table;
	options.fields = data.field_list;
	options.o=data.o;
	options.d= data.d;
	options.filter=data.filter;
	options.window_size=data.window_size;
	options.view = data.view;
	options.useInstanceTitle = true; // to make sure Data Table widget uses headerTitle always
	options.headerTitle = options.title;
	options.show_breadcrumbs=true;
	options.enable_filter=true;
	data.dataTableWidget = $sp.getWidget('test', options);
})();

 

 

View solution in original post

@DB1 Sure in the client controller i handled the redirection part in this below function at the top of your client controller script find it and replace it with below code 

c.openRecord=function(id)
{
window.open("/sp?id=ticket&table="+c.data.table+"&sys_id="+id ,"_blank");

}

 

Hope this helps 

Accept this solution if it helped you 

Thanks

View solution in original post

24 REPLIES 24

@Mohith Devatte 

The inner widget client is different though

(function() {
	if (!input) // asynch load list
		return;

	data.msg = {};
	data.msg.sortingByAsc = gs.getMessage("Sorting by ascending");
	data.msg.sortingByDesc = gs.getMessage("Sorting by descending");

	/*
	 * 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
	 */
	// 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', 'window_size']);
	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.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') || 'asc';
	data.useTinyUrl = gs.getProperty('glide.use_tiny_urls') === 'true';
	data.tinyUrlMinLength = gs.getProperty('glide.tiny_url_min_length');

	var grForMetaData = new GlideRecord(data.table);

	if (input.setOrderUserPreferences) {
		// update User Preferences on a manual sort for UI consistency
		gs.getUser().savePreference(data.table + ".db.order", data.o);
		gs.getUser().savePreference(data.table + ".db.order.direction", data.d == "asc" ? "" : "DESC");
		data.setOrderUserPreferences = false;
	}
	// if no sort specified, find a default column for UI consistency
	if (!data.o)
		getOrderColumn();
	
	data.page_index = data.p - 1;
	data.show_new = data.show_new || options.show_new;
	var windowSize = data.window_size || $sp.getValue('maximum_entries') || 20;
	windowSize = parseInt(windowSize);
	if (isNaN(windowSize) || windowSize < 1)
		windowSize = 20;
	data.window_size = windowSize;

	var gr;
	// FilteredGlideRecord is not supported in scoped apps, so GlideRecordSecure will always be used in an application scope
	if (typeof FilteredGlideRecord != "undefined" && (gs.getProperty("glide.security.ui.filter") == "true" || grForMetaData.getAttribute("glide.security.ui.filter") != null)) {
		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();
	data.title = input.useInstanceTitle && input.headerTitle ? gs.getMessage(input.headerTitle) : data.table_plural;
	data.hasTextIndex = $sp.hasTextIndex(data.table);
	if (data.filter) {
		if (data.filterACLs)
			gr = $sp.addQueryString(gr, data.filter);
		else
			gr.addEncodedQuery(data.filter);
	}
	if (data.keywords) {
		gr.addQuery('123TEXTQUERY321', data.keywords);
		data.keywords = null;
	}

	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
		data.exportQuery = target.getEncodedQuery();
		gr.addEncodedQuery(data.exportQuery); // get the query the relationship made for us
	}
	if (data.exportQuery)
		data.exportQuery += '^' + data.filter;
	else
		data.exportQuery = data.filter;
	data.exportQueryEncoded = encodeURIComponent(data.exportQuery);
	if (data.o){
		if (data.d == "asc")
			gr.orderBy(data.o);
		else
			gr.orderByDesc(data.o);
		if (gs.getProperty("glide.secondary.query.sysid") == "true")
			gr.orderBy("sys_id");
	}

	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.setCategory("service_portal_list");
	gr._query();

	data.row_count = gr.getRowCount();
	data.num_pages = Math.ceil(data.row_count / data.window_size);
	data.column_labels = {};
	data.column_types = {};
	data.fields_array = data.fields.split(',');

	// use GlideRecord to get field labels vs. GlideRecordSecure
	for (var i in data.fields_array) {
		var field = data.fields_array[i];
		var ge = grForMetaData.getElement(field);
		if (ge == null)
			continue;

		data.column_labels[field] = ge.getLabel();
		data.column_types[field] = ge.getED().getInternalType();
	}

	data.list = [];
	while (gr._next()) {
		var record = {};
		$sp.getRecordElements(record, gr, data.fields);
		if (typeof FilteredGlideRecord != "undefined" && 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.targetTable = gr.getRecordClassName();
		data.list.push(record);
	}

	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);

	// copy to data from input or options
	function optCopy(names) {
		names.forEach(function(name) {
			data[name] = input[name] || options[name];
		})
	}

	// getOrderColumn logic mirrors that of Desktop UI when no sort column is specified
	function getOrderColumn() {
		// First check for user preference
		var pref = gs.getUser().getPreference(data.table + ".db.order");
		if (!GlideStringUtil.nil(pref)) {
			data.o = pref;
			if (gs.getUser().getPreference(data.table + ".db.order.direction") == "DESC")
				data.d = 'desc';
			return;
		}

		// If no user pref, check for table default using same logic as Desktop UI:
		// 1) if task, use number
		// 2) if any field has isOrder attribute, use that
		// 3) use order, number, name column if exists (in that priority)
		if (grForMetaData.isValidField("sys_id") && grForMetaData.getElement("sys_id").getED().getFirstTableName() == "task") {
			data.o = "number";
			return;
		}
		
		// Next check for isOrder attribute on any column
		var elements = grForMetaData.getElements();
		// Global and scoped GlideRecord.getElements return two different things,
		// so convert to Array if needed before looping through
		if (typeof elements.size != "undefined") {
			var elementArr = [];
			for (var i = 0; i < elements.size(); i++)
				elementArr.push(elements.get(i));
			elements = elementArr;
		}
		// Now we can loop through
		for (var j = 0; elements.length > j; j++) {
			var element = elements[j];
			if (element.getAttribute("isOrder") == "true") {
				data.o = element.getName();
				return;
			}
		}
		// As last resort, sort on Order, Number, or Name column
		if (grForMetaData.isValidField("order"))
			data.o = "order";
		else if (grForMetaData.isValidField("number"))
			data.o = "number";
		else if (grForMetaData.isValidField("name"))
			data.o = "name";
	}
	
})();

@DB1 this is the server script that you pasted 

We got nothing to do with server script 

 

Try to compare the client script and HTML and replace the code above and try 

try this code in Client controller and HTML 

 

I have highlighted the code which i have added to the OOB code .

 

Let m know if this solutions works .

IF you want an alternative to this i can suggest one more alternative 

 

Also for changing the text "key word search" i have highlights the place where you have to change the text

HTML:

 

<div class="panel panel-{{options.color}} b" ng-class="{'data-table-high-contrast': accessibilityModeEnabled}">
<div class="panel-heading form-inline" ng-hide="options.hide_header">
<span class="dropdown m-r-xs">
<button aria-label="{{data.title || data.table_plural}} ${Context Menu}" class="btn dropdown-toggle glyphicon glyphicon-menu-hamburger" style="line-height: 1.4em" id="optionsMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
<ul class="dropdown-menu" aria-labelledby="optionsMenu">
<li ng-repeat="t in ::exportTypes">
<a ng-if="!tinyUrlEnabled" ng-href="/{{data.table}}_list.do?{{::t.value}}&sysparm_query={{data.exportQueryEncoded}}&sysparm_view={{data.view}}&sysparm_fields={{data.fields}}" target="_new" tabindex="-1">${Export as} {{::t.label}}</a>
<a ng-if="tinyUrlEnabled" ng-href="/{{data.table}}_list.do?{{::t.value}}&sysparm_tiny={{tinyUrl}}" target="_new" tabindex="-1">${Export as} {{::t.label}}</a>
</li>
</ul>
</span>
<h2 class="panel-title" style="display:inline"><i ng-if="options.glyph" class="fa fa-{{options.glyph}} m-r"></i>{{data.title || data.table_plural}}<span class="sr-only">${table} - ${page} {{data.p}}</span></h2>
<button name="new" role="button" class="btn btn-primary btn-sm m-l-xs" ng-click="newRecord()" ng-if="options.show_new && data.canCreate && !data.newButtonUnsupported" aria-label="${Create new record}">${New}</button>
<div class="pull-right" ng-if="options.show_keywords">
<form ng-if="data.hasTextIndex" ng-submit="setSearch(true)">
<div class="input-group" role="presentation">
<input type="text" name="datatable-search" ng-model="data.keywords" ng-model-options="{debounce:250}" class="form-control" placeholder="${Keyword Search}" aria-label="${Keyword Search}">
<span class="input-group-btn">
<button name="search" class="btn btn-default" type="submit" aria-label="${Search}" data-original-title="{{::c.data.searchMsg}}" data-toggle="tooltip" data-placement="bottom"><span class="glyphicon glyphicon-search"></span></button>
</span>
</div>
</form>
</div>
<div class="clearfix"></div>
</div>
<!-- body -->
<div class="panel-body">
<div ng-if="options.show_breadcrumbs && (data.filter || data.enable_filter)" class="filter-breadcrumbs">
<sp-widget widget="data.filterBreadcrumbs"></sp-widget>
</div>
<div class="clearfix"></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">
<caption class="sr-only">{{data.title || data.table_plural}}</caption>
<thead>
<tr>
<th ng-repeat="field in data.fields_array track by $index" class="text-nowrap" ng-click="setOrderBy(field)"
scope="col" role="columnheader" aria-sort="{{field == data.o ? (data.d == 'asc'? 'ascending': 'descending') : 'none'}}">
<div class="th-title" title="${Sort by} {{field == data.o ? (data.d == 'asc' ? '${Descending}': '${Ascending}') : '${Ascending}'}}" role="button" tabindex="0" aria-label="{{data.column_labels[field]}}">{{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>
<th>Actions</th>
</tr>

</thead>
<tbody>
<tr ng-repeat="item in data.list track by item.sys_id">
<td role="{{$first ? 'rowheader' : 'cell'}}" class="pointer sp-list-cell" ng-class="{selected: item.selected}" ng-click="go(item.targetTable, item)" ng-repeat="field in ::data.fields_array" data-field="{{::field}}" data-th="{{::data.column_labels[field]}}"><a href="javascript&colon;void(0)" ng-if="$first" aria-label="${Open record}: {{::item[field].display_value}}">{{::item[field].display_value | limitTo : item[field].limit}}{{::item[field].display_value.length > item[field].limit ? '...' : ''}}</a><span ng-if="!$first">{{::item[field].display_value | limitTo : item[field].limit}}{{::item[field].display_value.length > item[field].limit ? '...' : ''}}</span>
</td>
<td> <button ng-click ="c.openRecord(item.sys_id)">
Open Record
</button></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" role="navigation" aria-label="${Pagination}">
<div class="btn-toolbar m-r pull-left">
<div class="btn-group">
<a href="javascript&colon;void(0)" ng-click="setPageNum(data.p - 1)" ng-class="{'disabled': data.p == 1}" class="btn btn-default" aria-label="${Previous page} {{data.p == 1 ? '${disabled}' : ''}}" tabindex="{{(data.p == 1) ? -1 : 0}}"><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&colon;void(0)" ng-class="{active: ($index + 1) == data.p}" type="button" class="btn btn-default" aria-label="${Page} {{$index + 1}}" ng-attr-aria-current="{{($index + 1) == data.p ? 'page' : undefined}}">{{$index + 1}}</a>
</div>
<div class="btn-group">
<a href="javascript&colon;void(0)" ng-click="setPageNum(data.p + 1)" ng-class="{'disabled': data.p == data.num_pages}" class="btn btn-default" aria-label="${Next page} {{data.p == data.num_pages ? '${disabled}' : ''}}" tabindex="{{(data.p == data.num_pages) ? -1 : 0}}"><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>

 

 

Client controller :

function ($scope, $location, $window, spUtil, amb, $http, spAriaUtil, $timeout, spNavStateManager, i18n) {
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 separated list of fields that become the list columns
* view (string) = the default view to load for columns, overrides fields
*/
$scope.accessibilityModeEnabled = spAriaUtil.isAccessibilityEnabled();
$scope.exportTypes = [{label:'PDF', value: 'PDF'}, {label:'Excel', value:'EXCEL'}, {label:'CSV', value:'CSV'}];
var keys = ['table', 'filter', 'p', 'o', 'd'];

c.openRecord=function(id)
{
window.open("/sp?id=form&table="+c.data.table+"&sys_id="+id ,"_blank");

}
var i18nMsgs = {
filteredResults: i18n.getMessage('Filtered {0} list showing {1} to {2} of {3} records'),
unFilteredResults: i18n.getMessage('Unfiltered {0} list showing {1} to {2} of {3} records'),
filteredNoResults: i18n.getMessage('Filtered {0} list showing 0 records'),
unFilteredNoResults: i18n.getMessage('Unfiltered {0} list showing 0 records')
};

var eventNames = {
click: 'data_table.click',
setFilter: 'data_table.setFilter',
setKeywords: 'data_table.setKeywords'
};

$scope.go = function(table, item) {
if ($window.getSelection().toString().length > 0)
return;

spNavStateManager.onRecordChange(table).then(function() {
var parms = {};
parms.table = table;
parms.sys_id = item.sys_id;
parms.record = item;
$scope.ignoreLocationChange = true;
for (var x in c.data.list) {
c.data.list[x].selected = false;
}
item.selected = true;
$scope.$emit(eventNames.click, parms);
}, function() {
// do nothing in case of closing the modal by clicking on x
});
};

$scope.checkAndSetTinyUrl = function() {
var url = $scope.data.table + '_list.do?' + 'sysparm_query=' + $scope.data.exportQueryEncoded + '&sysparm_view=' + $scope.data.view + '&sysparm_fields=' + $scope.data.fields;
$scope.tinyUrlEnabled = c.data.useTinyUrl && url.length >= c.data.tinyUrlMinLength;
if ($scope.tinyUrlEnabled)
$scope.getTinyUrl(url);
};

$scope.getTinyUrl = function(url) {
$http.post('/api/now/tinyurl', {
url: url
}).then(function(response) {
$scope.tinyUrl = new URL($window.location.origin + '/' + response.data.result).searchParams.get('sysparm_tiny');
});
};

$scope.newRecord = function(){
var parms = {
id: 'form',
table: $scope.data.table,
view: $scope.data.view,
sys_id: '-1'
};
if ($scope.data.exportQuery != '')
parms.query = $scope.data.exportQuery;

$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) {
var origSearch = $location.search();
$scope.$on('$locationChangeSuccess', function(e) {
var s = $location.search();
if (origSearch.id !== s.id)
return;

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, data.filter);

invokeResultsLiveMessage();
initRecordWatcher(f.table, f.filter);
$scope.checkAndSetTinyUrl();
});
}


function invokeResultsLiveMessage(){
var data = $scope.data;
var totalRowCount = data.row_count;
var startIndex = data.window_start+1;
var endIndex = Math.min(data.window_end, totalRowCount);

if (totalRowCount > 0)
spAriaUtil.sendLiveMessage( (data.filter ? i18nMsgs.filteredResults : i18nMsgs.unFilteredResults).withValues([data.table_plural, startIndex, endIndex, totalRowCount]));
else
spAriaUtil.sendLiveMessage( (data.filter ? i18nMsgs.filteredNoResults : i18nMsgs.unFilteredNoResults).withValues([data.table_plural]));
}

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(message) {
if (!message.data)
return;
switch(message.data.action) {
case "change":
updateRowFromRW(message);
break;
case "exit":
// A record was removed
case "enter":
// A record was added
default:
spUtil.update($scope);
break;
}
});
}
}

function updateRowFromRW(message) {
if (message.data && message.data.sys_id && $scope.data.list) {
var row, field;
for(var i=0;i<$scope.data.list.length; i++) {
row = $scope.data.list[i];
if (row.sys_id == message.data.sys_id) {
var fields = Object.getOwnPropertyNames(message.data.record);
for(var f in fields) {
field = fields[f];
if(typeof row[field] !== 'undefined') {
row[field].display_value = message.data.record[field].display_value;
}
}
}
}
}
}

$scope.$on('$destroy', function() {
if (watcher)
watcher.unsubscribe();
});

$scope.setPageNum = function(num) {
$scope.data.p = num;
getData(true);
$timeout(function() {
$scope.focusOnTableHeader();
});
}

$scope.setOrderBy = function(field) {
var d = "asc";
// descending default sort for date/time columns for UI consistency
var fieldType = $scope.data.column_types[field];
if (fieldType == "glide_date_time" || fieldType == "glide_date")
d = "desc";
if ($scope.data.o == field) {
if ($scope.data.d == "asc")
d = "desc";
else
d = "asc";
}

if (d === "asc") {
spAriaUtil.sendLiveMessage($scope.data.msg.sortingByAsc);
} else if (d === "desc") {
spAriaUtil.sendLiveMessage($scope.data.msg.sortingByDesc);
}

$scope.data.o = field;
$scope.data.d = d;
$scope.data.setOrderUserPreferences = true;
$scope.setSearch(true);
}

$scope.setSearch = function(updateUrl) {
$scope.data.p = 1;
if ($scope.data.keywords) {
var previousSearchTerm = $scope.previousSearchTerm;
if (previousSearchTerm) {
previousSearchTerm = '123TEXTQUERY321=' + previousSearchTerm;
var previousSearchTermStartIndex = $scope.data.filter.indexOf(previousSearchTerm);
var previousSearchTermEndIndex = previousSearchTermStartIndex + previousSearchTerm.length;
if (previousSearchTermStartIndex >= 0)
$scope.data.filter = $scope.data.filter.substr(0, previousSearchTermStartIndex) + $scope.data.filter.substr(previousSearchTermEndIndex + 1, $scope.data.filter.length);
}
$scope.previousSearchTerm = $scope.data.keywords;
}
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
$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;
initRecordWatcher($scope.data.table, $scope.data.filter);
$scope.checkAndSetTinyUrl();
});

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.isMultiPart = function(terms) {
for (var i = 0; i < terms.length; i++) {
var term = terms[i];
while (term.left)
term = term.left;
if (term.NQ)
return true;
}
return false;
}

c.showMatching = function(field, newTerm) {
var queryString = $scope.data.filter;
var eq = "";
parseQuery($scope.data.table, queryString).then(function(oldTerms) {
var isMultiPart = c.isMultiPart(oldTerms);
for (var i = 0; i < oldTerms.length; i++) {
var term = oldTerms[i];
if (!isMultiPart && isSameField(newTerm, term))
continue;

if (eq.length)
eq += '^';

// term may be separated into nested "left" and "right" bits,
// follow the lefts to the bottom where "NQ" might be specified
var termNQCheck = oldTerms[i];
while (termNQCheck.left)
termNQCheck = termNQCheck.left;
if (termNQCheck.NQ) {
// query is multipart so apply new term to each part
eq += getEncodedTerm(newTerm);
eq += "^NQ";
}
eq += getEncodedTerm(term);
}
if (eq.length)
eq += '^';
eq += getEncodedTerm(newTerm);

$scope.data.filter = eq;
$scope.setSearch();
$location.search(angular.extend($location.$$search, {spa: 1, filter: eq,}));
});
};

c.filterOut = function(field, newTerm) {
var eq = "";
if ($scope.data.filter.indexOf("^NQ") == -1) {
// don't need server roundtrip to parse query,
// can just append the new term
eq = $scope.data.filter;
if (eq.length)
eq += '^';
eq += getEncodedTerm(newTerm);
$scope.data.filter = eq;
$scope.setSearch();
$location.search(angular.extend($location.$$search, {spa: 1, filter: eq,}));
return;
}

// query may be multipart so must apply new term to each part
var queryString = $scope.data.filter;
parseQuery($scope.data.table, queryString).then(function(oldTerms) {
for (var i = 0; i < oldTerms.length; i++) {
var term = oldTerms[i];
if (eq.length)
eq += '^';

// term may be separated into nested "left" and "right" bits,
// follow the lefts to the bottom where "NQ" might be specified
var termNQCheck = oldTerms[i];
while (termNQCheck.left)
termNQCheck = termNQCheck.left;
if (termNQCheck.NQ) {
// query is multipart so apply new term to end of each part
eq += getEncodedTerm(newTerm);
eq += "^NQ";
}
eq += getEncodedTerm(term);
}
if (eq.length)
eq += '^';
eq += getEncodedTerm(newTerm);

$scope.data.filter = eq;
$scope.setSearch();
$location.search(angular.extend($location.$$search, {spa: 1, filter: eq,}));
});
};

function isSameField(t1, t2) {
if ('left' in t1 && 'left' in t2)
return t1.left.query_term_field === t2.left.query_term_field;
else if ('left' in t1)
return t1.left.query_term_field === t2.query_term_field;
else if ('left' in t2)
return t1.query_term_field === t2.left.query_term_field;
return t1.query_term_field === t2.query_term_field;
}

function getEncodedTerm(term) {
var eq;
if (term.left) {
eq = getEncodedTerm(term.left);
eq += '^OR';
eq += getEncodedTerm(term.right);
} else {
eq = term.query_term_field;
eq += term.operator;
eq += term.value;
}
return eq;
}
}

 

 

Mark my answer correct if this helps you 

Thanks

 

 

@DB1 try this code in Client controller and HTML 

 

 

Let m know if this solutions works .

IF you want an alternative to this i can suggest one more alternative 

 

Also for changing the text "key word search" i have highlights the place where you have to change the text

HTML:

 

 

<div class="panel panel-{{options.color}} b" ng-class="{'data-table-high-contrast': accessibilityModeEnabled}">
<div class="panel-heading form-inline" ng-hide="options.hide_header">
<span class="dropdown m-r-xs">
<button aria-label="{{data.title || data.table_plural}} ${Context Menu}" class="btn dropdown-toggle glyphicon glyphicon-menu-hamburger" style="line-height: 1.4em" id="optionsMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
<ul class="dropdown-menu" aria-labelledby="optionsMenu">
<li ng-repeat="t in ::exportTypes">
<a ng-if="!tinyUrlEnabled" ng-href="/{{data.table}}_list.do?{{::t.value}}&sysparm_query={{data.exportQueryEncoded}}&sysparm_view={{data.view}}&sysparm_fields={{data.fields}}" target="_new" tabindex="-1">${Export as} {{::t.label}}</a>
<a ng-if="tinyUrlEnabled" ng-href="/{{data.table}}_list.do?{{::t.value}}&sysparm_tiny={{tinyUrl}}" target="_new" tabindex="-1">${Export as} {{::t.label}}</a>
</li>
</ul>
</span>
<h2 class="panel-title" style="display:inline"><i ng-if="options.glyph" class="fa fa-{{options.glyph}} m-r"></i>{{data.title || data.table_plural}}<span class="sr-only">${table} - ${page} {{data.p}}</span></h2>
<button name="new" role="button" class="btn btn-primary btn-sm m-l-xs" ng-click="newRecord()" ng-if="options.show_new && data.canCreate && !data.newButtonUnsupported" aria-label="${Create new record}">${New}</button>
<div class="pull-right" ng-if="options.show_keywords">
<form ng-if="data.hasTextIndex" ng-submit="setSearch(true)">
<div class="input-group" role="presentation">
<input type="text" name="datatable-search" ng-model="data.keywords" ng-model-options="{debounce:250}" class="form-control" placeholder="${Keyword Search}" aria-label="${Keyword Search}">
<span class="input-group-btn">
<button name="search" class="btn btn-default" type="submit" aria-label="${Search}" data-original-title="{{::c.data.searchMsg}}" data-toggle="tooltip" data-placement="bottom"><span class="glyphicon glyphicon-search"></span></button>
</span>
</div>
</form>
</div>
<div class="clearfix"></div>
</div>
<!-- body -->
<div class="panel-body">
<div ng-if="options.show_breadcrumbs && (data.filter || data.enable_filter)" class="filter-breadcrumbs">
<sp-widget widget="data.filterBreadcrumbs"></sp-widget>
</div>
<div class="clearfix"></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">
<caption class="sr-only">{{data.title || data.table_plural}}</caption>
<thead>
<tr>
<th ng-repeat="field in data.fields_array track by $index" class="text-nowrap" ng-click="setOrderBy(field)"
scope="col" role="columnheader" aria-sort="{{field == data.o ? (data.d == 'asc'? 'ascending': 'descending') : 'none'}}">
<div class="th-title" title="${Sort by} {{field == data.o ? (data.d == 'asc' ? '${Descending}': '${Ascending}') : '${Ascending}'}}" role="button" tabindex="0" aria-label="{{data.column_labels[field]}}">{{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>
<th>Actions</th>
</tr>

</thead>
<tbody>
<tr ng-repeat="item in data.list track by item.sys_id">
<td role="{{$first ? 'rowheader' : 'cell'}}" class="pointer sp-list-cell" ng-class="{selected: item.selected}" ng-click="go(item.targetTable, item)" ng-repeat="field in ::data.fields_array" data-field="{{::field}}" data-th="{{::data.column_labels[field]}}"><a href="javascript&colon;void(0)" ng-if="$first" aria-label="${Open record}: {{::item[field].display_value}}">{{::item[field].display_value | limitTo : item[field].limit}}{{::item[field].display_value.length > item[field].limit ? '...' : ''}}</a><span ng-if="!$first">{{::item[field].display_value | limitTo : item[field].limit}}{{::item[field].display_value.length > item[field].limit ? '...' : ''}}</span>
</td>
<td> <button ng-click ="c.openRecord(item.sys_id)">
Open Record
</button></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" role="navigation" aria-label="${Pagination}">
<div class="btn-toolbar m-r pull-left">
<div class="btn-group">
<a href="javascript&colon;void(0)" ng-click="setPageNum(data.p - 1)" ng-class="{'disabled': data.p == 1}" class="btn btn-default" aria-label="${Previous page} {{data.p == 1 ? '${disabled}' : ''}}" tabindex="{{(data.p == 1) ? -1 : 0}}"><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&colon;void(0)" ng-class="{active: ($index + 1) == data.p}" type="button" class="btn btn-default" aria-label="${Page} {{$index + 1}}" ng-attr-aria-current="{{($index + 1) == data.p ? 'page' : undefined}}">{{$index + 1}}</a>
</div>
<div class="btn-group">
<a href="javascript&colon;void(0)" ng-click="setPageNum(data.p + 1)" ng-class="{'disabled': data.p == data.num_pages}" class="btn btn-default" aria-label="${Next page} {{data.p == data.num_pages ? '${disabled}' : ''}}" tabindex="{{(data.p == data.num_pages) ? -1 : 0}}"><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>

 

 

 

Client controller :

 

 

function ($scope, $location, $window, spUtil, amb, $http, spAriaUtil, $timeout, spNavStateManager, i18n) {
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 separated list of fields that become the list columns
* view (string) = the default view to load for columns, overrides fields
*/
$scope.accessibilityModeEnabled = spAriaUtil.isAccessibilityEnabled();
$scope.exportTypes = [{label:'PDF', value: 'PDF'}, {label:'Excel', value:'EXCEL'}, {label:'CSV', value:'CSV'}];
var keys = ['table', 'filter', 'p', 'o', 'd'];

c.openRecord=function(id)
{
window.open("/sp?id=form&table="+c.data.table+"&sys_id="+id ,"_blank");

}
var i18nMsgs = {
filteredResults: i18n.getMessage('Filtered {0} list showing {1} to {2} of {3} records'),
unFilteredResults: i18n.getMessage('Unfiltered {0} list showing {1} to {2} of {3} records'),
filteredNoResults: i18n.getMessage('Filtered {0} list showing 0 records'),
unFilteredNoResults: i18n.getMessage('Unfiltered {0} list showing 0 records')
};

var eventNames = {
click: 'data_table.click',
setFilter: 'data_table.setFilter',
setKeywords: 'data_table.setKeywords'
};

$scope.go = function(table, item) {
if ($window.getSelection().toString().length > 0)
return;

spNavStateManager.onRecordChange(table).then(function() {
var parms = {};
parms.table = table;
parms.sys_id = item.sys_id;
parms.record = item;
$scope.ignoreLocationChange = true;
for (var x in c.data.list) {
c.data.list[x].selected = false;
}
item.selected = true;
$scope.$emit(eventNames.click, parms);
}, function() {
// do nothing in case of closing the modal by clicking on x
});
};

$scope.checkAndSetTinyUrl = function() {
var url = $scope.data.table + '_list.do?' + 'sysparm_query=' + $scope.data.exportQueryEncoded + '&sysparm_view=' + $scope.data.view + '&sysparm_fields=' + $scope.data.fields;
$scope.tinyUrlEnabled = c.data.useTinyUrl && url.length >= c.data.tinyUrlMinLength;
if ($scope.tinyUrlEnabled)
$scope.getTinyUrl(url);
};

$scope.getTinyUrl = function(url) {
$http.post('/api/now/tinyurl', {
url: url
}).then(function(response) {
$scope.tinyUrl = new URL($window.location.origin + '/' + response.data.result).searchParams.get('sysparm_tiny');
});
};

$scope.newRecord = function(){
var parms = {
id: 'form',
table: $scope.data.table,
view: $scope.data.view,
sys_id: '-1'
};
if ($scope.data.exportQuery != '')
parms.query = $scope.data.exportQuery;

$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) {
var origSearch = $location.search();
$scope.$on('$locationChangeSuccess', function(e) {
var s = $location.search();
if (origSearch.id !== s.id)
return;

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, data.filter);

invokeResultsLiveMessage();
initRecordWatcher(f.table, f.filter);
$scope.checkAndSetTinyUrl();
});
}


function invokeResultsLiveMessage(){
var data = $scope.data;
var totalRowCount = data.row_count;
var startIndex = data.window_start+1;
var endIndex = Math.min(data.window_end, totalRowCount);

if (totalRowCount > 0)
spAriaUtil.sendLiveMessage( (data.filter ? i18nMsgs.filteredResults : i18nMsgs.unFilteredResults).withValues([data.table_plural, startIndex, endIndex, totalRowCount]));
else
spAriaUtil.sendLiveMessage( (data.filter ? i18nMsgs.filteredNoResults : i18nMsgs.unFilteredNoResults).withValues([data.table_plural]));
}

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(message) {
if (!message.data)
return;
switch(message.data.action) {
case "change":
updateRowFromRW(message);
break;
case "exit":
// A record was removed
case "enter":
// A record was added
default:
spUtil.update($scope);
break;
}
});
}
}

function updateRowFromRW(message) {
if (message.data && message.data.sys_id && $scope.data.list) {
var row, field;
for(var i=0;i<$scope.data.list.length; i++) {
row = $scope.data.list[i];
if (row.sys_id == message.data.sys_id) {
var fields = Object.getOwnPropertyNames(message.data.record);
for(var f in fields) {
field = fields[f];
if(typeof row[field] !== 'undefined') {
row[field].display_value = message.data.record[field].display_value;
}
}
}
}
}
}

$scope.$on('$destroy', function() {
if (watcher)
watcher.unsubscribe();
});

$scope.setPageNum = function(num) {
$scope.data.p = num;
getData(true);
$timeout(function() {
$scope.focusOnTableHeader();
});
}

$scope.setOrderBy = function(field) {
var d = "asc";
// descending default sort for date/time columns for UI consistency
var fieldType = $scope.data.column_types[field];
if (fieldType == "glide_date_time" || fieldType == "glide_date")
d = "desc";
if ($scope.data.o == field) {
if ($scope.data.d == "asc")
d = "desc";
else
d = "asc";
}

if (d === "asc") {
spAriaUtil.sendLiveMessage($scope.data.msg.sortingByAsc);
} else if (d === "desc") {
spAriaUtil.sendLiveMessage($scope.data.msg.sortingByDesc);
}

$scope.data.o = field;
$scope.data.d = d;
$scope.data.setOrderUserPreferences = true;
$scope.setSearch(true);
}

$scope.setSearch = function(updateUrl) {
$scope.data.p = 1;
if ($scope.data.keywords) {
var previousSearchTerm = $scope.previousSearchTerm;
if (previousSearchTerm) {
previousSearchTerm = '123TEXTQUERY321=' + previousSearchTerm;
var previousSearchTermStartIndex = $scope.data.filter.indexOf(previousSearchTerm);
var previousSearchTermEndIndex = previousSearchTermStartIndex + previousSearchTerm.length;
if (previousSearchTermStartIndex >= 0)
$scope.data.filter = $scope.data.filter.substr(0, previousSearchTermStartIndex) + $scope.data.filter.substr(previousSearchTermEndIndex + 1, $scope.data.filter.length);
}
$scope.previousSearchTerm = $scope.data.keywords;
}
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
$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;
initRecordWatcher($scope.data.table, $scope.data.filter);
$scope.checkAndSetTinyUrl();
});

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.isMultiPart = function(terms) {
for (var i = 0; i < terms.length; i++) {
var term = terms[i];
while (term.left)
term = term.left;
if (term.NQ)
return true;
}
return false;
}

c.showMatching = function(field, newTerm) {
var queryString = $scope.data.filter;
var eq = "";
parseQuery($scope.data.table, queryString).then(function(oldTerms) {
var isMultiPart = c.isMultiPart(oldTerms);
for (var i = 0; i < oldTerms.length; i++) {
var term = oldTerms[i];
if (!isMultiPart && isSameField(newTerm, term))
continue;

if (eq.length)
eq += '^';

// term may be separated into nested "left" and "right" bits,
// follow the lefts to the bottom where "NQ" might be specified
var termNQCheck = oldTerms[i];
while (termNQCheck.left)
termNQCheck = termNQCheck.left;
if (termNQCheck.NQ) {
// query is multipart so apply new term to each part
eq += getEncodedTerm(newTerm);
eq += "^NQ";
}
eq += getEncodedTerm(term);
}
if (eq.length)
eq += '^';
eq += getEncodedTerm(newTerm);

$scope.data.filter = eq;
$scope.setSearch();
$location.search(angular.extend($location.$$search, {spa: 1, filter: eq,}));
});
};

c.filterOut = function(field, newTerm) {
var eq = "";
if ($scope.data.filter.indexOf("^NQ") == -1) {
// don't need server roundtrip to parse query,
// can just append the new term
eq = $scope.data.filter;
if (eq.length)
eq += '^';
eq += getEncodedTerm(newTerm);
$scope.data.filter = eq;
$scope.setSearch();
$location.search(angular.extend($location.$$search, {spa: 1, filter: eq,}));
return;
}

// query may be multipart so must apply new term to each part
var queryString = $scope.data.filter;
parseQuery($scope.data.table, queryString).then(function(oldTerms) {
for (var i = 0; i < oldTerms.length; i++) {
var term = oldTerms[i];
if (eq.length)
eq += '^';

// term may be separated into nested "left" and "right" bits,
// follow the lefts to the bottom where "NQ" might be specified
var termNQCheck = oldTerms[i];
while (termNQCheck.left)
termNQCheck = termNQCheck.left;
if (termNQCheck.NQ) {
// query is multipart so apply new term to end of each part
eq += getEncodedTerm(newTerm);
eq += "^NQ";
}
eq += getEncodedTerm(term);
}
if (eq.length)
eq += '^';
eq += getEncodedTerm(newTerm);

$scope.data.filter = eq;
$scope.setSearch();
$location.search(angular.extend($location.$$search, {spa: 1, filter: eq,}));
});
};

function isSameField(t1, t2) {
if ('left' in t1 && 'left' in t2)
return t1.left.query_term_field === t2.left.query_term_field;
else if ('left' in t1)
return t1.left.query_term_field === t2.query_term_field;
else if ('left' in t2)
return t1.query_term_field === t2.left.query_term_field;
return t1.query_term_field === t2.query_term_field;
}

function getEncodedTerm(term) {
var eq;
if (term.left) {
eq = getEncodedTerm(term.left);
eq += '^OR';
eq += getEncodedTerm(term.right);
} else {
eq = term.query_term_field;
eq += term.operator;
eq += term.value;
}
return eq;
}
}

 

 

 

 

Mark my answer correct if this helps you 

Thanks

 

 

I updated the client and HTML as above on the inner widget

And updated the below line on Parent Widget

data.dataTableWidget = $sp.getWidget('copy_of_data_table_widget_2', options);

However when I select ctrl it still opens up on the same page not in a new window