How to make Typeahead Search in Service Portal show Service Catalog Items when not logged in?

sethhumphrey
Mega Guru

Our Service Portal is very public by design.  We just discovered that searching in the Typeahead Search widget will not yield results from the Service Catalog unless you're logged in.  How do I fix that so it will show catalog item matches regardless if you're logged in?  In cloning the widget I don't see where that's being set.

find_real_file.png

You may be asking yourself, "why show something that don't have access to anyway?"  Our strategy is to heavily use Content Items to populate our Service Catalog so the SC is the full picture of the services we provide whether you have to request them or not.  Requestable items are visible but a user must login to order the service (see graphic).

find_real_file.png

1 ACCEPTED SOLUTION

Yes!  Sorry I never came back to update this issue.  Here is the Typeahead Search code that works for me:

 

HTML

<form ng-submit="c.submitSearch()">
  <input type="hidden" name="id" value="search"/>
  <input type="hidden" name="t" value="{{data.searchType}}"/>
  <div class="input-group input-group-{{::c.options.size}}">
    <!-- uses ui.bootstrap.typeahead -->
    <!-- replaced `uib-typeahead="item for item in c.getResults($viewValue)"`
         in the element below to fix the [object Object] issue -->
    <input id="search-input"
           name="q" type="text" placeholder="{{::c.options.title}}" ng-model="c.selectedState"
           ng-model-options="{debounce: 250}" autocomplete="off"
     			 uib-typeahead="item as item.label for item in c.getResults($viewValue)"
           typeahead-focus-first="false"
           typeahead-on-select="c.onSelect($item, $model, $label)"
           typeahead-template-url="sp-typeahead-auburn.html"
           typeahead-popup-template-url="sp-typeahead-popup-auburn.html"
           class="form-control input-typeahead"
           title="{{::c.options.title}}"
           role="textbox"
           aria-label="{{::c.options.title}}" tabindex="0" aria-haspopup="true">
    <span class="input-group-btn">
      <button name="search" type="submit" class="btn btn-{{::c.options.color}}"
              title="{{::c.options.title}}"
              aria-label="{{::c.options.title}}">
    	<i ng-if="::c.options.glyph" class="fa fa-{{::c.options.glyph}}"></i>
      </button>
    </span>
  </div>
</form>

CSS

ul.dropdown-menu {
    min-width: 100%;
    border-radius: 0px 0px 4px 4px;
    margin:0px;
}

ul.dropdown-menu a.ta-item {
	line-height: 20px;
}
ul.dropdown-menu li h4 {
  background-color:#efefef;
  margin-left:0;
  padding:10px;
}
span.ta-item-header{
     -moz-user-select: -moz-none;
   -khtml-user-select: none;
   -webkit-user-select: none;
   -o-user-select: none;
   user-select: none;
  pointer-events:none;
}
ul.dropdown-menu i.ta-icon, i.ta-img {
    width: 20px;
    height: 20px;
    background-size: contain;
    display: inline-block;
    background-repeat: no-repeat;
    background-position: center center;
    text-align: center;
    line-height: 20px;
    float:left;
    margin-right: 8px;
}

input[name="q"] {
color: black;
}

.ta-item-header h4 {
  margin-left: 7px;
}

.no-result {
 margin-left: 7px;
 padding-left: 20px;
}

Client Script

function ($filter, $location,spAriaUtil, $window) {
	
	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.onSelect = function($item, $model, $label) {
		if ($item.target)
			window.open($item.url, $item.target);
		else
			$location.url($item.url);
	};

	c.getResults = function(query) {
		return c.server.get({q: query}).then(function(response) {
			var a = $filter('orderBy')(response.data.results, '-order');
			var resultCount = $filter('limitTo')(a, c.data.limit).length;
				spAriaUtil.sendLiveMessage(parseInt(resultCount) + " " + 
																	 c.data.resultMsg + " " +
																	 c.data.navigationMsg + 
																	 getNavigationKeys());
			return $filter('limitTo')(a, c.data.limit);
		});
	}

	c.submitSearch = function() {
		if (c.selectedState) {
			$location.search({
				id: 'search',
				t: c.data.searchType,
				q: c.selectedState
			});
			console.log(c.selectedState);
		}
	}
	
	function getNavigationKeys() {
		if($window.navigator.userAgent.indexOf("Mac OS X") > -1) 
			return '⌘';
		return 'Control';
	}
}

Server Script

(function() {

//var order = 0;// ordering articles and catalog
data.searchType = $sp.getParameter("t");
data.results = [];
data.searchMsg = gs.getMessage("Search");
data.limit = options.limit || 15;
var order = data.limit;

var textQuery = '123TEXTQUERY321';

if (!input)
return;

data.q = input.q;

getCatalogItems();
getKnowledge();

// add in additional search tables from sp_search_groups
var portalGR = $sp.getPortalRecord();
var portalID = portalGR.getDisplayValue('sys_id');
var sg = GlideRecord('sp_search_group');
sg.addQuery('sp_portal',portalID);
sg.addQuery('active',true);
sg.orderBy('order');
sg.query();

while (sg.next())
    addSearchTable(sg);

// typeahead search generates multiple "Your text query contained only
// common words..." msgs, we don't want them
gs.flushMessages();

function addSearchTable(sg) {
    var table = sg.getValue('name');
    var condition = sg.getValue('condition');
    var gr = GlideRecord(table);

    if (condition)
        gr.addEncodedQuery(condition);

    gr.addQuery(textQuery, data.q);
    gr.query();

    var searchTableCount = 10; // serach count

    var header = {};     // begin header label for catalog and article

    header.type="header";
    header.label = "<h4>"+sg.tables.getDisplayValue()+"</h4>";
    header.score = 0;
    header.order=order;
    header.text= sg.tables.getDisplayValue();
    data.results.push(header);
    //order++;     // end
		order--;

    while (gr.next() && searchTableCount < data.limit) {
        var rec = {};
        rec.type = "rec";
        rec.table = table;
        rec.sys_id = gr.getDisplayValue('sys_id');
        rec.page = sg.getDisplayValue('sp_page');

        if (!rec.page)
            rec.page = "form";

        rec.label =     "<b>"+table+"</b> "+gr.getDisplayValue(); // displaying label 
        rec.score = parseInt(gr.ir_query_score.getDisplayValue());
        rec.order=order;   // order begin
        data.results.push(rec);
        searchTableCount++;
        //order++;   // order   end 
				order--;
    } 
}




function getKnowledge() {
    var kb = new GlideRecord('kb_knowledge');
    kb.addQuery('workflow_state', 'published');
    kb.addQuery('valid_to', '>=', (new GlideDate()).getLocalDate().getValue());
    kb.addQuery(textQuery, data.q);
    //kb.addQuery('kb_knowledge_base', $sp.getDisplayValue('kb_knowledge_base'));
    kb.addQuery('kb_knowledge_base.title', 'Information Technology');  
    kb.query();

    var kbCount = 10; // count list 

    var header = {}; // begin header label for   article                     

    header.type = "header";
    header.label = "<h4>Knowledge Base</h4>";
    header.score = 10;
    header.order = order;
    header.text = "Knowledge Article";
	
		header.hasItems = kb.hasNext();
	
    data.results.push(header);
    //order++; //order end
		order--;
	
		if (!kb.hasNext()) {
			header.label += '<i class="no-result">No results of this type.</i>';
		}
	
    while (kb.next() && kbCount < data.limit) {
        if (!$sp.canReadRecord(kb))
            continue;

        var article = {};

        article.type = "kb";
        $sp.getRecordDisplayValues(article, kb, 'sys_id,number,short_description,description,picture,published,text');
		
				if (!article.text)
            article.text = "";
			
				article.glyph = 'file-text-o';
        article.text = $sp.stripHTML(article.text);
        article.text = article.text.substring(0, 200);
        article.score = parseInt(kb.ir_query_score.getDisplayValue());
        article.label = article.short_description; // highlight KB in bold fornt of KB article
        article.order = order; // order begin
				article.url = '/it?id=' + 'kb_article' + '&sys_id=' + article.sys_id;
        data.results.push(article);
        kbCount++;
        //order++; // order ends
				order--;
    }
}

function getCatalogItems() {
    var sc = new GlideRecord('sc_cat_item');
    sc.addQuery(textQuery, data.q);
    sc.addQuery('active', true);
    sc.addQuery('no_search', '!=', true);
    sc.addQuery('visible_standalone', true);
    sc.addQuery('sys_class_name', 'NOT IN', 'sc_cat_item_wizard');
    sc.addQuery('sc_catalogs', $sp.getValue('sc_catalog'));
    sc.query();

    var catCount = 10; // limit the search to 5
    var header = {}; // begin of header and label for catalog 

    header.type = "header";
    header.label = "<h4>Service Catalog</h4>";
    header.score = 10;
    header.order = order;
    header.text = "Service Catalog";
	
    data.results.push(header);
    //order++;
		order--;

		if (!sc.hasNext()) {
			header.label += '<i class="no-result">No results of this type.</i>';
		}
	
    while (sc.next() && catCount < data.limit) {
        if (!$sp.canReadRecord(sc))
            continue;

        var item = {};
			
				item.fullResult = $sp;
			
        if (sc.getRecordClassName() == "sc_cat_item_guide")
            item.type = "sc_guide";

        else if (sc.getRecordClassName() == "sc_cat_item_content") {
            var gr = new GlideRecord('sc_cat_item_content');
            gr.get(sc.getUniqueValue());
            $sp.getRecordValues(item, gr, 'url,content_type,kb_article');
            item.type = "sc_content";
        } else
            item.type = "sc";
				
        $sp.getRecordDisplayValues(item, sc, 'name,short_description,description,picture,price,sys_id');
        item.score = parseInt(sc.ir_query_score.getDisplayValue());
        item.order = order; //order begin
      
				item.label = item.name; // highlight SC in bold infront of catlog name ("Service Catalog "+)
			
				var urlid;
				if (sc.getRecordClassName() === 'sc_cat_item_guide')
					urlid = 'sc_cat_item_guide';
				else
					urlid = 'sc_cat_item'
			
				item.url = '/it?id=' + urlid + '&sys_id=' + item.sys_id;
				data.results.push(item);
        catCount++;
        //order++; // order ends
				order--;
    }
}

function getQuestions() {
    var questionGR = new GlideRecord("kb_social_qa_question");
    questionGR.addActiveQuery();
    questionGR.addQuery(textQuery, data.q);
    questionGR.query();
    var qCount = 0;
    var header = {}; // begin of header for questions 
    header.type = "header";
    header.label = "<h4>Questions</h4>";
    header.score = 0;
    header.order = order;
    header.text = "Questions";
    data.results.push(header);
    //order++;
		order--;

    while (questionGR.next() && qCount < data.limit) {
        if (!$sp.canReadRecord(questionGR))
            continue;

        var question = {};
        question.type = "qa";
        $sp.getRecordDisplayValues(question, questionGR, 'question,question_details,sys_created_on,sys_id');
        question.text = (question.question_details) ? $sp.stripHTML(question.question_details) : "";
        question.text = question.text.substring(0, 200);
        question.label = "<b>Question</b> " + question.question;
        question.score = 0;
        question.order = order; //order begin
        data.results.push(question);
        qCount++;
        //order++; // order ends
				order--;
    }
}

})();

