Skip to content
Will Leingang edited this page May 5, 2017 · 15 revisions

Using data & events across widgets

So far the incident workspace page only has one widget but this is the perfect time to think about sharing data and interactions between widgets. As soon as you add another widget to the page you will probably ask yourself one of these questions:

  • How do I keep my widgets in sync when changing records or filters?
  • How can my widgets share context?
  • How do I maintain and persist state?

You can easily drop widgets onto a page and have them all do their own thing. A portal homepage is a good example of that. In this case, all the widgets on the page are ultimately going to react to a single table&filter or a selected record.

To share data between widgets you can use a custom javascript dependency or an angular provider called a service. In this lab you will create a toolbar widget, data service, and then use that data service across your widgets.

Add a toolbar widget

  1. Using STUDIO click Create Application File.

  2. Select Service Portal -> Widget. Click Create.

  3. On the widget editor click Create a new widget. Use the following values:

    Widget name: Workspace header
    Widget ID: workspace_header

    Click Submit.

  4. Edit the option schema with the following values:
    Label: Title
    Name: title
    Type: string

    Label: Table
    Name: table
    Default Value: incident
    Type: string

    Label: Count expressions
    Name: count_expressions
    Hint: Label,query;Label,query;...
    Default Value: Unassigned Incidents,assigned_toISEMPTY;New Incidents,state=1
    Type: string

  5. Click Save

  6. Enter this for the HTML Template:

<div class="workspace-header clearfix">
    <div class="pull-right">
      <div class="count" ng-repeat="counter in c.counters">
        {{counter.count}}
        <span class="count-label">{{::counter.label}}</span>
      </div>
    </div>
    <div class="h2">
      {{::c.options.title}}
    </div>
</div>
  1. Enter this for CSS:
$workspace-header-background: $gray-light !default;
$workspace-header-padding: 5px !default;

.workspace-header {
	background-color: $workspace-header-background;
  margin: 0px;
  margin-bottom: 5px;
  padding: $workspace-header-padding;
  
  @include border-top-radius(3px);
  @include border-bottom-radius(3px);
  
  
  .count {
  	font-size: 3rem;
    text-align: center;
    display: inline-block;
    margin-right: 20px;
    
    .count-label {
      font-size: 1.8rem;
    	display: block;
    }
  }
}
  1. Enter this for Client Script:
function($http) {
  /* widget controller */
  var c = this;
	
	var countExpressions = c.options.count_expressions.split(";");
	c.counters = [];
	for(var i=0;i<=countExpressions.length-1;i++) {
		var parts = countExpressions[i].split(",");
		var counter = makeCounter(parts[0], parts[1]);
		runCounter(counter);
		c.counters.push(counter);
	}
	
	function runCounter(counter) {
		var url = "/api/now/stats/"+ c.options.table +"?sysparm_query="+ counter.filter +"&sysparm_count=true";
		$http.get(url).then(function(response) {
			counter.count = response.data.result.stats.count;
		});
	}
	
	function makeCounter(label, filter) {
		return {label: label, filter: filter, count: 0};
	}
}
  1. Save the widget.

In summary, this widget can show a heading and counts.

When you configure the instance options for this widget you can provide count expressions in the following format:

Label,filter

Label: A string to show below the count
Filter: An encoded query string

Separate multiple count expressions with a semi-colon.

For each count expression, it will display the label and call the stats rest api to get a count of records matching the filter. This isn’t very intuitive to configure or efficient to execute but it can be easily modified later to use a shared data source. The takeaway here is that we aren’t using Server Script at all. You will soon see how to provide data to your widgets via rest endpoints.

Add the toolbar to the Incident Workspace page

  1. Open the Service Portal designer in a new tab by going to [instacnce]/$spd.do
  2. Open the Incident Workspace page.
  3. Drag a new 12 column container to the very top of the page above the existing [3 | 9] layout like this:
  4. Drag the Workspace header widget into that new container:

Create an angular provider

Now that you have 2 widgets on a page using nearly the same data source, it’s time to create a data service that both widgets can use. It’s important to remember that since widgets are just angular directives, all the widgets on a page will use the same instance of an angular service. It’s just an instance of an object that is accessible by any widget that chooses to use it.

Angular Providers can’t be created from Studio so you need to create it using the platform.

  1. Open your instance in a new tab:
    [instance]/
  2. Type Service Portal in the navigator and select Angular Providers
  3. At the top of the list click New
  4. Add a new Widget Angular Provider with the following values:
    Type: Service
    Name: workspaceData
    Client Script:
function(amb) {
	var watcher;
	var dataUpdatedHandlers = [];
	function init(table, filter) {
		if (watcher) {
			watcher.unsubscribe();
		}

		if (table && filter) {
			var watcherChannel = amb.getChannelRW(table, filter);
			amb.connect();
			watcher = watcherChannel.subscribe(function(message) {
				if (!message.data) {
					return;
				}
				dataUpdatedHandlers.forEach(function(fn) { fn.call(fn); });
			});
		}
	}
	return {
		onDataUpdated: function(callbackFn) {
			dataUpdatedHandlers.push(callbackFn);
		},
		initRecordWatcher: function(table, filter) {
			init(table, filter);
		}
	};
}
Clone this wiki locally