
- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
on 03-23-2022 11:06 AM
For table fields, ServiceNow provides a very useful Conditions field type which essentially adds a condition builder to a form and stores the corresponding encoded query as its value. However, there is no OOTB equivalent Conditions variable type for record producer/catalog item variables. In this article, I will demonstrate how to replicate the Conditions field type for Service Portal variables (I will be configuring a record producer, but the same can be applied to catalog items as well).
1. Create record producer (RP) which will display the Conditions variable, include detailed instructions in the RP's Description to reduce potential end-user confusion:
2. Navigate to the OOTB widget: SN Desktop Filter, and click Clone Widget (this will automatically add the necessary SN Filter dependency to your new widget):
3. Rename your newly cloned widget accordingly, for example I named mine Location Manager Query Builder.
4. Add a new variable to your RP with Type = Custom with Label, and set the variable's Widget = your newly cloned widget from step 3:
5. Add a new variable to your RP with Type = Multi Line Text and Read only = true, this is where we will store the encoded query generated from your new Query Builder widget:
6. Update the Server script of your new Query Builder widget to set data.table and data.initialQuery according to your requirements (in my example I am querying the cmn_location table and I have an initial query of Country is USA):
(function() {
/* populate the 'data' object */
/* e.g., data.table = $sp.getValue('table'); */
data.table = 'cmn_location';
data.initialQuery = 'country=USA';
})();
7. Optional: If you are providing an initial query in your Query Builder widget, be sure to set the default value of your Multi Line Text variable to match:
8. Update the Client controller of your new Query Builder widget to set c.config.closeFilter = false (removes the X button from the widget since it doesn't work in this context), and add an event listener to update your Multi Line Text variable with the encoded query value of the Query Builder when user clicks Run:
function($scope) {
/* widget controller */
var c = this;
c.config = {
outputType: "encoded_query",
closeFilter: false, //remove X button since it doesn't work in this context
encodedQuery: massageEncodedQuery(c.data.initialQuery),
manageFiltersLink: "?id=lf&table=sys_filter"
};
//add event listener to update Multi Line Text variable with the encoded query value of the Query Builder when user clicks Run
$scope.$on("snfilter:update_query", function(e, query) {
$scope.page.g_form.setValue('encoded_query', massageEncodedQuery(query));
});
function massageEncodedQuery(encodedQuery) {
return (encodedQuery) ? encodedQuery.replace(/CONTAINS/g, "LIKE").replace(/DOES NOT CONTAIN/g, "NOT LIKE") : encodedQuery;
}
}
9. At this point, the basic Conditions field type functionality is in place for the RP from Service Portal. The end-user can build a query with the familiar condition builder UI, and the resulting encoded query can be accessed from the Multi Line Text variable to perform whatever operations are needed after submission:
All steps that follow are to further enhance the end-user's experience:
10. It may be useful for the end-user to validate which records are returned by their query before submitting. To allow them to do so, begin by adding a new variable to your RP with Type = HTML and Read only = true:
11. Create new client-callable script include to retrieve the number of records returned by the query:
var queryBuilderUtils = Class.create();
queryBuilderUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {
getMatchingCount: function(sysparm_encQuery) {
var encQuery = global.JSUtil.nil(sysparm_encQuery) ? '' + this.getParameter('sysparm_encQuery') : '' + sysparm_encQuery;
var recordCount = new GlideAggregate('cmn_location');
recordCount.addEncodedQuery(encQuery);
recordCount.addAggregate('COUNT');
recordCount.query();
return (recordCount.next() ? recordCount.getAggregate('COUNT') : 0);
},
type: 'queryBuilderUtils'
});
12. Add new onLoad catalog client script to RP for populating HTML variable from step 9 when form loads. Be sure to set UI Type = Mobile / Service Portal:
function onLoad() {
//Type appropriate comment here, and begin script below
var encQuery = g_form.getValue('encoded_query') ? g_form.getValue('encoded_query') : '';
var ga = new GlideAjax('global.queryBuilderUtils');
ga.addParam('sysparm_name', 'getMatchingCount');
ga.addParam('sysparm_encQuery', encQuery);
ga.getXMLAnswer(function(answer) {
var url = '<h4><a href="/cmn_location_list.do?sysparm_query=' + encQuery + '" target="_blank">' + answer + ' Locations found</a></h4>';
g_form.setValue('matching_locations', url);
});
}
13. Add new onChange catalog client script to RP for populating HTML variable from step 9 when the encoded_query variable changes. Again be sure to set UI Type = Mobile / Service Portal:
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading || newValue == '') {
return;
}
//Type appropriate comment here, and begin script below
var encQuery = newValue;
var ga = new GlideAjax('global.queryBuilderUtils');
ga.addParam('sysparm_name', 'getMatchingCount');
ga.addParam('sysparm_encQuery', encQuery);
ga.getXMLAnswer(function(answer) {
var url = '<h4><a href="/cmn_location_list.do?sysparm_query=' + encQuery + '" target="_blank">' + answer + ' Locations found</a></h4>';
g_form.setValue('matching_locations', url);
});
}
14. Now your end-user has a way to validate the records returned by their query via form-generated link:
15. It may be useful to not allow the end-user to Submit the RP if their query does not return any records. It may also be useful to require a "double-check" for the end-user to confirm before Submitting the RP. Both of these requirements can be accomplished by adding a new onSubmit catalog client script to your RP. Again be sure to set UI Type = Mobile / Service Portal:
function onSubmit() {
//Type appropriate comment here, and begin script below
if (g_scratchpad.isFormValid) {
return true;
}
var encQuery = g_form.getValue('encoded_query');
var ga = new GlideAjax('global.queryBuilderUtils');
ga.addParam('sysparm_name', 'getMatchingCount');
ga.addParam('sysparm_encQuery', encQuery);
ga.getXMLAnswer(setAnswer);
return false;
function setAnswer(answer) {
if (answer == '0') {
g_form.addErrorMessage('At least 1 ServiceNow Location must be returned by your query. Request not submitted.');
return false;
} else {
var popup = confirm(answer + ' Locations will be processed with xyz operations. Do you want to continue?');
if (!popup) {
return false;
} else {
g_scratchpad.isFormValid = true;
g_form.submit(g_form.getActionName());
}
}
}
}
I hope you will find this article helpful! Please let me know if there are any questions or feedback.
If this article helped you , then please bookmark it or mark it as helpful.
Regards,
Christopher Perry
- 9,508 Views
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Very Good Article...
Thanks a lot.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Absolutely amazing!
A very well written article with clear instructions and screenshots to reference.....and it works.
Thank you for sharing this, this is exactly what I needed.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hello,
This is a helpful article.
Is there a way to dynamically choose the table name based on another variable on the catalog item form in Step 6?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi,
Its working only for OR condition , Cannot we update the filter with AND conditions?

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
@Priya2 it should work for both AND conditions and OR conditions already. Since we are cloning the OOB sn-desktop-filter widget, we are using the OOB code to translate filter conditions UI to an encoded query string.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hello Chrisperry,
I have written this on 'Change_Request' table. Can we add any query in server script like - only Change Records with Configuration Item on 'Generic Application' Class should populate to select in this condition builder.
I have written below query but it is not working. Can you please suggest me.
Code -
data.table = 'change_request';
data.query = 'cmdb_ci.sys_class_name=cmdb_ci_appl_generic';
data.initialQuery = 'cmdb_ci= ';
Thanks & Regards,
S.Swaroop.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
is their is a way to set tha data.table's dynamically. record producer have field named table when any table is selected that will be used in condition builder. if anyone can help me with this
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hello @Hamid
Do you got a chance to pass data.table dynamically. I am still looking for solutions. I am trying to pass table name from a reference field in record producer where i am taking input from user. Rest i am able to modify like record URL and record Count but struggling to modify data.table dynamically.
CC Service Portal FAQ Service Portal Design Guidelines @Ankur Bawiskar @Chuck Tomasi @Pradeep Sharma Our Past MVP Awardees @Göran Lundqvist
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
How to add Change From, Change To, Changes in the filter option
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
How to add to below option to the filter
extended_operators=VALCHANGES;CHANGESFROM;CHANGESTO
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
@rvkvivekofficia - I am also facing the same issue. Did you get any way to add the extended operators?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
@chrisperry - Very good article. I have scenario where I would be need the changes/changes from/changes to operators. Is there a way to bring those operator in this widget?

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi @rvkvivekofficia & @ahan ,
No, it is not possible to add the changes/changes to/changes from operators to this widget. Those operators are specific to business rule trigger conditions, not for filtering table records. The widget replicates the same options that are available when filtering table records -- for example, if you are reporting on the incident table, you cannot add a filter for 'Caller changes', if that makes sense.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi @chrisperry,
Can you please let me know how to get the data.table dynamically as my requirement is to pass the table value from another reference field
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
HI @chrisperry Thanks for this useful information it helped a lot.
currently, I am having issues with extended filters like changes, changes to and changes from
I need to add these filters to this widget.
@Chuck Tomasi
@PrabhaU
@ahan
@Kieran Anson
@rvkvivekofficia
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Great article, it was super useful! My use case required being able to dynamically select the table being used from a catalog item variable. My variable is named "selected_table". Make sure you have a default table name in the variable so the widget renders with the filters active. Then I used this function in my controller to watch for changes to 'selected_table' and when found to broadcast the events to rootscope. This dynamically updates the table used in the desktop filter so it shows the correct field names to filter on. Hopefully this helps someone else.
//Watch for changes in the selected_table variable
$scope.$watch(function() {
return $scope.page.g_form.getValue('selected_table');
}, function(value){
c.data.table = $scope.page.g_form.getValue('selected_table');
$rootScope.$broadcast('snfilter:update_table', {table: c.data.table});
$rootScope.$broadcast("snfilter:initialize_query", c.data.table);
});
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
@rvkvivekofficia and @hamid laghari1 please refer to below link for extended operators
Extended Operators in SN Desktop Filter widget
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
@JenniferFoster Thank you so much for your post. This article was great, but your post was what I was really looking for. The issue however, is that When I place your function in my Client Control, all I get is "keywords" in my field list regardless of the table I choose. I dont see any of the table fields.
After troubleshooting a ton, I figured out my issue. The widget wants the table name not the display value of the table name. (Ex: change_request, sc_req_item). My Reference field to the sys_db_object field was not working as it wasn't returning the actual table name..... So I switched it to a "Lookup Select Box" type, set the Lookup Value Field to "Name", and set the Lookup Label Field to "label". In addition, per your suggestion, I set the default value to a value (i choose sc_req_item).
It works.
Posting this as well incase someone hits the same issue I did.
Thank you very much!