Join the #BuildWithBuildAgent Challenge! Get recognized, earn exclusive swag, and inspire the ServiceNow Community with what you can build using Build Agent.  Join the Challenge.

search portal

rah dev
Tera Contributor

Hi All, i am stuck on a requirement, need to make and use a button with similar functionality when click on this, it should show the result like we do in search icon
html:
<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"></sp-widget>
<button>
search
</button>
</div>
</div>
server script:
var aisEnabled = $sp.isAISearchEnabled();

if (aisEnabled)
data.typeAheadSearch = $sp.getWidget('typeahead-search', options);
else
data.typeAheadSearch = $sp.getWidget('typeahead-search', options.typeahead_search);
Thanks in Advance!

1 ACCEPTED SOLUTION

IbrarA
Giga Guru
Giga Guru

Hi @rah dev, try the following code, 

<!-- Custom CSS -->
<style>
  .dropdown-short {
    height: calc(100% / 3);
  }
  .form-row > .col-md-12 {
    display: flex;
    justify-content: center;
  }
  .button-center {
    justify-content: center;
  }
  .input-group .input-group-btn {
    margin-right: -1px; /* Adjust margin to decrease gap */
  }
</style>
 
<form ng-if="!c.data.aisEnabled" ng-submit="c.submitSearch()" role="search">
  <input type="hidden" name="id" value="search"/>
  <input type="hidden" name="t" value="{{c.searchType}}"/>
  <div class="input-group input-group-{{::c.options.size}} input-group-typeahead" role="presentation">
 
    <!-- Search Icon Button -->
    <span class="input-group-btn">
      <button type="button" class="btn btn-{{::c.options.color}} p-2"
              title="Search" aria-label="Search" data-toggle="tooltip" data-placement="bottom">
        <i ng-if="::c.options.glyph" class="fa fa-search"></i>
  </button>
    </span>
      <!-- Search Input Field -->
      <div class="col-md-9">
        <input ng-if="c.isTypeAheadEnabled && c.showSuggestions"
               name="q" placeholder="{{::c.options.title}}" ng-model="c.searchTerm"
               autocomplete="off"
               uib-typeahead="item as item.term for item in c.getSearchSuggestions($viewValue)"
               typeahead-wait-ms="c.data.typeaheadWaitMS"
               typeahead-min-length="c.data.typeaheadMinLength"
               typeahead-focus-first="false"
               typeahead-on-select="c.onSelect($item, $model, $label)"
               typeahead-template-url="sp-typeahead.html"
               typeahead-popup-template-url="sp-typeahead-popup.html"
               class="form-control input-typeahead"
               role="combobox"
               aria-autocomplete="list"
               title="{{::c.options.title}}" data-toggle="tooltip" data-placement="bottom"
               aria-label="{{::c.options.title}}" tabindex="0" aria-haspopup="listbox">
        <input ng-if="c.isTypeAheadEnabled && !c.showSuggestions"
               name="q" placeholder="{{::c.options.title}}" ng-model="c.searchTerm"
               autocomplete="off"
               uib-typeahead="item as item.primary for item in c.getResults($viewValue)"
               typeahead-wait-ms="c.data.typeaheadWaitMS"
               typeahead-min-length="c.data.typeaheadMinLength"
               typeahead-focus-first="false"
               typeahead-on-select="c.onSelect($item, $model, $label)"
               typeahead-template-url="sp-typeahead.html"
               typeahead-popup-template-url="sp-typeahead-popup.html"
               class="form-control input-typeahead"
               role="combobox"
               aria-autocomplete="list"
               title="{{::c.options.title}}" data-toggle="tooltip" data-placement="bottom"
               aria-label="{{::c.options.title}}" tabindex="0" aria-haspopup="listbox">
        <input ng-if="!c.isTypeAheadEnabled"
               name="q" type="text" placeholder="{{::c.options.title}}" ng-model="c.searchTerm"
               autocomplete="off"
               class="form-control"
               role="listbox"
               title="{{::c.options.title}}" data-toggle="tooltip" data-placement="bottom"
               aria-label="{{::c.options.title}}" tabindex="0">
      </div>
 
      <!-- Dropdown Menu -->
     
      
    <!-- Custom Button on next line -->
      <div class="col-md-12">
        <span class="input-group-btn">
          <button name="customButton" type="submit" class="btn btn-{{::c.options.color}} w-25"
                  title="Button" aria-label="Button" data-toggle="tooltip" data-placement="bottom">
            Button
          </button>
        </span>
      </div>
   </form>
 
<div ng-if="c.data.aisEnabled">
  <sn-search-combobox class="aisearch" 
      search-context-config-id="{{c.data.searchApplicationId}}"
      placeholder="{{::c.options.placeholder}}" 
      search-term="{{c.data.q}}" 
      disable-autocomplete="{{c.options.disable_all_suggestions}}"
      placement="header"
      enable-exact-match=false
      exact-match-regex="{{c.data.exactMatchRegex}}">
  </sn-search-combobox>
</div>
 
 
css:
.sp-tagline-color {
color: $sp-tagline-color;
}
 
#homepage-search {
  .aisearch {
    --classicsponlydonotuse--rem-multipy: 1.6;
  }
}
  client script:
  function ($http, $filter, $location,spAriaUtil, $window, $scope, $rootScope, spAriaFocusManager, snAnalytics, spAISearchResults) {
var c = this;
 
// Code common for Zing and AI Search start
$scope.$on('$locationChangeSuccess', onLocationChangeSuccess);
function setSearchTerm(newUrl, oldUrl) {
try {
var oldQuery = new URL(oldUrl).searchParams.get("q");
var newQuery = new URL(newUrl).searchParams.get("q");
if (oldQuery === newQuery)
return;
if (c.data.aisEnabled)
c.data.q = newQuery;
else
c.searchTerm = newQuery;
} catch (e) {}
}
 
var regExpr = /[?&](t=[^&]+)/;
 
function onLocationChangeSuccess(event, newUrl, oldUrl) {
if (!c.data.aisEnabled && c.searchSourceChanged(newUrl, oldUrl)) {
var newUrlParams = newUrl.match(regExpr);
if (!newUrlParams)
c.searchType = null;
else
c.searchType = newUrlParams[1].substring(2);
}
setSearchTerm(newUrl, oldUrl);
}
// Code common for Zing and AI Search end
 
if (c.data.aisEnabled)
intializeAISearch();
else
initializeZingSearch();
 
function intializeAISearch() {
// AI Search functions
 
c.aisSubmit = function(payload) {
var shouldReloadPage = c.data.refreshPageOnSearch && $location.search().id === 'search';
if (payload.searchTerm) {
var newUrlObj = {
id: 'search',
spa: '1',
q: payload.searchTerm,
disableAllSuggestions: c.options.disable_all_suggestions && c.options.disable_all_suggestions.toString(),
search_application: c.options.search_application || undefined,
search_results_configuration: c.options.search_results_configuration || undefined,
searchFilters : $location.search().searchFilters || c.data.aiSearchSourceFilter || undefined,
disableSpellCheck: 'false'
};
 
$rootScope.$applyAsync(function() {
            var navigateToUrl = $location.search(newUrlObj);
if (shouldReloadPage)
$scope.$emit("sp.page.reload");
spAriaFocusManager.navigateToLink(navigateToUrl.url());
        });  
}
}
 
c.navigate = spAISearchResults.navigate;
 
}
 
 
function initializeZingSearch() {
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 || "";
c.latitude = null;
c.longitude = null;
c.isGlideSignalsLoaded = false;
c.isLocationTrackerDisabled = c.data.isLocationTrackerDisabled === "true";
c.isTypeAheadEnabled = c.data.isTypeAheadEnabled === "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);
};
if (c.isTypeAheadEnabled) {
 
if (window.GlideSignals)
initializeGlideSignals();
else {
$rootScope.$on("sp.defer_scripts.loaded", function(){
if (window.GlideSignals)
initializeGlideSignals();
});
}
 
if (!c.isLocationTrackerDisabled) {
setUserLocationCoords(function(coords) {
c.latitude = coords.latitude;
c.longitude = coords.longitude;
});
}
c.searchType = c.data.searchType;
 
// Zing Search functions
function initializeGlideSignals() {
if (!c.isLocationTrackerDisabled && window.GlideSignals.init)
window.GlideSignals.init();
 
c.isGlideSignalsLoaded = window.GlideSignals.trackEvent || c.isGlideSignalsLoaded;
}
 
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)
return;
 
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&spa=1&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){
var isInstanceHistory = item.type === 'INSTANCE_HISTORY';
c.instanceHistorySuggestionsCount += isInstanceHistory;
c.userHistorySuggestionsCount += !isInstanceHistory;
});
}
 
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;
if (c.totalSuggestionsCount > 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.forEach(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) {
return {
record_id: item.sys_id,
table_name: item.table
};
});
}
 
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
});
};
 
