chrisperry
Giga Sage

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:

find_real_file.png

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):

find_real_file.png

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:

find_real_file.png

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:

 find_real_file.png

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:

find_real_file.png

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:

 find_real_file.png

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:

 find_real_file.png

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:

find_real_file.png

find_real_file.png

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());
            }
        }
    }
}

find_real_file.png

 

find_real_file.png

 

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

Comments
Anjaneyulu1
Giga Contributor

Very Good Article...

Thanks a lot.

danielbartholom
Mega Expert

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.

Samya Andra
Tera Contributor

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?

Priya2
Tera Contributor

Hi, 

Its working only for OR condition , Cannot we update the filter with AND conditions?

chrisperry
Giga Sage

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

swaroop
Kilo Sage

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.

hamid laghari
Tera Contributor

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

kumarsatyam
Tera Expert

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 

 

rvkvivekofficia
Tera Contributor

How to add Change From, Change To, Changes in the filter option

rvkvivekofficia
Tera Contributor

How to add to below option to the filter 
extended_operators=VALCHANGES;CHANGESFROM;CHANGESTO

ahan
Tera Guru

@rvkvivekofficia - I am also facing the same issue. Did you get any way to add the extended operators?

 

ahan
Tera Guru

@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?

chrisperry
Giga Sage

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.

priyaaggarw
Tera Contributor

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

hamid laghari1
Tera Contributor

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 

JenniferFoster
Tera Explorer

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);
        });

ahan
Tera Guru

@rvkvivekofficia and @hamid laghari1 please refer to below link for extended operators
Extended Operators in SN Desktop Filter widget 

KevinCask
Tera Contributor

@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!

Version history
Last update:
‎03-23-2022 11:06 AM
Updated by: