Embedding knowledge article as Widget in Catalog Item

Jake Golden
Tera Expert

hello,

 

Needing some help on trying to enhance a solution to the thread posted below:

Embedded Knowledge Article in Catalog Item (servicenow.com)

 

In that post - A widget is created and placed inside a catalog item to display a defaulted knowledge article.

 

What I am trying to accomplish is to create a widget that can be dynamic.

 

I currently have 3 choices in my catalog item. Category, SubCategory, and issue. Once these fields are selected a knowledge article number is populated on the catalog item. 

Code below:

Client Script:

function onChange(control, oldValue, newValue, isLoading) {
    if (!isLoading && newValue) {
        var category = g_form.getValue('category');
        var subcategory = g_form.getValue('subcategory');

        var kbGa = new GlideAjax('KB_Data_Utils');
        kbGa.addParam('sysparm_name', 'getKBID');
        kbGa.addParam('sysparm_cat', category);
        kbGa.addParam('sysparm_sub_cat', subcategory);
        kbGa.addParam('sysparm_issue', newValue);
        kbGa.getXMLAnswer(populateKBID);
    }

    function populateKBID(answer) {
        if (answer) {
            g_form.setValue('u_knowledge_article', answer);
        } else {
            g_form.setValue('u_knowledge_article', '');
        }
    }
}

 

 

Client Callable Script Include:

 

var KB_Data_Utils = Class.create();
KB_Data_Utils.prototype = Object.extendsObject(AbstractAjaxProcessor, {

    getKBID: function() {
		var category = this.getParameter('sysparm_cat');
		var subcategory = this.getParameter('sysparm_sub_cat');
		var issue = this.getParameter('sysparm_issue');
		
        var kbArticle = new GlideRecord('kb_knowledge');
        kbArticle.addQuery('u_knowledge_base', "General");
        kbArticle.addQuery('category', category);
        kbArticle.addQuery('u_subcategory', subcategory);
        kbArticle.addQuery('u_issue', issue);
        kbArticle.query();
        if (kbArticle.next()) {
            return kbArticle.sys_id + '';
        }
        return '';
    },

    type: 'KB_Data_Utils'
})

 

 

Somehow I would like to replicate the above code into the example post from above. Not sure where to start.

 

thanks,

Jake

 

1 ACCEPTED SOLUTION

AnveshKumar M
Tera Sage
Tera Sage

Hi @Jake Golden ,

You can keep the existing code as is and extend the catalog to display the KB in widget. I just tweaked the actual widget you referenced in your question. Follow along to get it.

Create a widget with the following scripts,

Name: Embedded KB Article

ID: embedded_kb_article

Body HTML Template: 

 

<div ng-if="data.isArticle" class="articleContainer">

    <!-- Shows the article's title - You can remove this line if you don't want to show this -->
    <h3 ng-bind-html="data.articleTitle"></h3>

    <!-- This line loads the article's HTML into the body of this widget -->
    <div ng-bind-html="data.articleHTML"></div>

</div>

 

 

CSS:

 

.articleContainer {
  overflow:scroll;
  max-height: 500px;
  border: 1px solid #DDDDDD;
  border-radius: 5px;
  padding: 16px;
}

 

 

Server Script:

 

(function() {	
	// Only run the below after the client controller calls this. Reason: Catalog item loads quicker & the script below requires the variable_name loaded in the page
	data.isArticle = false;
	if(input) { 

		var article = new GlideRecord('kb_knowledge');  // Initiate a glide record object (knowledge article table)
			article.addQuery('number', input.kbNumber);      // The KB number corresponds to the default value set in this variable of the record producer
			article.addQuery('workflow_state', 'published');// The KB article should be in the published state to be shown to end-users
			article.query();                                // Execute the query

			if(article.next()) { // If an article is found, show the article
				data.articleTitle = article.getDisplayValue('short_description').toString();               // Capture the title of the article
				data.articleHTML  = article.text.toString().replace('<![CDATA[ ', '').replace(' ]]>', ''); // Capture & clean the body HTML of the article
				data.isArticle = true;
			} else {             // If an article is not found, display the below message
				data.articleHTML = '<p>Article ' + data.kbNumber + ' missing or out-dated. Please contact support.<p>';
			}
	}
})();

 

 