if (window.navigator.geolocation) {
window.navigator.geolocation.getCurrentPosition(onSuccess, onError, {
enableHighAccuracy: true
});
}
}
 
 
function getResultDescription(result){
return result.name || result.primary || result.sec_title;
}
 
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;
 
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,
signal_type: "CLICK",
signal_value: rank,
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
}
};
 
$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?sysparm_cancelable=true", 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 (item.link)
item.url = item.link.indexOf('sys_attachment.do') != -1 ? item.link : config.linkToPage ? item.url : item.link;
 
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.searchSourceChanged = function(newUrl, oldUrl) {
var newUrlParams = newUrl.match(regExpr),
oldUrlParams = oldUrl.match(regExpr);
 
if(!newUrlParams && !oldUrlParams) {
return false;
}
 
if((!newUrlParams && oldUrlParams) || (newUrlParams && !oldUrlParams)) {
return true;
}
 
return newUrlParams[1] !== oldUrlParams[1];
}
 
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;
}
}
}
server script:
(function() {
var portalRecord = $sp.getPortalRecord();
data.aisEnabled = $sp.isAISearchEnabled();
data.q = $sp.getParameter('q');
 
if (options.refresh_page_on_search_submission == undefined)
data.refreshPageOnSearch = true;
else
data.refreshPageOnSearch = options.refresh_page_on_search_submission;
 
// AI Search
if (data.aisEnabled) {
data.searchApplicationId = options.search_application || portalRecord.getValue('search_application');
options.placeholder = options.placeholder ? gs.getMessage(options.placeholder) : gs.getMessage('Search');
data.aiSearchSourceFilter = "";
var aiSearchSource = options.ai_search_source_filter;
var filterGr = new GlideRecord('sys_search_filter');
data.exactMatchRegex = new GlideSystem().getProperty("com.snc.service_portal.typeahead.exact_match_request_criterion_regex")
filterGr.addQuery('ais_search_source', aiSearchSource);
filterGr.addQuery('search_context_config', data.searchApplicationId);
filterGr.addActiveQuery();
filterGr.orderBy('order');
filterGr.query();
if (filterGr.next())
data.aiSearchSourceFilter = filterGr.getUniqueValue();
        var portalId = portalRecord && portalRecord.getUniqueValue();
        var resultMap = $sp.getAISearchResultsActionConfig(data.searchApplicationId, portalId);
resultMap = JSON.parse(resultMap);
        data.tableToSourceMap = resultMap.tableToSourceMap;
        data.sourceToPageMap = resultMap.sourceToPageMap;
        data.urlMap = resultMap.urlMap;
data.portalId = portalId;
return;
}
 
if (input && input.action === "GlideSPSearchAnalyticsUpdateRank") {
input.action = "";
var textSearchAnalytics = $sp.publishSearchAnalytics(JSON.stringify(input.payload));
return ;
}
if (options.title) {
options.title = gs.getMessage(options.title);
}
 
data.resultMsg = gs.getMessage("Search results.");
data.navigationMsg = gs.getMessage("To navigate, use up and down arrow keys.");
data.portalID = portalRecord && portalRecord.getUniqueValue();
data.searchMsg = gs.getMessage("Search");
data.searchSuggestionsMsg = gs.getMessage("suggestions");
 
data.noResultsFoundMsg = gs.getMessage("No results found");
 
data.isSuggestionsEnabled = gs.getProperty('glide.search.suggestions.enabled');
data.searchTypeBehavior = gs.getProperty('glide.service_portal.search_as_you_type_behavior').toLowerCase();
data.typeaheadWaitMS = getIntProperty('glide.service_portal.typeahead.wait_ms', '1000');
data.typeaheadMinLength = getIntProperty('glide.service_portal.typeahead.min_length', '3');
data.isLocationTrackerDisabled = gs.getProperty('glide.service_portal.disable_location_tracker');
data.isTypeAheadEnabled = gs.getProperty('glide.service_portal.enable_typeahead', 'true');
 
var searchSources;
data.searchType = null;
data.searchSources = [];
if ($sp.getParameter("id") == "search" && $sp.getParameter("t")) {
data.searchType = $sp.getParameter("t");
searchSources = $sp.getSearchSources(data.portalID);
} else {
var contextualSearchSourceIDs = options.contextual_search_sources || null;
searchSources = $sp.getSearchSources(data.portalID, contextualSearchSourceIDs);
if (searchSources.length == 1) {
data.searchType = searchSources[0].id;
}
}
 
data.searchSourceSysIds = [];
data.typeaheadTemplates = {};
data.searchSourceConfiguration = {};
searchSources.forEach(function(source) {
data.searchSourceSysIds.push(source.sys_id);
if (source.isTypeaheadEnabled) {
data.searchSources.push(source.id);
}
var sourceTemplateConfiguration = {
sys_id: source.sys_id,
glyph: source.typeaheadGlyph,
linkToPage: source.typeaheadPage
};
 
if (source.isAdvancedTypeaheadConfig) {
sourceTemplateConfiguration.type = "ADVANCED";
sourceTemplateConfiguration.template = "sp-typeahead-" + source.id + ".html";
data.typeaheadTemplates["sp-typeahead-" + source.id + ".html"] = $sp.translateTemplate(source.typeaheadTemplate);
} else {
sourceTemplateConfiguration.type = "SIMPLE";
if (!sourceTemplateConfiguration.linkToPage)
console.log("Warning: No typeahead page or URL provided for search source " + source.name);
}
 
data.searchSourceConfiguration[source.id] = sourceTemplateConfiguration;
});
 
function getIntProperty(name, defaultValue) {
var answer = parseInt(gs.getProperty(name, defaultValue));
return isNaN(answer) ? defaultValue : answer;
}
})();
adjust css according to the requirement.
ibrar

