Add tabs to Catalog Item form

Dazler
Mega Sage

Hi,

 

I have a catalog item form that is rather large in variables.  I use containers to clean it a little, but it would look better and more presentable in tabs.

 

I found this article, https://www.servicenow.com/community/developer-articles/how-to-create-a-tabbed-catalog-item-in-servi....  But, I don't want to have to adjust the OOB widget for SC Catalog Item.  

 

I found another article that shows how to create this same effect using a widget within a Custom variable and ui policies.

https://www.servicenowelite.com/blog/2019/11/20/catalog-item-sections


But I am not sure how I can accommodate for mandatory fields.  Especially on the tabs.  

 

Has anyone created a tabbed version on the catalog item similiar to the second article?  

1 ACCEPTED SOLUTION

@Dazler- With the second option you posted only one widget is needed. The widget is used to control which "tab" is selected. All the variables are created normally within the record producer thus allowing you to use UI policies and/or Client scripts as you would normally. (depending on requirements you might need to get a little creative)

The basic idea behind it is that you start with hiding all the variables.

The current_section variable holds the index number of the tab that would be selected; default first tab

The widget that holds the tab selectors populates the hidden current_section variable with the corresponding tab number

A Client script is used to show/hide variables based on the value in current_section (tab index number)

I'll add some screenshots of a mock up I did that includes using a variable that is mandatory in it's variable setup and a variable that is made mandatory via a UI policy based on a variable value that is in a different tab.

 

In my mock I changed some things to make it a little more clear (I hope) than what is presented in the link you posted.

Record Producer Variable setup:

record_producer_variable_setup.png

 

Catalog Client Script setup:

client_scripts.png

 

Client Script: Initial Display

//Initial Display OnLoad 
function onLoad() {
    g_form.setDisplay('variable1_tab1', true);
    g_form.setDisplay('variable2_tab1', true);
}

 

Client Script: Variable Controller

//Variable Controller OnChange  (current_section)
function onChange(control, oldValue, newValue, isLoading) {
    if (isLoading || newValue == '') {
        return;
    }

    if (newValue == '0' || newValue == '1') {
       //display variables for tab 1
        g_form.setDisplay('variable1_tab1', true);
        g_form.setDisplay('variable2_tab1', true);

        //variables that should not dipslay on tab 1
        g_form.setDisplay('variable1_tab2', false);
        g_form.setDisplay('variable2_tab2', false);
    }

    if (newValue == '2') {
       //display variables for tab 2
        g_form.setDisplay('variable1_tab2', true);
        g_form.setDisplay('variable2_tab2', true);

       //variables that should not display on tab 2
        g_form.setDisplay('variable1_tab1', false);
        g_form.setDisplay('variable2_tab1', false);
    }

}

 

UI Policies:

ui_policies.pngPolicy setup: (Makes variable 2 on tab 2 mandatory based on variable 2 tab 1 value)

ui_policy_setup.png

 

Variable with type Custom holding the custom widget to control the tabs

type_custom_variable.png

 

For the widget I didn't get fancy. I simply used what is already in Service Portal. But below is the widget setup.

Body HTML Template

<div>
  <button ng-click="setTabIndex('1',$event)" class="btn btn-primary" data-tab-index="1">Tab 1</button>
  <button ng-click="setTabIndex('2', $event)" class="btn btn-default" data-tab-index="2">Tab 2</button>
</div>

 

Client Controller

api.controller = function($scope) {
    /* widget controller */
    var c = this;

	//g_form is avaiable via the page from $scope. Making it shorter here.
    var gform = $scope.page.g_form;

    $scope.setTabIndex = function(index, $event) {
        var oldValue = gform.getValue('current_section');
        if (index == '2') {
            mandatoryCheck('variable2_tab1', oldValue, index, $event);
            return;
        }

        gform.setValue('current_section', index);
        $event.target.classList.remove('btn-default');
        $event.target.classList.add('btn-primary');
		setActiveButton();
    }

    function setActiveButton(){
		var activeTab = gform.getValue('current_section');
		$('[data-tab-index="' + activeTab + '"]').removeClass('btn-default').addClass('btn-primary');

		$('[data-tab-index]').filter(function(){
			return $(this).attr('data-tab-index') != activeTab;
		}).removeClass('btn-primary').addClass('btn-default');

	}

    function mandatoryCheck(variableName, oldValue, newValue, $event) {
        if (gform.isMandatory(variableName) && gform.getValue(variableName) == "") {
            gform.showErrorBox(variableName, "Populate Mandatory field before moving on");
            gform.setValue('current_section', oldValue)
            return;
        }

        gform.setValue('current_section', newValue);
        $event.target.classList.remove('btn-default');
        $event.target.classList.add('btn-primary');
		setActiveButton();
    }

};

 

Again, I didn't get fancy with the code. It's simple so that it should be clear. It could be more optimized I'm sure but I'm sure you have specific requirements and if you use this it would be up to you to take the concept in modify to your needs.

 

Here is a working example of the functionality:

https://youtu.be/6b0sK16li6o

 

View solution in original post

Reply to a ServiceNow Community question: Add tabs to Catalog Item form https://www.servicenow.com/community/developer-forum/add-tabs-to-catalog-item-form/m-p/2603033#M1012488
17 REPLIES 17