Client Script:

 

api.controller = function($scope, $rootScope, $timeout) { // REMEBER to add $scope in the parameter list here (not in by default)
	var c = this;
	
	$rootScope.$on("field.change", function(evt, parms){
		if(parms.field.name == 'u_knowledge_article'){
			var kb_num = parms.field.value;
			if(kb_num){
				$scope.data.kbNumber = parms.field.value;
				c.server.update();
			}
		}
	});
	
};

 

 

Save the widget.

 

Change the client Callable script include to return the number instead of sys_id.

 

var KB_Data_Utils = Class.create();

KB_Data_Utils.prototype = Object.extendsObject(AbstractAjaxProcessor, {

 

    getKBID: function() {

  var category = this.getParameter('sysparm_cat');

  var subcategory = this.getParameter('sysparm_sub_cat');

  var issue = this.getParameter('sysparm_issue');

  

        var kbArticle = new GlideRecord('kb_knowledge');

        kbArticle.addQuery('u_knowledge_base', "General");

        kbArticle.addQuery('category', category);

        kbArticle.addQuery('u_subcategory', subcategory);

        kbArticle.addQuery('u_issue', issue);

        kbArticle.query();

        if (kbArticle.next()) {

            return kbArticle.number + '';

        }

        return '';

    },

 

    type: 'KB_Data_Utils'

})

Create a custom field in the catalog and set the widget field to the one we created in previous steps (you can refere the original article you referenced for this step), you can make the u_knowledge_article field to hidden, but don't delete it.

 

Now try filling all three fields and the KB will be previewed.

 

 

Thanks,
Anvesh

View solution in original post

5 REPLIES 5

AnveshKumar M
Tera Sage
Tera Sage

Hi @Jake Golden ,

You can keep the existing code as is and extend the catalog to display the KB in widget. I just tweaked the actual widget you referenced in your question. Follow along to get it.

Create a widget with the following scripts,

Name: Embedded KB Article

ID: embedded_kb_article

Body HTML Template: 

 

<div ng-if="data.isArticle" class="articleContainer">

    <!-- Shows the article's title - You can remove this line if you don't want to show this -->
    <h3 ng-bind-html="data.articleTitle"></h3>

    <!-- This line loads the article's HTML into the body of this widget -->
    <div ng-bind-html="data.articleHTML"></div>

</div>

 

 

CSS:

 

.articleContainer {
  overflow:scroll;
  max-height: 500px;
  border: 1px solid #DDDDDD;
  border-radius: 5px;
  padding: 16px;
}

 

 

Server Script:

 

(function() {	
	// Only run the below after the client controller calls this. Reason: Catalog item loads quicker & the script below requires the variable_name loaded in the page
	data.isArticle = false;
	if(input) { 

		var article = new GlideRecord('kb_knowledge');  // Initiate a glide record object (knowledge article table)
			article.addQuery('number', input.kbNumber);      // The KB number corresponds to the default value set in this variable of the record producer
			article.addQuery('workflow_state', 'published');// The KB article should be in the published state to be shown to end-users
			article.query();                                // Execute the query

			if(article.next()) { // If an article is found, show the article
				data.articleTitle = article.getDisplayValue('short_description').toString();               // Capture the title of the article
				data.articleHTML  = article.text.toString().replace('<![CDATA[ ', '').replace(' ]]>', ''); // Capture & clean the body HTML of the article
				data.isArticle = true;
			} else {             // If an article is not found, display the below message
				data.articleHTML = '<p>Article ' + data.kbNumber + ' missing or out-dated. Please contact support.<p>';
			}
	}
})();

 

 

Client Script:

 

api.controller = function($scope, $rootScope, $timeout) { // REMEBER to add $scope in the parameter list here (not in by default)
	var c = this;
	
	$rootScope.$on("field.change", function(evt, parms){
		if(parms.field.name == 'u_knowledge_article'){
			var kb_num = parms.field.value;
			if(kb_num){
				$scope.data.kbNumber = parms.field.value;
				c.server.update();
			}
		}
	});
	
};

 

 

Save the widget.

 

Change the client Callable script include to return the number instead of sys_id.

 

var KB_Data_Utils = Class.create();

KB_Data_Utils.prototype = Object.extendsObject(AbstractAjaxProcessor, {

 

    getKBID: function() {

  var category = this.getParameter('sysparm_cat');

  var subcategory = this.getParameter('sysparm_sub_cat');

  var issue = this.getParameter('sysparm_issue');

  

        var kbArticle = new GlideRecord('kb_knowledge');

        kbArticle.addQuery('u_knowledge_base', "General");

        kbArticle.addQuery('category', category);

        kbArticle.addQuery('u_subcategory', subcategory);

        kbArticle.addQuery('u_issue', issue);

        kbArticle.query();

        if (kbArticle.next()) {

            return kbArticle.number + '';

        }

        return '';

    },

 

    type: 'KB_Data_Utils'

})

Create a custom field in the catalog and set the widget field to the one we created in previous steps (you can refere the original article you referenced for this step), you can make the u_knowledge_article field to hidden, but don't delete it.

 

Now try filling all three fields and the KB will be previewed.

 

 

Thanks,
Anvesh

Hi @AnveshKumar M 

 

Thanks so much, works very well. I do have one sticking point and its when the KB article is not found. 

 

So right now it works whenever there is an associated KB to the issue, the article is found, and a preview occurs. If I change to another issue, that associate KB is found and previewed.

 

But if I select an issue that does not have an associated KB, the prior selected KB is still there, as though nothing updated and is not displaying the:

} else {             // If an article is not found, display the below message
				data.articleHTML = '<p>Article ' + data.kbNumber + ' missing or out-dated. Please contact support.<p>';
			}

thanks,

Jake 

Hi @Jake Golden ,

Just change the client script to fix this, try this one.

 

api.controller = function($scope, $rootScope, $timeout) { // REMEBER to add $scope in the parameter list here (not in by default)

 var c = this;

 

 $rootScope.$on("field.change", function(evt, parms){

  if(parms.field.name == 'u_knowledge_article'){

   var kb_num = parms.field.value;

   

    $scope.data.kbNumber = parms.field.value;

    c.server.update();

  }

 });

 

};

 

And in the server script do these modifications,

 

(function() { 

 // Only run the below after the client controller calls this. Reason: Catalog item loads quicker & the script below requires the variable_name loaded in the page

 data.isArticle = false;

var article_num = data.kbNumber;

 if(input) { 

 

  var article = new GlideRecord('kb_knowledge'); // Initiate a glide record object (knowledge article table)

   article.addQuery('number', input.kbNumber); // The KB number corresponds to the default value set in this variable of the record producer

   article.addQuery('workflow_state', 'published');// The KB article should be in the published state to be shown to end-users

   article.query(); // Execute the query

 

   if(article.next()) { // If an article is found, show the article

    data.articleTitle = article.getDisplayValue('short_description').toString(); // Capture the title of the article

    data.articleHTML = article.text.toString().replace('<![CDATA[ ', '').replace(' ]]>', ''); // Capture & clean the body HTML of the article

    data.isArticle = true;

   } else if(gs.nil(article_num)){

 

data.articleHTML = '<p> No KB Article found.</p>';

}else { // If an article is not found, display the below message

    data.articleHTML = '<p>Article ' + data.kbNumber + ' missing or out-dated. Please contact support.<p>';

   }

 }

})();

 

 

 

Thanks,
Anvesh

Awesome! Thank you so much for your help @AnveshKumar M 

 

I also added a data.articleTitle to your elseif statement to replace the header that is previewed. But everything works perfectly.