View solution in original post

3 REPLIES 3

Hi @Anirudh Pathak, i need to apply button with search icon functionality, but in here they are modifying search bar

IbrarA
Giga Guru
Giga Guru

Hi @rah dev, try the following code, 

<!-- Custom CSS -->
<style>
  .dropdown-short {
    height: calc(100% / 3);
  }
  .form-row > .col-md-12 {
    display: flex;
    justify-content: center;
  }
  .button-center {
    justify-content: center;
  }
  .input-group .input-group-btn {
    margin-right: -1px; /* Adjust margin to decrease gap */
  }
</style>
 
<form ng-if="!c.data.aisEnabled" ng-submit="c.submitSearch()" role="search">
  <input type="hidden" name="id" value="search"/>
  <input type="hidden" name="t" value="{{c.searchType}}"/>
  <div class="input-group input-group-{{::c.options.size}} input-group-typeahead" role="presentation">
 
    <!-- Search Icon Button -->
    <span class="input-group-btn">
      <button type="button" class="btn btn-{{::c.options.color}} p-2"
              title="Search" aria-label="Search" data-toggle="tooltip" data-placement="bottom">
        <i ng-if="::c.options.glyph" class="fa fa-search"></i>
  </button>
    </span>
      <!-- Search Input Field -->
      <div class="col-md-9">
        <input ng-if="c.isTypeAheadEnabled && c.showSuggestions"
               name="q" placeholder="{{::c.options.title}}" ng-model="c.searchTerm"
               autocomplete="off"
               uib-typeahead="item as item.term for item in c.getSearchSuggestions($viewValue)"
               typeahead-wait-ms="c.data.typeaheadWaitMS"
               typeahead-min-length="c.data.typeaheadMinLength"
               typeahead-focus-first="false"
               typeahead-on-select="c.onSelect($item, $model, $label)"
               typeahead-template-url="sp-typeahead.html"
               typeahead-popup-template-url="sp-typeahead-popup.html"
               class="form-control input-typeahead"
               role="combobox"
               aria-autocomplete="list"
               title="{{::c.options.title}}" data-toggle="tooltip" data-placement="bottom"
               aria-label="{{::c.options.title}}" tabindex="0" aria-haspopup="listbox">
        <input ng-if="c.isTypeAheadEnabled && !c.showSuggestions"
               name="q" placeholder="{{::c.options.title}}" ng-model="c.searchTerm"
               autocomplete="off"
               uib-typeahead="item as item.primary for item in c.getResults($viewValue)"
               typeahead-wait-ms="c.data.typeaheadWaitMS"
               typeahead-min-length="c.data.typeaheadMinLength"
               typeahead-focus-first="false"
               typeahead-on-select="c.onSelect($item, $model, $label)"
               typeahead-template-url="sp-typeahead.html"
               typeahead-popup-template-url="sp-typeahead-popup.html"
               class="form-control input-typeahead"
               role="combobox"
               aria-autocomplete="list"
               title="{{::c.options.title}}" data-toggle="tooltip" data-placement="bottom"
               aria-label="{{::c.options.title}}" tabindex="0" aria-haspopup="listbox">
        <input ng-if="!c.isTypeAheadEnabled"
               name="q" type="text" placeholder="{{::c.options.title}}" ng-model="c.searchTerm"
               autocomplete="off"
               class="form-control"
               role="listbox"
               title="{{::c.options.title}}" data-toggle="tooltip" data-placement="bottom"
               aria-label="{{::c.options.title}}" tabindex="0">
      </div>
 
      <!-- Dropdown Menu -->
     
      
    <!-- Custom Button on next line -->
      <div class="col-md-12">
        <span class="input-group-btn">
          <button name="customButton" type="submit" class="btn btn-{{::c.options.color}} w-25"
                  title="Button" aria-label="Button" data-toggle="tooltip" data-placement="bottom">
            Button
          </button>
        </span>
      </div>
   </form>
 
