• David Tessaro

Tab-Style Menu for Simple Lists

Updated: Jan 10

Enhancing the Simple List widget with user-friendly navigation

One essential and often under-appreciated feature of every self-service portal is the My Tickets page. This is usually as simple as one list of open tickets, whether they be incidents, requests, or maybe a unified list. However, often enough I come across the need to display multiple views of ticket lists.

For example, giving access to closed tickets would result in two lists: My Open Tickets, and My Closed Tickets

What if you are a manager and you want to see your team’s tickets? Then you may want these four lists:

  1. My Open Tickets

  2. My Closed Tickets

  3. Team’s Open Tickets

  4. Team’s Closed Tickets

There may be even more lists that you want to show. On average I see two to four, but I’ve seen as many as eight different tickets lists in a portal.

Sure, you could put a dropdown menu in the header, but a much nicer solution would be a tabbed menu of sorts. This would be easier to access, and it can give easy indication of what list is currently being displayed. Something like this:

Let’s create a menu widget designed specifically for navigating a series of lists. Actually, we’re going to create two widgets:

  1. Menu for Lists

  2. Simple List w Menu

You may be thinking, why don’t we just create one widget to put at the top of a list? Why two widgets? Because, if we have multiple lists to navigate, that will result in multiple pages. If we put a Menu for Lists widget on each page, then we will have an instance of that widget for every page. We’re already going to have an instance of Simple List on each page. So, we can simply call our Menu for Lists from the Simple List widget and avoid the need for additional instances of a widget that isn’t going to change. If you’re brain hurts, just read on and you’ll understand.

Step 1: Clone the Simple List widget.

Go to the Widgets module and open the Simple List widget. It’ll be read-only.

Click the Clone Widget button in the upper right to create your own copy.

Give the widget a new name. I’ll use the name “Simple List w Menu”. Click Update.

Step 2: Create option for selecting a menu

We’ll want the option of choosing which menu to use for our list. Let’s create an option for that purpose. To do that, we’ll use the Widget Editor. Find and click Service Portal Configuration in the navigator:

Then click on the Widget Editor button

Find your new widget in the Recently Updated list and click on it.

This opens your widget in editor mode.

Click on the menu in the upper right.

Choose Edit option schema. That will open the options panel where we can add a new option.

You’ll see all the existing options for this widget, which we’ll leave alone. Click on the plus icon in the upper right to add a new option. You’ll need to look to the bottom of this window to see your new, blank option.

We are creating an option to reference the Widget Instance table so we can select the menu we create in later steps. Fill in the fields like below, then click Save.

The widget options seem to be broken in Istanbul. You can add a new option via the Widget Editor. But to configure the option in a widget instance, you’ll have to do that manually via the Additional options, JSON format field on the widget instance record. Check out Step 11 for some insight into manually configuring instance options.

Step 3: Reference the option in the server script

We’ll need a way to tell our HTML template what menu to include at the top of the list. Keep in mind, we haven’t yet created the menu that we will be referencing; we’ve only created the option for selecting the menu. We’ll create the menu widget and instance in later steps. But for now, we’ll use the following objects and function in the server script to enable our List w Menu widget to include a menu.

  1. data is an object we use to share data across our widget’s scripts. listMenu is a namespace that we create. Therefore, listMenu is the object we’ll use to hold our menu widget instance.

  2. options references the widget options, and menu is the name we gave our new option. Therefore, menu will equal the sys_id of the menu that we select (after we create the menu in later steps).

  3. $sp.getWidgetFromInstance () is a Service Portal function that will get everything we need from a widget file in order to render it in our HTML template.

Now we combine these elements to form this line:

data.listMenu = $sp.getWidgetFromInstance(options.menu);

Add this line to the bottom of the server script, right before the closing })(). Like this:

Note: If you place this line at the beginning of the widget server script, your widget will break! So be sure you put it at the end of the function.

Click Save, but don’t go away from here yet. We’re going to update the HTML template now.

Step 4: Include the widget in the Simple List w Menu HTML template

