Step 5: Create a Yext Case Deflection Widget for your External Service Portals
Overview
Next, we’ll walk through how to add Yext Search to the default Case Form in the Consumer Service Portal. This is an OOTB customer-facing Service Portal that comes as part of the Customer Service Management solution created by ServiceNow. This step of the guide assumes you have installed Customer Service Management and have provisioned access to the Consumer Service Portal user roles to the admin who will be walking through these steps.
Leveraging the power of Yext Search on the Case Form can help lower the volume of customer cases by giving users the answers they are looking for in real time. The embedded search experience will automatically search based on the subject of the case as the customer fills it out.
Create the Case Deflection Widget
Navigate to Service Portal > Widgets. Click New to create a new widget.
Fill in the widget name and ID with something identifiable like
Yext Case Deflection
andyextcasedeflection
respectively.Replace the code in the Body HTML template box with the following code:
<div id=”answers-container”></div>
Replace the code in the Client controller box with the following code. Note: make sure you fill in the placeholder with your search experience production URL on line 3 of the code snippet.
api.controller=function($scope) { var c = this; var url = "REPLACE_ME_PRODUCTION_URL"; var userId = c.data.user; var sourceUrlParams = window.top.location.search; generateIFrame(url); // observe changes in iframe src to init experience each time var targetInitNode = document.getElementById('answers-frame'); var initObserverConfig = {attributes: true}; var initCallback = function() { initializeExperience(userId,sourceUrlParams); }; var initObserver = new MutationObserver(initCallback); initObserver.observe(targetInitNode, initObserverConfig); // observe main container changes to set up interacivity var targetAutosearchNode = document.querySelector("[ng-class='::main.getContainerClasses(container)']"); var autosearchObserverConfig = { attributes: false, childList: true, subtree: true }; var autosearchCallback = function() { if (document.getElementById('sp_formfield_short_description') != null) { enableInteractivity(); autosearchObserver.disconnect(); } }; var autosearchObserver = new MutationObserver(autosearchCallback); autosearchObserver.observe(targetAutosearchNode, autosearchObserverConfig); }; function generateIFrame(domain) { var containerEl = document.querySelector('#answers-container'); var iframe = document.createElement('iframe'); var pathToIndex = containerEl.dataset.path; iframe.allow = 'geolocation'; domain = domain || ''; var queryParam = queryParam || 'query'; var urlParam = urlParam || 'verticalUrl'; var calcFrameSrc = function() { var paramString = window.location.search; paramString = paramString.substr(1, paramString.length); // Decode ASCII forward slash to avod repeat encodings on page refreshes paramString = paramString.replace("%2F", "/"); // Parse the params out of the URL var params = paramString.split('&'), verticalUrl; var referrerPageUrl = document.referrer.split('?')[0].split('#')[0]; if (pathToIndex) { verticalUrl = pathToIndex; } // Don't include the verticalUrl or raw referrerPageUrl in the frame src var new_params = params.filter(function(param) { return (param.split('=')[0] !== 'verticalUrl') && (param.split('=')[0] !== 'referrerPageUrl'); }); for (var i = 0; i < params.length; i ++) { var kv = params[i].split('='); if (kv[0] === 'verticalUrl') { verticalUrl = kv[1]; } if (kv[0] === 'referrerPageUrl') { referrerPageUrl = kv[1]; } } new_params.push('referrerPageUrl=' + referrerPageUrl); // Build the Iframe URL var iframeUrl = domain; if (verticalUrl) { iframeUrl += '/' + verticalUrl; } iframeUrl += '?' + new_params.join('&'); return iframeUrl; }; iframe.src = calcFrameSrc(); iframe.frameBorder = 0; // For dynamic iFrame sizing iframe.style.width = '1px'; iframe.style.minWidth = '100%'; iframe.id = 'answers-frame'; containerEl.appendChild(iframe); // For dynamic iFrame resizing window.iFrameResize({ checkOrigin: false, onMessage: function(messageData) { var message = JSON.parse(messageData.message); if (message.action === "paginate") { var iframeOffsetTop = iframe.offsetTop; document.documentElement.scrollTop = iframeOffsetTop; document.body.scrollTop = iframeOffsetTop; // For Safari return; } var params = message.params; var pageTitle = message.pageTitle; if (pageTitle) { iframe.title = pageTitle; } iframe.iFrameResizer.resize(); // var currLocation = window.location.href.split('?')[0]; // var newLocation = currLocation + '?' + params; // if (window.location.href !== newLocation) { // history.replaceState({query: params}, window.document.title, newLocation); // } } }, '#answers-frame'); } function initializeExperience(user,src) { var visitorObj = { id: user, idMethod: "sn_user_id" }; var configObject = { querySource: src, visitor: visitorObj }; var msg = JSON.stringify({ action: "passConfig", value: configObject }); var frameEl = document.getElementById("answers-frame"); frameEl.contentWindow.postMessage(msg, "*"); }; function enableInteractivity() { var answersQuery = ""; var subject = document.getElementById('sp_formfield_short_description'); "enable func running"; subject.addEventListener('blur', function(e) { if (e.target.value !== "") { var newQuery = e.target.value; var msg = JSON.stringify({ "action": "setQuery", "value": newQuery }); var frameEl = document.getElementById("answers-frame"); frameEl.contentWindow.postMessage(msg, "*"); answersQuery = newQuery; } }); }
Replace the code in the Server script with the following code.
(function() { data.user = gs.getUserID(); })();
Click Submit.
Back in your widget, scroll to the bottom of the page to the ** Dependencies ** table. Click Edit to select the dependency you added to your Search Results Widget.
Search for the iFrame Resizer JS dependency you created earlier in the guide. Select it and click the right arrow to add the dependency to this widget. Click Save.
Congratulations, you’ve successfully created your widget! Now we’ll walk through adding the widget to the default incident creation form.
Add the Deflection Widget to your Customer Case Form
Navigate to Service Portal > Pages.
Select the Get Help page. This is the default record producer for the Customer Service Portal.
Make a copy by navigating to the page and clicking Clone Page.
Navigate back to the original page. Scroll down to the Related Links section and click Open in Designer.
Delete all widgets and containers from the page to provide a clean starting template. You may need to refresh the page after doing so in order to add new container layouts.
Add a two-column container from the Layouts section on the left. Choose the option with two equally sized columns.
In the left column, add back the SC Catalog Item widget.
Click the Pen icon to edit the settings for the widget instance. Uncheck the Order Item Section On Top checkbox and click Save.
In the right column, add your new Yext Case Deflection Widget.
Congratulations, your page is ready to go! To test the integration, log in to your Customer Service Portal and navigate to Get Help.
You’ll notice that once you finish typing in the subject of the case and navigate outside of the input box via mouse or keyboard, the content will be automatically searched within the embedded experience, providing relevant answers to the user based on their issue: