Step 4: Create a Yext Incident Deflection Widget for your Internal Service Portals

Overview

Next, we’ll walk through how to add Yext Search to the default Incident Form. Leveraging the power of Yext Search on this employee-focused form can help lower the volume of incidents by giving employees the answers they need in real time. The embedded search experience will automatically search based on incident description after the employee fills it out.

While this guide focuses on optimizing the default incident creation page, this widget template could be used for any record creation page with just a few small adjustments to the code. You’ll notice this is almost an identical process to the creation of the Case Deflection widget later in the guide, with a small change to the code to enable auto-search for the incident description instead of the case subject. Follow this pattern to tweak this code for any catalog item!

Create the Incident Deflection Widget

  1. Navigate to Service Portal > Widgets. Click New to create a new widget.

  2. Fill in the widget name and ID with something identifiable like Yext Incident Deflection and yextincidentdeflection respectively.

  3. Replace the code in the Body HTML template box with the following code:

    <div id=”answers-container”></div>
  4. 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_comments') != 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_comments');
    "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;
    }
    });
    }
  5. Replace the code in the Server script with the following code.

      ​​(function() {
    data.user = gs.getUserID();
      })();
  6. Click Submit.

  7. 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.

  8. 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 Incident Form

  1. Navigate to Service Portal > Pages.

  2. Select your Incident Form page. By default, this is most likely the Catalog Item page, with an ID of sc_cat_item.

  3. Make a copy by navigating to the page and clicking Clone Page.

  4. If you’d like the embedded search experience to appear on all record producer pages, navigate back to the original Catalog Item page. If you want to pick and choose certain catalog items for which to show Yext Search, use the cloned page instead and ensure you choose this page to be used for the desired catalog items.

  5. Scroll down to the Related Links section and click Open in Designer.

  6. 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.

  7. Add a two-column container from the Layouts section on the left. Choose the option with two equally sized columns.

  8. In the left column, add back the SC Catalog Item widget.

  9. Click the Pen icon to edit the settings for the widget instance. Uncheck the Order Item Section On Top checkbox and click Save.

  10. In the right column, add your new Yext Incident Deflection Widget.

Congratulations, your page is ready to go! To test the integration, log in to your Service Portal and navigate to Get Help > Create Incident. You’ll notice that once you finish typing in the issue description and navigate outside of the description input via mouse or keyboard, the content will be automatically searched on the right, providing relevant answers to the user based on their issue.

incident deflection