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

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

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