- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
04-25-2021 01:46 AM
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 ]
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;
}
}
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
04-25-2021 06:33 AM
I made changes with OOTB. What I did is update the typeahead template of Catalogs Search Source of /sp Service Portal
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:
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
04-25-2021 08:52 AM
Sorry David, but I can't follow you. If user search for some text like "User" then Typeahead Search widget will display items from all Search Sources. Not all found items could have short_description property or not all Search Sources could include the data from the field. Thus I guess that you should make no modification of Typeahead Search widget and make modifications in the corresponding Search Source.
I posted the answer on very close question. If you need to display short description of catalog items, then it's enough to make minimal modification in Typeahead template to display short description of catalog items. Is it probably what you search for?

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
04-25-2021 06:24 AM
Hi,
Can you please try modifying your script little bit?
You have something like this in your script to return ITEM name:
c.searchItems = result.entries.map(function(item) {
item.query = getQueryToHighlight(item, c.searchQuery);
item.glyph = getIcon(item.type);
item.term = item.name;
return item;
});
Please modify the line no 4 as given below:
c.searchItems = result.entries.map(function(item) {
item.query = getQueryToHighlight(item, c.searchQuery);
item.glyph = getIcon(item.type);
item.term = item.name+' '+item.short_description+'' ; //Please change this line
return item;
});
Note: This is not good practice, but you can give a try.
Thanks,
Anil Lande
Thanks
Anil Lande
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
04-25-2021 07:48 AM
Thank You replying me, I'll check this
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
04-25-2021 07:55 AM
Hello Anil, This did not work
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
04-25-2021 06:33 AM
I made changes with OOTB. What I did is update the typeahead template of Catalogs Search Source of /sp Service Portal
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: