Service Portal search [ Item name + short description ], Please help

David Cross
Tera Expert

Hello All, I am new to SP and Angular. Can you please help me with a widget.
Here on search option i only get Item name, But instead we need Item name + short description

this is my Client code & below that is HTML code, Can you please suggest me what changes are required here to get Item name + short description

Service Portal Search :  [ We need Item name + Short description ]

find_real_file.png

 

Client Code:

 

function ($http, $filter, $location,spAriaUtil, $window, $scope, spAriaFocusManager, snAnalytics) {
var c = this;
c.options.glyph = c.options.glyph || 'search';
c.options.title = c.options.title || c.data.searchMsg;
c.options.color = c.options.color || "default";
c.searchTerm = c.data.q;
c.searchQuery = "";
c.pageID = $scope.page && $scope.page.id;
c.showSuggestions = c.data.searchTypeBehavior === "suggestions" && c.data.isSuggestionsEnabled === "true";
c.suggestionsLimit = c.options.limit > 0 ? c.options.limit : "";
c.latitude = null;
c.longitude = null;
c.isGlideSignalsLoaded = false;
c.isLocationTrackerDisabled = c.data.isLocationTrackerDisabled === "true";
c.sendAnalytics = function(type){
var payload= {};
payload.name = "Initiate Search";
payload.data = {};
payload.data["Keyword"] = (type == 'User Entered' ? c.searchTerm : c.searchQuery);
payload.data["Type"] = type;
payload.data["Page ID"] = c.pageID;
snAnalytics.addEvent(payload);
};

function initializeGlideSignals() {
if (!c.isLocationTrackerDisabled && window.GlideSignals.init) {
window.GlideSignals.init();
}
if (window.GlideSignals.trackEvent) {
c.isGlideSignalsLoaded = true;
}
}

if (window.GlideSignals)
initializeGlideSignals();

c.trackSuggestionsRenderedEvent = function(searchQueryLength, responseTimeInMilliSeconds){
if(c.isGlideSignalsLoaded)
GlideSignals.trackEvent('SEARCH_SUGGESTIONS_RENDERED', GlideSignals.priority.INFO,
{'applicationId': c.data.portalID,
'searchQueryLength': searchQueryLength,
'totalSuggestionsCount': c.totalSuggestionsCount,
'userHistorySuggestionsCount' : c.userHistorySuggestionsCount,
'instanceHistorySuggestionsCount' : c.instanceHistorySuggestionsCount,
'responseTime': responseTimeInMilliSeconds+' ms'
});
};

c.trackSearchClickedEvent = function(model){
if(c.isGlideSignalsLoaded) {
if(c.showSuggestions) {
GlideSignals.trackEvent('SEARCH_SUGGESTION_CLICKED', GlideSignals.priority.INFO,
{'applicationId': c.data.portalID,
'searchQueryLength' : c.searchQuery.length,
'suggestionClickedLength': model.name.length,
'totalSuggestionsCount': c.totalSuggestionsCount,
'suggestionClickedType': model.type,
'aggregatedClickIndex': getSearchItemIndex(c.searchItems, model),
'userHistorySuggestionsCount' : c.userHistorySuggestionsCount,
'instanceHistorySuggestionsCount' : c.instanceHistorySuggestionsCount,
'suggestionsDisplayLimit': c.suggestionsLimit,
'relativeClickIndex': getRelativeSearchItemIndex(c.searchItems, model)
})
}
else {
GlideSignals.trackEvent('SEARCH_TYPEAHEAD_CLICKED', GlideSignals.priority.INFO,
{'applicationId': c.data.portalID,
'searchQueryLength' : c.searchQuery.length,
'typeaheadClickedLength': model.name && model.name.length,
'resultSysId': model.sys_id,
'clickIndex': model.query_location != null ? model.query_location : getSearchItemIndex(c.searchItems, model),
'sourceId': model.type != null ? model.type : model.table,
'typeaheadDisplayLimit': c.options.limit
})
}
}
}

c.onSelect = function($item, $model, $label) {
c.sendAnalytics(c.showSuggestions ? "Suggestions" : "Typeahead");
c.searchTerm = ""; // prevents unexpected result if user quickly clicks search button after selecting
if (c.showSuggestions)
$item.url = "?id=search&q="+encodeURIComponent($item.name);

if(!$item.url || $item.url === "")
return;

if (!c.showSuggestions) {
var index = $(".typeahead-popup li.active").data('index');
c.trackSearchResultClicked(index + 1);
}

c.trackSearchClickedEvent($model);

if ($item.target)
window.open($item.url, $item.target);
else {
var newUrl = $location.url($item.url);
spAriaFocusManager.navigateToLink(newUrl.url());
}
};

function recordSuggestionsCount(){
c.instanceHistorySuggestionsCount = 0;
c.userHistorySuggestionsCount = 0;

c.searchItems.forEach(function(item){
return item.type === 'INSTANCE_HISTORY' ? c.instanceHistorySuggestionsCount++ : c.userHistorySuggestionsCount++;
});
}

function getSearchItemIndex(items, targetItem) {
return (items || []).findIndex(function(item) {
return item.name === targetItem.name;
});
}

function getRelativeSearchItemIndex(items, targetItem) {
var groupedItems = (items || []).filter(function(item) {
return item.type === targetItem.type;
});

return getSearchItemIndex(groupedItems, targetItem);
}

c.getSearchSuggestions = function(query) {
c.searchQuery = query;
if ($location.search().q == c.searchQuery)
return;

var payload = {
params: {
"sysparm_term" : c.searchQuery,
"sysparm_sp_portal_id": c.data.portalID,
"sysparm_suggestions_limit": c.suggestionsLimit,
"sysparm_search_sources": c.data.searchSourceSysIds || ""
},
headers : {'Accept' : 'application/json'}
};

var requestTimeStamp = new Date().getTime();
return $http.get("/api/now/search/sp_suggestions", payload).then(function(response){
var responseTimeStamp = new Date().getTime();
var responseTimeInMilliSeconds = (responseTimeStamp - requestTimeStamp);
var result = response.data.result;

c.totalSuggestionsCount = result != null ? result.entries.length : 0;
sendLiveMessage(c.totalSuggestionsCount);

c.searchItems = result.entries.map(function(item) {
item.query = getQueryToHighlight(item, c.searchQuery);
item.glyph = getIcon(item.type);
item.term = item.name;
return item;
});

recordSuggestionsCount();
c.trackSuggestionsRenderedEvent(query.length, responseTimeInMilliSeconds);
return c.searchItems;
});
   };

function getSearchSources(results, c) {
var sources = {};
c.data.searchSources.map(function(key) {
sources[key] = 0;
});
results.map(function(item) {
if(sources[item.type])
sources[item.type]++;
else
sources[item.type] = 1;
});
var searchSources = [];
Object.keys(sources).map(function(key) {
var source_id = c.data.searchSourceConfiguration[key] ? c.data.searchSourceConfiguration[key].sys_id : key;
searchSources.push({
source_id: source_id,
number_of_results: sources[key]
});
});
return searchSources;
}

function getSearchResultsSignals(results, c) {
return results.map(function(item) {
var recordId = item.sys_id;
var tableName = item.table;

return {
record_id: recordId,
table_name: tableName
};
});
}

function setUserLocationCoords(cb) {
var onSuccess = function(pos) {
return cb({
latitude: pos.coords.latitude,
longitude: pos.coords.longitude
})
};

var onError = function() {
return cb({
latitude: null,
longitude: null
});
};

return window.navigator.geolocation.getCurrentPosition(onSuccess, onError, {
enableHighAccuracy: true
});
}

if(!c.isLocationTrackerDisabled) {
setUserLocationCoords(function(coords) {
c.latitude = coords.latitude;
c.longitude = coords.longitude;
});
}

function getResultDescription(result){
var description = null;
if(result.name != null)
description = result.name;
else if(result.primary != null)
description = result.primary;
else if(result.sec_title != null)
description = result.sec_title;
return description;
}

c.trackSearchResultClicked = function(rank) {
if (!rank || rank < 1) return ;
var query = c.latestQuery;
var results = _.get(c.data, 'results', []);
var result = results[rank-1];
var sourceTable = result.table != null ? result.table : null;

var payloadObject = {
action: "GlideSPSearchAnalyticsUpdateRank",
payload: {
query: query,
portal_id: this.data.portalID,
page_id: this.pageID,
results_per_source: getSearchSources(results, this),
search_results: getSearchResultsSignals(results, this),
refinement_occurred: false,
browser_info: $window.navigator.userAgent,
location: {
latitude: c.latitude,
longitude: c.longitude
},
result_event_sys_id : result.sys_id,
label_description : getResultDescription(result),
source_table: sourceTable,
signal_type: "CLICK",
signal_value: rank
}
};

$window.spSearchAnalytics = {
query: query
};
c.server.get(payloadObject);
}

c.getResults = function(query) {
c.searchQuery = query;
var payload = {
"query": c.searchQuery,
"portal": c.data.portalID,
"page": c.pageID,
"source": c.data.searchSources,
"include_facets": false,
"searchType": "typeahead"
};


if (c.options.limit || c.options.limit == 0)
payload.count = c.options.limit;

return $http.post("/api/now/sp/search", payload).then(function(response) {
// Prevents typeahead from displaying suggestions if queries from page and input are the same
if ($location.search().q == c.searchQuery)
return;

var result = response.data.result;
var resultCount = result != null ? result.results.length : 0
sendLiveMessage(resultCount);

c.data.results = result.results;
c.latestQuery = c.searchQuery;

c.searchItems = result.results.map(function(item) {
var config = c.data.searchSourceConfiguration[item.__search_source_id__];

if (!item.url && config.linkToPage) {
item.url = "?id=" + config.linkToPage;
if (item.sys_id)
item.url += "&sys_id=" + item.sys_id;
if (item.table)
item.url += "&table=" + item.table
}

if (config.type == "ADVANCED") {
item.templateID = config.template;
} else {
item.glyph = config.glyph;
}
return item;
});
if (c.searchItems.length == 0)
c.searchItems = [{"primary": c.data.noResultsFoundMsg}];

return c.searchItems;
});
}

c.searchType = c.data.searchType;
$scope.$on('$locationChangeSuccess', onLocationChangeSuccess);

function onLocationChangeSuccess(event, newUrl, oldUrl) {
if(searchSourceChanged(newUrl, oldUrl)) {
var newUrlParams = newUrl.match(/t=.+/);
if(!newUrlParams) {
c.searchType = null;
} else {
c.searchType = newUrlParams[0].split("&")[0].substring(2);
}
}
}

function searchSourceChanged(newUrl, oldUrl) {
var newUrlParams = newUrl.match(/t=.+/),
oldUrlParams = oldUrl.match(/t=.+/);

if(!newUrlParams && !oldUrlParams) {
return false;
}

if((!newUrlParams && oldUrlParams) || (newUrlParams && !oldUrlParams)) {
return true;
}

return newUrlParams[0].split("&")[0] !== oldUrlParams[0].split("&")[0];
}

c.submitSearch = function() {
c.sendAnalytics("User Entered");
var shouldReloadPage = c.data.refreshPageOnSearch && $location.search().id === 'search';

if (c.searchTerm) {
var newUrl = $location.search({
id: 'search',
spa: '1',
t: c.searchType,
q: c.searchTerm
});

if (shouldReloadPage)
$scope.$emit("sp.page.reload");

spAriaFocusManager.navigateToLink(newUrl.url());
//Pass the current page ID to search page for Search Analytics
$window.spSearchAnalytics = {
page_id: c.pageID
};
}
}

function sendLiveMessage(resultCount) {
spAriaUtil.sendLiveMessage(resultCount + " " +
c.data.resultMsg + " " +
(resultCount > 0 ? ' ' + c.data.navigationMsg : ''));
}

function getIcon(itemType) {
return itemType === "INSTANCE_HISTORY" ? 'search' : 'clock-o';
}

function getQueryToHighlight(item, query) {
return item.type === "INSTANCE_HISTORY" ? item.name.substring(query.length) : query;
}
}

 

 

 

1 ACCEPTED SOLUTION

Carlos Candano
Mega Guru

I made changes with OOTB. What I did is update the  typeahead template of Catalogs Search Source of /sp Service Portal

find_real_file.png

 

Code:

<!-- prefer item picture to item icon, prefer item icon to default icon -->
<div onclick="window.GlideWebAnalytics.trackEvent('Service Catalog', 'Catalog Search Type Ahead', 'Item Clicked')">
<i ng-if="match.model.picture" class="ta-img" style="background-image:url('{{match.model.picture}}?t=small')"></i>
<i ng-if="!match.model.picture && match.model.icon" class="ta-icon" style="background-image:url('{{match.model.icon}}'); width:16px; height:16px"></i>
<i ng-if="!match.model.picture && !match.model.icon" class="ta-icon fa fa-{{match.model.default_icon}}"></i>
<span ng-bind-html="match.label | uibTypeaheadHighlight:query"></span>
<p>
<span ng-bind-html="match.model.short_description | uibTypeaheadHighlight:query"></span>
  </p>
<strong ng-if="match.model.type == 'sc_content' && match.model.content_type == 'external'">âžš</strong>
</div>

Result:

find_real_file.png

View solution in original post

13 REPLIES 13

Thank You replying me, I'll check this

This code did not work, Below is the code whicch is written in my HTML 

 

My Code 

<div id="homepage-search" class="hidden-xs wrapper-xl">
<div class="wrapper-xl">
<h2 class="text-center text-4x m-b-lg sp-tagline-color" ng-bind="options.title"></h2>
<div ng-if="options.short_description" class="text-center h4 m-b-lg sp-tagline-color" ng-bind="options.short_description"></div>

<sp-widget widget="data.typeAheadSearch" />
</div>
</div>

 

 

Do not update the widget. What I changed is just the search source. Does your portal have a search source for catalogs? It should be defined on Portal definition related list

This is the one defined in OOTB.

find_real_file.png

 

Thank You So Much .. 

It worked ..... 

 

Thanks A Lot Again Carlos for helping me ..