ChrisBurks
Mega Sage

I haven't done this for tabs but I have used a custom widget as a custom variable with mandatory fields. Since this widget will be custom you control the html markup in it. So one way of doing this is having input fields as type hidden and that have an id attribute. Values that need to be mandatory when populated can populate correlating hidden inputs with either a specific value or a value of true.

In an onSubmit client catalog script you can capture the values of the inputs that correlate with the mandatory fields and check if they are populated or true/false. Depending upon the result cancel/allow the submit.

 

@Dazler- With the second option you posted only one widget is needed. The widget is used to control which "tab" is selected. All the variables are created normally within the record producer thus allowing you to use UI policies and/or Client scripts as you would normally. (depending on requirements you might need to get a little creative)

The basic idea behind it is that you start with hiding all the variables.

The current_section variable holds the index number of the tab that would be selected; default first tab

The widget that holds the tab selectors populates the hidden current_section variable with the corresponding tab number

A Client script is used to show/hide variables based on the value in current_section (tab index number)

I'll add some screenshots of a mock up I did that includes using a variable that is mandatory in it's variable setup and a variable that is made mandatory via a UI policy based on a variable value that is in a different tab.

 

In my mock I changed some things to make it a little more clear (I hope) than what is presented in the link you posted.

Record Producer Variable setup:

record_producer_variable_setup.png

 

Catalog Client Script setup:

client_scripts.png

 

Client Script: Initial Display

//Initial Display OnLoad 
function onLoad() {
    g_form.setDisplay('variable1_tab1', true);
    g_form.setDisplay('variable2_tab1', true);
}

 

Client Script: Variable Controller

//Variable Controller OnChange  (current_section)
function onChange(control, oldValue, newValue, isLoading) {
    if (isLoading || newValue == '') {
        return;
    }

    if (newValue == '0' || newValue == '1') {
       //display variables for tab 1
        g_form.setDisplay('variable1_tab1', true);
        g_form.setDisplay('variable2_tab1', true);

        //variables that should not dipslay on tab 1
        g_form.setDisplay('variable1_tab2', false);
        g_form.setDisplay('variable2_tab2', false);
    }

    if (newValue == '2') {
       //display variables for tab 2
        g_form.setDisplay('variable1_tab2', true);
        g_form.setDisplay('variable2_tab2', true);

       //variables that should not display on tab 2
        g_form.setDisplay('variable1_tab1', false);
        g_form.setDisplay('variable2_tab1', false);
    }

}

 

UI Policies:

ui_policies.pngPolicy setup: (Makes variable 2 on tab 2 mandatory based on variable 2 tab 1 value)

ui_policy_setup.png

 

Variable with type Custom holding the custom widget to control the tabs

type_custom_variable.png

 

For the widget I didn't get fancy. I simply used what is already in Service Portal. But below is the widget setup.

Body HTML Template

<div>
  <button ng-click="setTabIndex('1',$event)" class="btn btn-primary" data-tab-index="1">Tab 1</button>
  <button ng-click="setTabIndex('2', $event)" class="btn btn-default" data-tab-index="2">Tab 2</button>
</div>

 

Client Controller

api.controller = function($scope) {
    /* widget controller */
    var c = this;

	//g_form is avaiable via the page from $scope. Making it shorter here.
    var gform = $scope.page.g_form;

    $scope.setTabIndex = function(index, $event) {
        var oldValue = gform.getValue('current_section');
        if (index == '2') {
            mandatoryCheck('variable2_tab1', oldValue, index, $event);
            return;
        }

        gform.setValue('current_section', index);
        $event.target.classList.remove('btn-default');
        $event.target.classList.add('btn-primary');
		setActiveButton();
    }

    function setActiveButton(){
		var activeTab = gform.getValue('current_section');
		$('[data-tab-index="' + activeTab + '"]').removeClass('btn-default').addClass('btn-primary');

		$('[data-tab-index]').filter(function(){
			return $(this).attr('data-tab-index') != activeTab;
		}).removeClass('btn-primary').addClass('btn-default');

	}

    function mandatoryCheck(variableName, oldValue, newValue, $event) {
        if (gform.isMandatory(variableName) && gform.getValue(variableName) == "") {
            gform.showErrorBox(variableName, "Populate Mandatory field before moving on");
            gform.setValue('current_section', oldValue)
            return;
        }

        gform.setValue('current_section', newValue);
        $event.target.classList.remove('btn-default');
        $event.target.classList.add('btn-primary');
		setActiveButton();
    }

};

 

Again, I didn't get fancy with the code. It's simple so that it should be clear. It could be more optimized I'm sure but I'm sure you have specific requirements and if you use this it would be up to you to take the concept in modify to your needs.

 

Here is a working example of the functionality:

https://youtu.be/6b0sK16li6o

 

Reply to a ServiceNow Community question: Add tabs to Catalog Item form https://www.servicenow.com/community/developer-forum/add-tabs-to-catalog-item-form/m-p/2603033#M1012488

Hi @ChrisBurks,

 

This is awesome!  This helped me so much.  Thank you!

Hello Chris,

I was trying this solution, but whenever I change the tab, all the entered data are disappeared.

Any idea or suggestion is greatly appreciated.

Thank you.

abrouf