Link Function

function(scope) {
	var lazyLoader = $injector.get("lazyLoader");
	lazyLoader.putTemplates(scope.data.typeaheadTemplates);
}

Template - sp-typeahead-popup-auburn.html

<ul class="dropdown-menu" ng-show="isOpen() && !moveInProgress" ng-style="{top: position().top+'px', left: position().left+'px'}" role="listbox" aria-hidden="{{!isOpen()}}">
    <li role='option' aria-hidden='true' style='display: none'></li>    
  	<li ng-repeat="match in matches track by $index" ng-class="{active: isActive($index)}" 
        ng-mouseenter="selectActive($index)" 
        ng-click="match.model.type === 'header' ? '' : selectMatch($index, $event)"
        role="option" id="{{::match.id}}">
        <div uib-typeahead-match index="$index" match="match" query="query" template-url="templateUrl"></div>
    </li>
</ul>

Template - sp-typeahead-auburn.html

<!-- replaced instances `match.label.label` with `match.label` [object Object] issue,
     since we changed the value of the `uib-typeahead` attribute in the HTML template -->

<!-- added to show category headers as simple spans instead of a elements -->
<span class="ta-item-header ta-item ng-scope" 
      ng-if="match.model.type === 'header'" ng-bind-html="match.label">
</span>

