- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-05-2019 05:06 AM
Good Day Portal Experts,
I am facing a challenge on CSS of Typeahead search widget where the client wants to change the color of the incident ( i.e. red ) and request ( i.e. black) in the search box result. Please refer to the attached image and guide.
my work
=======
search widget of the portal uses typeahead widget internally to bring out the search box results so now when I override the CSS of the typeahead widget CSS it showed all search results in red but not able to specifically apply different colors to incident and request.
Solved! Go to Solution.
- Labels:
-
Service Portal
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-08-2019 06:09 AM
So, even better ...
Since you are using a scripted source you have total control of what data you need to retrieve then leverage.
This line ...
$sp.getRecordDisplayValues(item, sc, 'name,short_description,picture,price,sys_id,sys_class_name');
... is getting the data from the Catalog Items. So, if you want to leverage the 'Category' field then you just need to add that field to the code ...
$sp.getRecordDisplayValues(item, sc, 'name,short_description,picture,price,sys_id,sys_class_name,category');
Then you can extend or replace my prior example for the 'sp-typeahead-popup-custom.html' template with ...
ng-class="{ active: isActive($index),
redtext: (match.model.category === 'incident'),
boldtext: (match.model.category === 'request') }"
So, since there is more control with scripted sources; you can control your outcome.
Also, recommend you add the following to the typeahead widget's client script so you can see the results data in the browser console while you are testing.
30: return $http.post("/api/now/sp/search", payload).then(function(response) {
31: var result = response.data.result;
32:
33: console.log(result.results) //<<ADD THIS to see the results objects returned.
Hope that gets you closer ...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-08-2019 05:28 AM
Here it is. This is available on the ServiceNow as well ( Service Portal-> Portal -> Service Portal -> Search Source ( Service Catalog)
Search page template
<div>
<a href="?id=form&sys_id={{item.sys_id}}&table={{item.table}}&category={{item.category}}" class="h4 text-primary m-b-sm block">
<span ng-bind-html="highlight(item.primary, data.q)"></span>
</a>
<span class="text-muted" ng-repeat="f in item.fields">
<span class="m-l-xs m-r-xs" ng-if="!$first"> · </span>
{{f.label}}: <span ng-bind-html="highlight(f.display_value, data.q)"></span>
</span>
</div>
Data fetch script
(function(query) {
var results = [];
//Here goes the logic. Compute results however you want!
if (!gs.isLoggedIn())
return results;
var portalValue = $sp.getCatalogs().value;
var sc;
sc = new sn_sc.CatalogSearch().search(portalValue, '', query);
sc.addQuery('sys_class_name', 'NOT IN', 'sc_cat_item_wizard');
sc.addEncodedQuery('hide_sp=false^ORhide_spISEMPTY');
if (facets.category)
sc.addQuery("category", "IN", facets.category.join(","));
if (facets.catalog)
sc.addQuery("sc_catalogs", "CONTAINS", facets.catalog);
sc.query();
var catCount = 0;
while (sc.next() && catCount < data.limit) {
var catalog_item = new sn_sc.CatItem(sc.getUniqueValue());
var category = catalog_item.getFirstAccessibleCategoryForSearch(facets.catalog ? facets.catalog : portalValue);
if (!catalog_item.canViewOnSearch()
|| !category) {
continue;
}
var item = {};
item.type = "sc"; // supported types are "sc" and "sc_content"
item.page = "sc_cat_item";
if (sc.getRecordClassName() == "sc_cat_item_guide")
item.page = "sc_cat_item_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";
}
$sp.getRecordDisplayValues(item, sc, 'name,short_description,sys_id,sys_class_name');
item.name = escapeHTMl(item.name);
item.short_description = escapeHTMl(item.short_description);
item['picture'] = catalog_item.getPicture();
item['icon'] = catalog_item.getIcon();
item['price'] = catalog_item.getCompleteItemPrice();
item.score = parseInt(sc.ir_query_score.getDisplayValue());
item.label = item.name;
item.primary = item.name;
// determine URL/target and default icon in case item has no picture/icon
if (item.type == "sc") {
item.url = '?id=' + item.page + '&sys_id=' + item.sys_id;
item.default_icon = "folder-open-o";
} else if (item.type == "sc_content") {
item.default_icon = "book";
if (item.content_type == "kb") {
item.url = '?id=kb_article&sys_id=' + item.kb_article;
item.default_icon = "file-text-o";
} else if (item.content_type == "external") {
item.url = item.url + "";
item.target = '_blank';
} else
item.url = '?id=sc_cat_item&sys_id=' + item.sys_id;
}
results.push(item);
catCount++;
}
$sp.logSearch('sc_cat_item', query, sc.getRowCount(), data.searchType);
function escapeHTMl(value) {
var entityMap = {
"&": "&",
"<": "<",
">": ">",
'"': '"',
"'": ''',
"/": '/'
};
if (!value) return value;
value = String(value).replace(/[&<>"'\/]/g, function (s) {
return entityMap[s];
});
return value;
}
return results;
})(query);
Facet generation script
(function(query, facetService, searchResults) {
var portalValue = $sp.getCatalogs().value;
var sc;
var results = [];
if (gs.getProperty("glide.sc.largeSet.optimization.enable", "false") == "true")
return;
sc = new sn_sc.CatalogSearch().search(portalValue, '', query);
sc.addQuery('sys_class_name', 'NOT IN', 'sc_cat_item_wizard');
sc.addEncodedQuery('hide_sp=false^ORhide_spISEMPTY');
sc.query();
var catItemCount = 0;
while (sc.next()) {
var primaryCategory = sc.getValue("category");
if (GlideStringUtil.isEligibleSysID(primaryCategory)) {
var primaryCategoryJS = new sn_sc.CatCategory(primaryCategory);
var catalog_item = new sn_sc.CatItem(sc.getUniqueValue());
var category = catalog_item.getFirstAccessibleCategoryForSearch(portalValue);
if (!catalog_item.canViewOnSearch()
|| !category || !primaryCategoryJS.canView()) {
continue;
}
var item = {};
item.categoryID = primaryCategory;
var categoryJS = new sn_sc.CatCategory(primaryCategory);
if (categoryJS) {
item.categoryLabel = categoryJS.getTitle();
item.catalogID = categoryJS.getCatalog();
var catalogJS = new sn_sc.Catalog(item.catalogID);
item.catalogLabel = catalogJS.getTitle();
}
results.push(item);
}
}
var catalogMap = {};
var categoryMap = {};
var catalogCount = 0;
results.forEach(function(item) {
var catalogLabel = item.catalogLabel;
var catalogValue = item.catalogID;
var categoryLabel = item.categoryLabel;
var categoryValue = item.categoryID;
if (!categoryMap[categoryLabel]) {
categoryMap[categoryLabel] = categoryValue;
} else if ((categoryMap[categoryLabel] + "").indexOf(categoryValue) < 0) {
categoryMap[categoryLabel] += "," + categoryValue;
}
if (!catalogMap[catalogLabel]) {
catalogMap[catalogLabel] = catalogValue;
catalogCount++;
}
});
if (portalValue.split(",").length > 1) {
var catalogFacet = facetService.createFacet(gs.getMessage('Catalog'), 'catalog');
for (var label in catalogMap)
catalogFacet.addFacetItem(label, catalogMap[label]);
}
var categoryFacet = facetService.createMultiChoiceFacet(gs.getMessage('ATF:Category_uppercase'), 'category');
for (var label in categoryMap)
categoryFacet.addFacetItem(label, categoryMap[label]);
})(query, facetService, searchResults);
Typeahead template
<!-- prefer item picture to item icon, prefer item icon to default icon -->
<i ng-if="match.model.picture" class="ta-img" color="yellow" style="background-image:url('{{match.model.picture}}?t=small')"></i>
<i ng-if="!match.model.picture && match.model.icon" class="ta-icon" style="background-image:url('{{match.model.icon}}'); width:16px; height:16px"></i>
<i ng-if="!match.model.picture && !match.model.icon" class="ta-icon fa fa-{{match.model.default_icon}}"></i>
<span ng-bind-html="match.label | uibTypeaheadHighlight:query"></span>
<strong ng-if="match.model.type == 'sc_content' && match.model.content_type == 'external'">➚</strong>
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-08-2019 06:09 AM
So, even better ...
Since you are using a scripted source you have total control of what data you need to retrieve then leverage.
This line ...
$sp.getRecordDisplayValues(item, sc, 'name,short_description,picture,price,sys_id,sys_class_name');
... is getting the data from the Catalog Items. So, if you want to leverage the 'Category' field then you just need to add that field to the code ...
$sp.getRecordDisplayValues(item, sc, 'name,short_description,picture,price,sys_id,sys_class_name,category');
Then you can extend or replace my prior example for the 'sp-typeahead-popup-custom.html' template with ...
ng-class="{ active: isActive($index),
redtext: (match.model.category === 'incident'),
boldtext: (match.model.category === 'request') }"
So, since there is more control with scripted sources; you can control your outcome.
Also, recommend you add the following to the typeahead widget's client script so you can see the results data in the browser console while you are testing.
30: return $http.post("/api/now/sp/search", payload).then(function(response) {
31: var result = response.data.result;
32:
33: console.log(result.results) //<<ADD THIS to see the results objects returned.
Hope that gets you closer ...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-11-2019 02:22 AM
Thanks a ton, Chris.