Aggregate List Columns, Part X

“You’re always trying to get better. You’re always tinkering. You’re always learning new things.”
Kyle Korver

The previous installment in this series was supposed to be the last, as I wanted to get back to the Collaboration Store project that was just left hanging after the call for outside testing assistance. But then I decided to post the Update Set out on Share, and now that it is out there, it really disturbs my sense of The Way Things Ought To Be that you cannot click on an aggregate column and pull up a list of the records represented in that number. It doesn’t seem like it would take much to add that feature, and I am not sure why I never included it in the first place, but now that it is out there for all of the world to see, I feel as if I need to correct that omission. So here we go.

There are a couple of different ways to show a list of the records. One would be to simply link to a new page passing the sys_id of the row and the name of the table. Another would be some kind of modal pop-up containing a simple list or data table containing the records. To provide for all of these possibilities, we could add yet one more property to the configuration object for an aggregate column that we could call action. The value of the property could either be a URL query string for the new page, or the word broadcast, which would send out a broadcast message on click so that a companion widget could handle the modal dialog independently, outside the scope of the data table widget. We already have examples of this with the buttons and icons, and this would embrace that same strategy.

The easiest place to start with such a strategy would be with the Content Selector Configuration Editor, adding the new property to the table of aggregate column specifications as well as the pop-up aggregate column specification editor. Let’s start with the main widget and add the following to the list of column headings for that section:

<th style="text-align: center;">${Action}</th>

And then in the repeating rows of that table, let’s insert this line:

<td data-th="${Action}">{{agg.action}}</td>

On the server side, in the Save() function that rebuilds the script, let’s add these lines in the aggregate column specification section:

 script += "',\n                 action: '";
 script += thisAggregate.action;

Finally, on the client side, let’s add the following line to the new record section of the editAggregate() function:

shared.action = aggregate.action;

… and this line to the section that saves the edits in that same function:

aggregate.action = shared.action || '';

That takes care of the main widget, but we also need to add a new input field to the pop-up editor before this will actually work, so we need to add these lines to the HTML of the Aggregate Column Editor widget:

<snh-form-field
  snh-model="c.widget.options.shared.action"
  snh-name="action"/>

That updates the editor to now include a new aggregate column specification property called action. Of course, that doesn’t actually add any functionality to the Data Table widgets just yet, but at least now we can add a value to that property through the editor, which is a start. Now let’s take a look at the actual Data Table widgets and see what we need to do in order to leverage that new property.

Once again, the easiest place to start is with the HTML. Here is the current section of the HTML that deals with aggregate columns:

<td ng-repeat="obj in item.aggValue" class="text-right" ng-class="{selected: item.selected}" tabindex="0">
  {{obj.value}}
</td>

Let’s replace that with this:

<td ng-repeat="obj in item.aggValue" class="text-right" ng-class="{selected: item.selected}" tabindex="0">
  <a ng-if="obj.action" href="javascript:void(0)" ng-click="aggregateclick(obj.name, item)">{{obj.value}}</a>
  <span ng-if="!obj.action">{{obj.value}}</span>
</td>

Our new code now contains two mutually exclusive options, one an anchor and the other a span, which are rendered depending on whether or not there is a value in the new action property. The anchor tag does not directly launch the new page, which is only one of the possible responses to an action, but rather invokes a client-side script, passing the name of the aggregate column and the object containing the data for the row. Here is the code for this new client-side function:

$scope.aggregateclick = function(aggregate, item) {
	spNavStateManager.onRecordChange(c.data.table).then(function() {
		var parms = {};
		parms.table = c.data.table;
		parms.sys_id = item.sys_id;
		parms.record = item;
		for (var a in c.data.aggarray) {
			if (c.data.aggarray[a].name == aggregate) {
				parms.aggregate = c.data.aggarray[a];
			}
		}
		$scope.ignoreLocationChange = true;
		for (var x in c.data.list) {
			c.data.list[x].selected = false;
		}
		item.selected = true;
		$scope.$emit(eventNames.aggregateClick, parms);
		if (parms.aggregate.action && parms.aggregate.action != 'broadcast') {
			if (!parms.aggregate.action.startsWith('?')) {
				parms.aggregate.action = '?' + parms.aggregate.action;
			}
			if (parms.aggregate.action.indexOf('{{sys_id}}')) {
				parms.aggregate.action = parms.aggregate.action.replace('{{sys_id}}', parms.sys_id);
			}
			window.location.search = parms.aggregate.action;
		}
	}, function() {
		// do nothing in case of closing the modal by clicking on x
	});	
};

This function is basically a copy of the existing buttonclick function, adapted for use in the aggregate column section. In all cases where an action has been specified, the click information is broadcast to all of the other widgets on the page. Additionally, if the action is not the word broadcast, it is assumed to be a URL search string, and the window.location.search property is updated with the resolved value, invoking the new portal page.

The last thing that we need to do is to add a couple of new properties to the object returned by the getAggregateValue function on the server side. To support the new HTML, we just need to change this:

return {value: value};

… to this:

return {value: value, name: config.name, action: config.action};

That should do it for the changes. Now all we have to do is to build some test cases for both the new page and the pop-up dialog options to see if this all works. That sounds like a good project for our next installment.