<!--<a class="ta-item" ng-href="{{match.model.target != '_blank' ? match.model.url : ''}}" target="{{match.model.target}}">-->
<a ng-if="match.model.type !== 'header'" class="ta-item" ng-href="{{ match.model.url }}" target="{{match.model.target}}">
	<div ng-if="!match.model.templateID">
    <!-- if service catalog and has icon -->
    <i class="ta-img ng-scope" ng-if="match.model.type.includes('sc') && match.model.picture.length > 0" 
       ng-style="{'background-image':'url({{match.model.picture}})'}"></i>
    <!-- if service catalog and has no icon -->
    <i class="ta-img ng-scope ta-icon fa fa-file-text-o" ng-if="match.model.type.includes('sc') && match.model.picture.length === 0"></i>
    <!-- if knowledge base -->
    <i class="ta-img ng-scope ta-icon fa fa-file-text-o" ng-if="match.model.type.includes('kb')"></i>
    <span ng-bind-html="match.label | uibTypeaheadHighlight:query"></span>
  </div>
  <div ng-if="match.model.templateID" ng-include="match.model.templateID"></div>
</a>

View solution in original post

10 REPLIES 10

Yes!  Sorry I never came back to update this issue.  Here is the Typeahead Search code that works for me:

 

HTML

<form ng-submit="c.submitSearch()">
  <input type="hidden" name="id" value="search"/>
  <input type="hidden" name="t" value="{{data.searchType}}"/>
  <div class="input-group input-group-{{::c.options.size}}">
    <!-- uses ui.bootstrap.typeahead -->
    <!-- replaced `uib-typeahead="item for item in c.getResults($viewValue)"`
         in the element below to fix the [object Object] issue -->
    <input id="search-input"
           name="q" type="text" placeholder="{{::c.options.title}}" ng-model="c.selectedState"
           ng-model-options="{debounce: 250}" autocomplete="off"
     			 uib-typeahead="item as item.label for item in c.getResults($viewValue)"
           typeahead-focus-first="false"
           typeahead-on-select="c.onSelect($item, $model, $label)"
           typeahead-template-url="sp-typeahead-auburn.html"
           typeahead-popup-template-url="sp-typeahead-popup-auburn.html"
           class="form-control input-typeahead"
           title="{{::c.options.title}}"
           role="textbox"
           aria-label="{{::c.options.title}}" tabindex="0" aria-haspopup="true">
    <span class="input-group-btn">
      <button name="search" type="submit" class="btn btn-{{::c.options.color}}"
              title="{{::c.options.title}}"
              aria-label="{{::c.options.title}}">
    	<i ng-if="::c.options.glyph" class="fa fa-{{::c.options.glyph}}"></i>
      </button>
    </span>
  </div>
</form>

CSS

ul.dropdown-menu {
    min-width: 100%;
    border-radius: 0px 0px 4px 4px;
    margin:0px;
}

ul.dropdown-menu a.ta-item {
	line-height: 20px;
}
ul.dropdown-menu li h4 {
  background-color:#efefef;
  margin-left:0;
  padding:10px;
}
span.ta-item-header{
     -moz-user-select: -moz-none;
   -khtml-user-select: none;
   -webkit-user-select: none;
   -o-user-select: none;
   user-select: none;
  pointer-events:none;
}
ul.dropdown-menu i.ta-icon, i.ta-img {
    width: 20px;
    height: 20px;
    background-size: contain;
    display: inline-block;
    background-repeat: no-repeat;
    background-position: center center;
    text-align: center;
    line-height: 20px;
    float:left;
    margin-right: 8px;
}