We’ve already substantiated the selected menu as an object held by the namespace data.listMenu, so now we can include it in the HTML template with this snippet:

<div><sp-widget widget="data.listMenu"></sp-widget></div>

Add this to the top of the HTML template, like this:

(Please note that the first line below erroneously uses data.ticketsMenu. The correct object is data.listMenu, like mentioned above.)

Okay, we’re done with this widget. Now click Save.

Step 5: Create new widget for List Menu.

We did the necessary work for including a menu at the top of our new Simple List w Menu widget. Now let’s create the List Menu widget. Follow Step 2 to get back to the Widget Editor. But this time, instead of picking an existing widget, click the Create a new widget link.

A dialogue will pop-up and ask you to name the widget. Let’s call it List Menu. No need to create a test page, so don’t check that box. After naming it, click Submit.

Now you’re looking at a blank widget.

Let’s start with the HTML template.

<div class="row">
  <div class="col-xs-2 text-center" ng-repeat="item in data.items">
    <a class="block padder-v" href="{{::item.href}}">
      <span ng-if="item.glyph" class="fa-stack fa-2x {{::item.u_css_class}}">
        <i class="fa fa-circle text-{{::item.color}} fa-stack-1x"></i>
        <i class="fa fa-{{::item.glyph}} fa-stack fa-sm fa-inverse"></i>
      <h4 ng-class="{active: c.data.thisPage == item.sp_page}" class="m-t-sm">{{::item.label}}</h4>

To keep this post from being novel-length, I’m not going to explain all the HTML. But I will point a few important pieces.

  1. Line 2: ng-repeat=”item in data.items”

  2. This will cause lines 2 – 11 to be repeated for every item in our menu.

  3. The object item will be used to access our menu items’ field values

  4. Line 8: ng-class=”{active: c.data.thisPage == item.sp_page}”

  5. This will conditionally apply the class active to the h4 element if the condition is met.

  6. The condition is testing whether or not the current page is equal to the page linked to by the current menu item.

Paste the HTML into the Body HTML template field, replacing the existing placeholder stuff. It will look like this:

Step 6: Create CSS for new List Menu widget

Let’s add some styles so that our HTML template renders nicely. You may need to check the CSS – SCSS box at the top of the widget editor to expose the CSS field.

Here’s the CSS we will include.