<div ng-if="c.data.aisEnabled">
  <sn-search-combobox class="aisearch" 
      search-context-config-id="{{c.data.searchApplicationId}}"
      placeholder="{{::c.options.placeholder}}" 
      search-term="{{c.data.q}}" 
      disable-autocomplete="{{c.options.disable_all_suggestions}}"
      placement="header"
      enable-exact-match=false
      exact-match-regex="{{c.data.exactMatchRegex}}">
  </sn-search-combobox>
</div>
 
 
css:
.sp-tagline-color {
color: $sp-tagline-color;
}
 
#homepage-search {
  .aisearch {
    --classicsponlydonotuse--rem-multipy: 1.6;
  }
}
  client script:
  function ($http, $filter, $location,spAriaUtil, $window, $scope, $rootScope, spAriaFocusManager, snAnalytics, spAISearchResults) {
var c = this;
 
// Code common for Zing and AI Search start
$scope.$on('$locationChangeSuccess', onLocationChangeSuccess);
function setSearchTerm(newUrl, oldUrl) {
try {
var oldQuery = new URL(oldUrl).searchParams.get("q");
var newQuery = new URL(newUrl).searchParams.get("q");
if (oldQuery === newQuery)
return;
if (c.data.aisEnabled)
c.data.q = newQuery;
else
c.searchTerm = newQuery;
} catch (e) {}
}
 
var regExpr = /[?&](t=[^&]+)/;
 
function onLocationChangeSuccess(event, newUrl, oldUrl) {
if (!c.data.aisEnabled && c.searchSourceChanged(newUrl, oldUrl)) {
var newUrlParams = newUrl.match(regExpr);
if (!newUrlParams)
c.searchType = null;
else
c.searchType = newUrlParams[1].substring(2);
}
setSearchTerm(newUrl, oldUrl);
}
// Code common for Zing and AI Search end
 
if (c.data.aisEnabled)
intializeAISearch();
else
initializeZingSearch();
 
function intializeAISearch() {
// AI Search functions
 
c.aisSubmit = function(payload) {
var shouldReloadPage = c.data.refreshPageOnSearch && $location.search().id === 'search';
if (payload.searchTerm) {
var newUrlObj = {
id: 'search',
spa: '1',
q: payload.searchTerm,
disableAllSuggestions: c.options.disable_all_suggestions && c.options.disable_all_suggestions.toString(),
search_application: c.options.search_application || undefined,
search_results_configuration: c.options.search_results_configuration || undefined,
searchFilters : $location.search().searchFilters || c.data.aiSearchSourceFilter || undefined,
disableSpellCheck: 'false'
};
 
$rootScope.$applyAsync(function() {
            var navigateToUrl = $location.search(newUrlObj);
if (shouldReloadPage)
$scope.$emit("sp.page.reload");
spAriaFocusManager.navigateToLink(navigateToUrl.url());
        });  
}
}
 
c.navigate = spAISearchResults.navigate;
 
}
 
 
function initializeZingSearch() {
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 || "";
c.latitude = null;
c.longitude = null;
c.isGlideSignalsLoaded = false;
c.isLocationTrackerDisabled = c.data.isLocationTrackerDisabled === "true";
c.isTypeAheadEnabled = c.data.isTypeAheadEnabled === "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);
};
if (c.isTypeAheadEnabled) {
 
if (window.GlideSignals)
initializeGlideSignals();
else {
$rootScope.$on("sp.defer_scripts.loaded", function(){
if (window.GlideSignals)
initializeGlideSignals();
});
}
 
if (!c.isLocationTrackerDisabled) {
setUserLocationCoords(function(coords) {
c.latitude = coords.latitude;
c.longitude = coords.longitude;
});
}
c.searchType = c.data.searchType;
 
// Zing Search functions
function initializeGlideSignals() {
if (!c.isLocationTrackerDisabled && window.GlideSignals.init)
window.GlideSignals.init();
 
c.isGlideSignalsLoaded = window.GlideSignals.trackEvent || c.isGlideSignalsLoaded;
}
 
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)
return;
 
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&spa=1&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){
var isInstanceHistory = item.type === 'INSTANCE_HISTORY';
c.instanceHistorySuggestionsCount += isInstanceHistory;
c.userHistorySuggestionsCount += !isInstanceHistory;
});
}
 
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;
if (c.totalSuggestionsCount > 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.forEach(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) {
return {
record_id: item.sys_id,
table_name: item.table
};
});
}
 
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
});
};
 