input[name="q"] {
color: black;
}

.ta-item-header h4 {
  margin-left: 7px;
}

.no-result {
 margin-left: 7px;
 padding-left: 20px;
}

Client Script

function ($filter, $location,spAriaUtil, $window) {
	
	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.onSelect = function($item, $model, $label) {
		if ($item.target)
			window.open($item.url, $item.target);
		else
			$location.url($item.url);
	};

	c.getResults = function(query) {
		return c.server.get({q: query}).then(function(response) {
			var a = $filter('orderBy')(response.data.results, '-order');
			var resultCount = $filter('limitTo')(a, c.data.limit).length;
				spAriaUtil.sendLiveMessage(parseInt(resultCount) + " " + 
																	 c.data.resultMsg + " " +
																	 c.data.navigationMsg + 
																	 getNavigationKeys());
			return $filter('limitTo')(a, c.data.limit);
		});
	}

	c.submitSearch = function() {
		if (c.selectedState) {
			$location.search({
				id: 'search',
				t: c.data.searchType,
				q: c.selectedState
			});
			console.log(c.selectedState);
		}
	}
	
	function getNavigationKeys() {
		if($window.navigator.userAgent.indexOf("Mac OS X") > -1) 
			return '⌘';
		return 'Control';
	}
}

Server Script