.closed-tickets .fa-inverse {
  color: #666;
h4.active {
  border-bottom: 2px solid;
  padding-bottom: 3px;
h4 {
  font-size: 14px;
.fa-stack-1x {
  font-size: 1.5em;
  line-height: 1.5em;
.fa-sm {
  font-size: .75em;

Again, I’m not going to explain all this for the sake of brevity. Just paste it into the CSS field and trust me.

Step 7: List Menu widget client script

c.data.thisPage = $rootScope.page.sys_id;

Paste that into the client script field, right after var c = this.

Step 8: List Menu widget server script

Our server script is pretty simple too. It’s so easy because we get to use another Service Portal function: $sp.getMenuItems($sp.getValue(“sys_id”));

This function makes all our menu’s items available to us. We’ll assign these items to data.items. Remember how we used ng-repeat=”item in data.items” in the HTML template? Well, the following line is what makes that possible.

data.items = $sp.getMenuItems($sp.getValue("sys_id"));

Paste this into the Server script field, so that it looks like this:

Now we are done editing our new List Menu widget. Click Save.

Step 9: Create a menu

The next step is to create a menu. Go to the Service Portal Menus module and click New.

You’ll get a blank widget instance form. I’ve noticed that in some instances the Widget field is not present by default. If you don’t see it, use Personalize Form Layout to add the field. Otherwise, give the list a name, like Tickets List Menu. Then choose our List Menu widget in the Widget field.

Click Submit. This is as far as we are going with the menu for the moment. We need to create get some pages in order before we add the menu items.

Step 10: Create a page for each list

We need to ensure that we have a page for each list we want to display. Each page is going to have an instance of our new Simple List w Menu widget. For this example, we will use four pages:

  1. My Open Tickets

  2. My Closed Tickets

  3. Team’s Open Tickets

  4. Team’s Closed Tickets

I’m going to assume that you already know how to create a page. But, just to help you out, I’ll show you how I created mine. Find the Service Portal Pages module and click New. Give your page a Name (My Open Tickets) and ID (my_open_tickets), like I did here:

Click Submit. Now click on the Service Portal Configuration module:

Then click on the Designer box.

Find your new page by typing the name of it into the search input. Then click on your page.

Create a layout for your page. Use any kind you like, but for brevity, I’m only going to add one container, and a full-width column.

Step 11: Add the widget to the page

Find the Simple List w Menu widget and drag it onto the page. Your page should look like this now:

The next step is to configure the widget instance we just created by dragging the widget onto the page.

We’re going to exit Designer mode and go to the Widget Instance module. If you sort the list by Updated, then you’ll see your new, blank instance of Simple List w Menu at the top.

Open the new instance and give it a name, such as My Open Tickets. Configure the table and query.

Click on the Fields on List, Appearance tab, and make these selections:

On the Heading Glyph tab, select the icon you want to use to head this list.

Click Update to save the instance. Since we can’t access all the options we need from this view, we will go back into the Designer view to select a few more options. Click on the Service Portal Configuration module:

Then click on the Designer box.

Find your tickets page by typing the name of it into the search input. Then click on your page.

Now you’re in the page designer view. Hover over that My Open Tickets instance and click the pencil icon in the upper right of it.

This opens another view of the widget instance options. Scroll down the panel and look for Show even when empty. Check this box. Also look for Secondary fields and add the fields that I included. Finally, at the bottom you’ll see our Menu option. Select the new menu you created in step 9. Click Save.

Remember back in Step 2 I mentioned the great inconsistency in how widget options are configured? Well, you’ve experienced that here in this step, and in Istanbul you get hit with it even worse. So this is a good time to explain how to configure the options manually via JSON.

We’ll use the widget instance we’re working on this step as an example. Now that we’ve already configured our options, we can take a look at the options JSON, which gets generated automatically. Open the widget instance up from the Widget Instances list. Go back and refer to the beginnings of this step for finding your instance in the Widget Instances list. Go to the tab titled Heading Glyph, Color, Sizing. Check out the JSON code in the Additional options, JSON format field (this tab may be labeled Misc for some widgets).

Each option gets recorded as an object inside the JSON. Each object has 3 components:

  1. name: this is the name of the option

  2. value: this is the value given to the option

  3. displayValue: this is the display value given to the option.

If you go to the bottom of the JSON for our List Menu widget instance, you’ll see the entry for the Menu we’ve chosen to include:

 "menu": {
   "value": "4a6ad68bdb96f200e2a2fb16bf96190e",
   "displayValue": "CH Tickets Menu"

menu is the name we gave to the option we created in Step 2. The value is the sys_id of the Menu we created in Step 9. The displayValue is the name of that menu.

So, if you know a widget has an option but you can’t find where to configure that option, you can always come to the Additional Options, JSON Format field to manually configure the option via JSON values.

Check out the blog post Manually Configuring Widget Options for another example of manual option configuration, as well as other very useful insight into widget options.

Step 12: Repeat steps 10 and 11

Repeat steps 10 and 11 for every page/list combo you need. I ended up with these pages and list instances:

  1. My Open Tickets

  2. My Closed Tickets

  3. Team’s Open Tickets

  4. Team’s Closed Tickets

Just for reference, here are screenshots of each page and list widget instance I created:


Simple List w Menu widget instances:

With these pages and lists ready to go, we are prepared to create the menu items.

Step 13: Create menu items

Go to the Widget Instances module and look for the menu you created in Step 9. It is titled Tickets List Menu.

Open it up and you’ll see a related list for Menu Items.

Create a new item by clicking New. This first item will be for the My Open Tickets page, so name it accordingly. Order it appropriately with the Order field, and link it to the My Open Tickets page by selecting its ID in the Page field. Finally, select the Glyph Icon that you want to use. I’ll use the single user icon for My Tickets, and I plan on using the multiple users icon for Team’s Tickets.

Click Submit. This will drop you on the Menu form, but we’re going to hop right back into our new Menu Item. So, open the Item back up, then right-click in the header to get the Personalization Menu. Choose Configure, then choose Form Layout.

We want to add a field to this form so that we can assign each menu item a unique class name if we want. We’re going to use this to differentiate Open lists from Closed ones.

Use the Create New Field section to create the new field:

Click the Add button, then the Save button. Now you’ll see the CSS Class field right underneath the Glyph field.

We’re not going to add a class for My Open Tickets, but we will for the menu items that link to closed tickets lists.

If you created four pages in Step 10, then you are going to repeat Step 11 for each page you created. One difference, though. For your menu items linking to closed lists, include the class closed-tickets in the CSS Class field, like this:

This class was included in our CSS, so that the closed tickets link icons will be darker than the those for open tickets.

Here are the four menu items I created:

Step 14: Restrict Team’s Tickets links

Before we move on from the Menu Items, let’s restrict the Team’s links to only managers. We’ll use the Condition field on the Menu Item for that. Open the Menu Item for Team’s Open Tickets.

We’ll use this condition, which is just a simple GlideRecord to test if the current user is the manager of any other user.

var gr = new GlideRecord("sys_user"); gr.query("manager", gs.getUserID()); gr.hasNext()

Copy and paste this into the Condition field:

Click Update. Then do the same thing for the Team’s Closed Tickets menu item.

Step 15: Update header menu link

Whew, we’re almost done. Last thing is to update the link in our header menu so that users will go to the My Open Tickets page, rather than the out-of-box Requests page.

Go to the Service Portal Menus module. Find the header menu. If you’re using the out-of-box one, it’s called SP Header Menu. Open it up.

Check out the related list of Menu Items. Find the Item labeled Requests and open it. First off, you may want to rename this Menu Item to Tickets rather than Requests. Next, look in the Server Script at the second-to-last line.

This is where the View all requests link is inserted into the item’s dropdown menu. The out-of-box line is:

var link = {title: gs.getMessage('View all requests'), type: 'link', href: '?id=requests', items: []};

Let’s make these changes:

  1. Change ‘View all requests’ to ‘View all Tickets’.

  2. Change ‘?id=requests’ to ‘?id=my_open_tickets’

So now that second-to-last line reads:

var link = {title: gs.getMessage('View all Tickets'), type: 'link', href: '?id=my_open_tickets', items: []};

Click Update. Now go to the portal and check it out!

And when you click on the menu item, the selected item is underlined.

Just one final note… If your user doesn’t have any open tickets, then the header menu won’t show at all. That means you’ll need to put a link to the My Open Tickets page somewhere else in the portal. Maybe you create another link in the header menu, or maybe you put a link on the homepage. I’m not going to walk you through that. Because, if you made it this far, then I think you can handle a measly link on your own. Besides, it’s time for my happy hour.


In summary, here’s what we did:

  1. Cloned the Simple List widget: Simple List w Menu.

  2. Created an Option in our new widget for selecting a menu.

  3. Substantiated a server side object in our widget to hold the selected menu.

  4. Called our selected menu’s widget instance from the HTML template of the Simple List w Menu widget.

  5. Created a new widget: Menu for Lists.

  6. Created an HTML Template for our new widget, complete with HTML template, CSS, client script, and server script.

  7. Created a new menu from our new widget Menu for Lists.

  8. Created four pages, and four instances of the Simple List w Menu.

  9. Created four menu items to link to our pages.

  10. Restricted the Team’s Tickets menu items to managers only.

  11. Updated the header menu link to link to your new pages.

Congrats! You've completed the tutorial.

#serviceportalhowto #customizingserviceportal #tabstylemenu #simplelists #serviceportal

Read More: By Category

Read More: Recent Posts

Start Now

Security & Risk Solutions
IT Solutions
Business Solutions
HR Solutions
Customer Solutions
ServiceNow Services

© 2021 Cerna, LLC. All Rights Reserved. 1850 Diamond St. Suite 101, San Marcos, CA, 92078