if (window.navigator.geolocation) {
window.navigator.geolocation.getCurrentPosition(onSuccess, onError, {
enableHighAccuracy: true
});
}
}
 
 
function getResultDescription(result){
return result.name || result.primary || result.sec_title;
}
 
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;
 
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,
signal_type: "CLICK",
signal_value: rank,
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
}
};
 
$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?sysparm_cancelable=true", 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 (item.link)
item.url = item.link.indexOf('sys_attachment.do') != -1 ? item.link : config.linkToPage ? item.url : item.link;
 
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.searchSourceChanged = function(newUrl, oldUrl) {
var newUrlParams = newUrl.match(regExpr),
oldUrlParams = oldUrl.match(regExpr);
 
if(!newUrlParams && !oldUrlParams) {
return false;
}
 
if((!newUrlParams && oldUrlParams) || (newUrlParams && !oldUrlParams)) {
return true;
}
 
return newUrlParams[1] !== oldUrlParams[1];
}
 
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;
}
}
}
server script:
(function() {
var portalRecord = $sp.getPortalRecord();
data.aisEnabled = $sp.isAISearchEnabled();
data.q = $sp.getParameter('q');
 
if (options.refresh_page_on_search_submission == undefined)
data.refreshPageOnSearch = true;
else
data.refreshPageOnSearch = options.refresh_page_on_search_submission;
 
// AI Search
if (data.aisEnabled) {
data.searchApplicationId = options.search_application || portalRecord.getValue('search_application');
options.placeholder = options.placeholder ? gs.getMessage(options.placeholder) : gs.getMessage('Search');
data.aiSearchSourceFilter = "";
var aiSearchSource = options.ai_search_source_filter;
var filterGr = new GlideRecord('sys_search_filter');
data.exactMatchRegex = new GlideSystem().getProperty("com.snc.service_portal.typeahead.exact_match_request_criterion_regex")
filterGr.addQuery('ais_search_source', aiSearchSource);
filterGr.addQuery('search_context_config', data.searchApplicationId);
filterGr.addActiveQuery();
filterGr.orderBy('order');
filterGr.query();
if (filterGr.next())
data.aiSearchSourceFilter = filterGr.getUniqueValue();
        var portalId = portalRecord && portalRecord.getUniqueValue();
        var resultMap = $sp.getAISearchResultsActionConfig(data.searchApplicationId, portalId);
resultMap = JSON.parse(resultMap);
        data.tableToSourceMap = resultMap.tableToSourceMap;
        data.sourceToPageMap = resultMap.sourceToPageMap;
        data.urlMap = resultMap.urlMap;
data.portalId = portalId;
return;
}
 
if (input && input.action === "GlideSPSearchAnalyticsUpdateRank") {
input.action = "";
var textSearchAnalytics = $sp.publishSearchAnalytics(JSON.stringify(input.payload));
return ;
}
if (options.title) {
options.title = gs.getMessage(options.title);
}
 
data.resultMsg = gs.getMessage("Search results.");
data.navigationMsg = gs.getMessage("To navigate, use up and down arrow keys.");
data.portalID = portalRecord && portalRecord.getUniqueValue();
data.searchMsg = gs.getMessage("Search");
data.searchSuggestionsMsg = gs.getMessage("suggestions");
 
data.noResultsFoundMsg = gs.getMessage("No results found");
 
data.isSuggestionsEnabled = gs.getProperty('glide.search.suggestions.enabled');
data.searchTypeBehavior = gs.getProperty('glide.service_portal.search_as_you_type_behavior').toLowerCase();
data.typeaheadWaitMS = getIntProperty('glide.service_portal.typeahead.wait_ms', '1000');
data.typeaheadMinLength = getIntProperty('glide.service_portal.typeahead.min_length', '3');
data.isLocationTrackerDisabled = gs.getProperty('glide.service_portal.disable_location_tracker');
data.isTypeAheadEnabled = gs.getProperty('glide.service_portal.enable_typeahead', 'true');
 
var searchSources;
data.searchType = null;
data.searchSources = [];
if ($sp.getParameter("id") == "search" && $sp.getParameter("t")) {
data.searchType = $sp.getParameter("t");
searchSources = $sp.getSearchSources(data.portalID);
} else {
var contextualSearchSourceIDs = options.contextual_search_sources || null;
searchSources = $sp.getSearchSources(data.portalID, contextualSearchSourceIDs);
if (searchSources.length == 1) {
data.searchType = searchSources[0].id;
}
}
 
data.searchSourceSysIds = [];
data.typeaheadTemplates = {};
data.searchSourceConfiguration = {};
searchSources.forEach(function(source) {
data.searchSourceSysIds.push(source.sys_id);
if (source.isTypeaheadEnabled) {
data.searchSources.push(source.id);
}
var sourceTemplateConfiguration = {
sys_id: source.sys_id,
glyph: source.typeaheadGlyph,
linkToPage: source.typeaheadPage
};
 
if (source.isAdvancedTypeaheadConfig) {
sourceTemplateConfiguration.type = "ADVANCED";
sourceTemplateConfiguration.template = "sp-typeahead-" + source.id + ".html";
data.typeaheadTemplates["sp-typeahead-" + source.id + ".html"] = $sp.translateTemplate(source.typeaheadTemplate);
} else {
sourceTemplateConfiguration.type = "SIMPLE";
if (!sourceTemplateConfiguration.linkToPage)
console.log("Warning: No typeahead page or URL provided for search source " + source.name);
}
 
data.searchSourceConfiguration[source.id] = sourceTemplateConfiguration;
});
 
function getIntProperty(name, defaultValue) {
var answer = parseInt(gs.getProperty(name, defaultValue));
return isNaN(answer) ? defaultValue : answer;
}
})();
adjust css according to the requirement.
ibrar