(function() {

//var order = 0;// ordering articles and catalog
data.searchType = $sp.getParameter("t");
data.results = [];
data.searchMsg = gs.getMessage("Search");
data.limit = options.limit || 15;
var order = data.limit;

var textQuery = '123TEXTQUERY321';

if (!input)
return;

data.q = input.q;

getCatalogItems();
getKnowledge();

// add in additional search tables from sp_search_groups
var portalGR = $sp.getPortalRecord();
var portalID = portalGR.getDisplayValue('sys_id');
var sg = GlideRecord('sp_search_group');
sg.addQuery('sp_portal',portalID);
sg.addQuery('active',true);
sg.orderBy('order');
sg.query();

while (sg.next())
    addSearchTable(sg);

// typeahead search generates multiple "Your text query contained only
// common words..." msgs, we don't want them
gs.flushMessages();

function addSearchTable(sg) {
    var table = sg.getValue('name');
    var condition = sg.getValue('condition');
    var gr = GlideRecord(table);

    if (condition)
        gr.addEncodedQuery(condition);

    gr.addQuery(textQuery, data.q);
    gr.query();

    var searchTableCount = 10; // serach count

    var header = {};     // begin header label for catalog and article

    header.type="header";
    header.label = "<h4>"+sg.tables.getDisplayValue()+"</h4>";
    header.score = 0;
    header.order=order;
    header.text= sg.tables.getDisplayValue();
    data.results.push(header);
    //order++;     // end
		order--;

    while (gr.next() && searchTableCount < data.limit) {
        var rec = {};
        rec.type = "rec";
        rec.table = table;
        rec.sys_id = gr.getDisplayValue('sys_id');
        rec.page = sg.getDisplayValue('sp_page');

        if (!rec.page)
            rec.page = "form";

        rec.label =     "<b>"+table+"</b> "+gr.getDisplayValue(); // displaying label 
        rec.score = parseInt(gr.ir_query_score.getDisplayValue());
        rec.order=order;   // order begin
        data.results.push(rec);
        searchTableCount++;
        //order++;   // order   end 
				order--;
    } 
}




function getKnowledge() {
    var kb = new GlideRecord('kb_knowledge');
    kb.addQuery('workflow_state', 'published');
    kb.addQuery('valid_to', '>=', (new GlideDate()).getLocalDate().getValue());
    kb.addQuery(textQuery, data.q);
    //kb.addQuery('kb_knowledge_base', $sp.getDisplayValue('kb_knowledge_base'));
    kb.addQuery('kb_knowledge_base.title', 'Information Technology');  
    kb.query();

    var kbCount = 10; // count list 

    var header = {}; // begin header label for   article                     

    header.type = "header";
    header.label = "<h4>Knowledge Base</h4>";
    header.score = 10;
    header.order = order;
    header.text = "Knowledge Article";
	
		header.hasItems = kb.hasNext();
	
    data.results.push(header);
    //order++; //order end
		order--;
	
		if (!kb.hasNext()) {
			header.label += '<i class="no-result">No results of this type.</i>';
		}
	
    while (kb.next() && kbCount < data.limit) {
        if (!$sp.canReadRecord(kb))
            continue;

        var article = {};

        article.type = "kb";
        $sp.getRecordDisplayValues(article, kb, 'sys_id,number,short_description,description,picture,published,text');
		
				if (!article.text)
            article.text = "";
			
				article.glyph = 'file-text-o';
        article.text = $sp.stripHTML(article.text);
        article.text = article.text.substring(0, 200);
        article.score = parseInt(kb.ir_query_score.getDisplayValue());
        article.label = article.short_description; // highlight KB in bold fornt of KB article
        article.order = order; // order begin
				article.url = '/it?id=' + 'kb_article' + '&sys_id=' + article.sys_id;
        data.results.push(article);
        kbCount++;
        //order++; // order ends
				order--;
    }
}

function getCatalogItems() {
    var sc = new GlideRecord('sc_cat_item');
    sc.addQuery(textQuery, data.q);
    sc.addQuery('active', true);
    sc.addQuery('no_search', '!=', true);
    sc.addQuery('visible_standalone', true);
    sc.addQuery('sys_class_name', 'NOT IN', 'sc_cat_item_wizard');
    sc.addQuery('sc_catalogs', $sp.getValue('sc_catalog'));
    sc.query();

    var catCount = 10; // limit the search to 5
    var header = {}; // begin of header and label for catalog 

    header.type = "header";
    header.label = "<h4>Service Catalog</h4>";
    header.score = 10;
    header.order = order;
    header.text = "Service Catalog";
	
    data.results.push(header);
    //order++;
		order--;

		if (!sc.hasNext()) {
			header.label += '<i class="no-result">No results of this type.</i>';
		}
	
    while (sc.next() && catCount < data.limit) {
        if (!$sp.canReadRecord(sc))
            continue;

        var item = {};
			
				item.fullResult = $sp;
			
        if (sc.getRecordClassName() == "sc_cat_item_guide")
            item.type = "sc_guide";

        else if (sc.getRecordClassName() == "sc_cat_item_content") {
            var gr = new GlideRecord('sc_cat_item_content');
            gr.get(sc.getUniqueValue());
            $sp.getRecordValues(item, gr, 'url,content_type,kb_article');
            item.type = "sc_content";
        } else
            item.type = "sc";
				
        $sp.getRecordDisplayValues(item, sc, 'name,short_description,description,picture,price,sys_id');
        item.score = parseInt(sc.ir_query_score.getDisplayValue());
        item.order = order; //order begin
      
				item.label = item.name; // highlight SC in bold infront of catlog name ("Service Catalog "+)
			
				var urlid;
				if (sc.getRecordClassName() === 'sc_cat_item_guide')
					urlid = 'sc_cat_item_guide';
				else
					urlid = 'sc_cat_item'
			
				item.url = '/it?id=' + urlid + '&sys_id=' + item.sys_id;
				data.results.push(item);
        catCount++;
        //order++; // order ends
				order--;
    }
}

function getQuestions() {
    var questionGR = new GlideRecord("kb_social_qa_question");
    questionGR.addActiveQuery();
    questionGR.addQuery(textQuery, data.q);
    questionGR.query();
    var qCount = 0;
    var header = {}; // begin of header for questions 
    header.type = "header";
    header.label = "<h4>Questions</h4>";
    header.score = 0;
    header.order = order;
    header.text = "Questions";
    data.results.push(header);
    //order++;
		order--;

    while (questionGR.next() && qCount < data.limit) {
        if (!$sp.canReadRecord(questionGR))
            continue;

        var question = {};
        question.type = "qa";
        $sp.getRecordDisplayValues(question, questionGR, 'question,question_details,sys_created_on,sys_id');
        question.text = (question.question_details) ? $sp.stripHTML(question.question_details) : "";
        question.text = question.text.substring(0, 200);
        question.label = "<b>Question</b> " + question.question;
        question.score = 0;
        question.order = order; //order begin
        data.results.push(question);
        qCount++;
        //order++; // order ends
				order--;
    }
}

})();

Link Function

function(scope) {
	var lazyLoader = $injector.get("lazyLoader");
	lazyLoader.putTemplates(scope.data.typeaheadTemplates);
}

Template - sp-typeahead-popup-auburn.html

<ul class="dropdown-menu" ng-show="isOpen() && !moveInProgress" ng-style="{top: position().top+'px', left: position().left+'px'}" role="listbox" aria-hidden="{{!isOpen()}}">
    <li role='option' aria-hidden='true' style='display: none'></li>    
  	<li ng-repeat="match in matches track by $index" ng-class="{active: isActive($index)}" 
        ng-mouseenter="selectActive($index)" 
        ng-click="match.model.type === 'header' ? '' : selectMatch($index, $event)"
        role="option" id="{{::match.id}}">
        <div uib-typeahead-match index="$index" match="match" query="query" template-url="templateUrl"></div>
    </li>
</ul>

Template - sp-typeahead-auburn.html

<!-- replaced instances `match.label.label` with `match.label` [object Object] issue,
     since we changed the value of the `uib-typeahead` attribute in the HTML template -->

<!-- added to show category headers as simple spans instead of a elements -->
<span class="ta-item-header ta-item ng-scope" 
      ng-if="match.model.type === 'header'" ng-bind-html="match.label">
</span>

<!--<a class="ta-item" ng-href="{{match.model.target != '_blank' ? match.model.url : ''}}" target="{{match.model.target}}">-->
<a ng-if="match.model.type !== 'header'" class="ta-item" ng-href="{{ match.model.url }}" target="{{match.model.target}}">
	<div ng-if="!match.model.templateID">
    <!-- if service catalog and has icon -->
    <i class="ta-img ng-scope" ng-if="match.model.type.includes('sc') && match.model.picture.length > 0" 
       ng-style="{'background-image':'url({{match.model.picture}})'}"></i>
    <!-- if service catalog and has no icon -->
    <i class="ta-img ng-scope ta-icon fa fa-file-text-o" ng-if="match.model.type.includes('sc') && match.model.picture.length === 0"></i>
    <!-- if knowledge base -->
    <i class="ta-img ng-scope ta-icon fa fa-file-text-o" ng-if="match.model.type.includes('kb')"></i>
    <span ng-bind-html="match.label | uibTypeaheadHighlight:query"></span>
  </div>
  <div ng-if="match.model.templateID" ng-include="match.model.templateID"></div>
</a>

I'm currently not worthy.... lol. Did you have to do anything to your content items?  Set any roles for access? ACLs?

Negative.  Content items that have roles assigned to them won't show up in the search results unless you're logged in.  But this time it's on purpose.  We don't want people to see things they don't have access to, right?

Edit: actually, i'm not sure.  It was so long ago.  We do all the content item permissions on the that page, so maybe they do see them.

sethhumphrey
Mega Guru

I have gone back and added lines into the Server Script to have better search results based on partial word searches:

Specifically, lines were updated to add: + "*"

Examples: 

  • kb.addQuery(textQuery, data.q + "*");
  • sc.addQuery(textQuery, data.q + "*");
  • questionGR.addQuery(textQuery, data.q + "*");

Cuneyt
Tera Contributor

Hi,

I used the widget you shared but encountered some problems. First, the widget doesnt show the items, should I create some acl or user criteria for the items.

And found an interesting bug. The SC Category Widget keeps loading and doesnt show any items but if I resize the page to a mobile, items appear. The Console says "(authIntercepter) User has been logged out" which looks normal because I'm not logged in. Do you have any idea about this ? Because I checked your https://auburn.service-now.com/it and we are trying to make something similiar.

Our dev is https://dev21688.service-now.com/sp

 

Thanx,