Service Portal Custom List that displays only unique values

Dazler
Mega Sage

Hi,

I have been task with creating a Role Base Access (RBAC) table. This custom table is used for our Identity Access Management.

I had an idea that would allow our business users to do attestation of the job codes and job profiles that they own in that RBAC table from within the Service portal.

I am a bit stuck with what I am trying to accomplish.  Below is a screenshot that I hope would make sense.

 

find_real_file.png

Part 1 of my question:

I created a left navigation that contains a list of records from that custom RBAC table where the Portal Identifier is not empty.  As you can see in the above image, the side navigation has duplicate values. The reason there are duplicate values is that each record may have the same job code, but different applications.  That is what makes up the RBAC table.  How can I get that list to display only unique values?

 

Part 2 of my question:

If I can get the left navigation working, I want it so that if they click on one of the options in the left navigation then in the table list on the right would display only the records that match the Portal Identifier of the option selected.

Any ideas on how to achieve this?

1 ACCEPTED SOLUTION

In the server script you should not do this:

var data = {
	'rbac': []
};

you are hiding the "real" object data and so your data will not arrive to client side.

You can load the data directly, there is no need to pump it through function simulateRow - I did so only to make the code "demonstrable" in Scripts - Background:

var rbacGR = new GlideRecord('u_role_base_access');

rbacGR.addQuery('u_active', true);

rbacGR.query();

while (rbacGR.next()) {
	data.rbac.push({
		'portalIdentifier': rbacGR.u_portal_identifier.toString(),
		'jobCode': rbacGR.u_job_code.toString(),
		'jobProfileName': rbacGR.u_job_profile_name.toString(),
		'costCenterID': rbacGR.getUniqueValue(),
		'costCenterName': rbacGR.getDisplayValue(),
	});
}

In my opinion

rbacGR.getUniqueValue()

is better looking than

rbacGR.sys_id.toString()

and the end result is the same - possibly a little faster.

As for the HTML template, since you are declaring "variable" rbac when writing

<... ng-repeat="rbac in data.jobs">

you have to use the same variable "down the road":

<h4 class="list-group-item-heading">
{{rbac.code}}

and

<p class="list-group-item-text">
Portal Identifier: {{rbac.portalID}}

But I must also say that

<... ng-repeat="rbac in data.jobs">

is misleading

<... ng-repeat="job in data.jobs">

better conveys "the message" of what is going on; of course the new "variable" job must be used in all places where it has been used:

<h4 class="list-group-item-heading">
{{job.code}}
<p class="list-group-item-text">
Portal Identifier: {{job.portalID}}

After those adjustments it should work.

Here's my test widget:

HTML template:

<div class="panel panel-default">
	<div class="panel-heading clearfix">
		<h3 class="panel-title pull-left">
			${RBAC Items}
		</h3>
	</div>
	<div class="list-group">
		<a class="list-group-item" ng-click="c.selectItem($index)" ng-repeat="job in c.data.jobs">
			<h class="list-group-item-heading">{{job.code}}</h>
			<p class="list-group-item-text">Portal Identifier: {{job.portalID}}</p>
		</a>
	</div>
</div>

Server script:

(function ($sp, options, data, input) {
	data.rbac = [];

	var rbacGR = new GlideRecord('u_role_base_access');

	rbacGR.addQuery('u_active', true);

	rbacGR.query();

	while (rbacGR.next()) {
		data.rbac.push(simulateRow(rbacGR.u_portal_identifier.toString() + ',' + rbacGR.u_job_code.toString() + ',' + rbacGR.u_job_profile_name.toString() + ',' + rbacGR.sys_id.toString()));
	}

	//data.rbac.push(simulateRow('1236599,ITSM Developer,,,1236599ITSM Developer'));
	//data.rbac.push(simulateRow('1236599,ITSM Developer,,,1236599ITSM Developer'));
	//data.rbac.push(simulateRow('dsaDASDAS8, FDASFSDA, , , dsaDASDAS8FDASFSDA'));
	//data.rbac.push(simulateRow('1236599,ITSM Developer,,,1236599ITSM Developer'));
	//data.rbac.push(simulateRow('TestCode, 236589 - 1, 56989, fadsfdsafds, TestCode236589 - 1'));

	data.jobs = data.rbac.reduce(groupJobs({}), []);

	console.log(JSON.stringify(data, null, '\t'));

	console.log(data.jobs);

	function groupJobs (jobsAdded) {
		return function (jobs, row) {
			if (typeof jobsAdded[row.portalIdentifier] == 'undefined') {
				jobsAdded[row.portalIdentifier] = true;

				jobs.push({
					'portalID': row.portalIdentifier,
					'code': row.jobCode,
					'profileName': row.jobProfileName
				});
			}

			return jobs;
		};
	}

	function addFieldToRow (fields) {
		return function (row, fieldName, index) {
			row[fieldName] = fields[index];
			return row;
		};
	}

	function simulateRow (data) {
		var fields = data.split(/\s*,\s*/g),
			row = {};

		return ['portalIdentifier', 'jobCode', 'jobProfileName', 'costCenterID', 'costCenterName'].reduce(addFieldToRow(fields), row)
	}
})($sp, options, data, input);

Note that using

console.log()

in the server script does not mean that the data logged actually is present client side. To test the data on client side one should use console.log() in the Client controller script. For sure while both look the same, there is absolutely no connection/relation between the server side console.log() and the client side control.log();

On the other hand, on client side - at least, there is an even easier way to inspect the data: right (inverse) click on the widget while holding down Ctrl than activating either the "Log to console: $scope.data" or the "Log to console: $scope" menu items:

find_real_file.png

View solution in original post

23 REPLIES 23

Nikita Kale
Giga Guru

Hi

Please refer to the below exercises as they are similar to your use case.

For part 1: 

Exercise: Populate the Notes List Widget | ServiceNow Developer

For part 2:

Exercise: Emit and Respond to Events | ServiceNow Developer

 

Please mark the response as helpful/correct, if it answers your question.

 

Thanks

Nikita

 

Thank you for replying back.

This exercise does help me, but one thing that I have come across that I am having trouble with is in the list.  I added unique to the ng-repeat (see below).  I did this so that it will show me the unique values.

ng-repeat="rbac in data.rbac | unique: 'u_portal_identifier'">

Adding the unique does display just the unique values, but once I add the information for the second part of the exercise, the identifier does not match what is being selected.  It's almost like it is still recognizing the order of the list all of all the records and not just the unique ones.

In the below image you can see that I selected the 3rd options, but it returned the portal identifier of the one above it.

find_real_file.png

 

Can you share your entire code?

Here is the first Widget HTML:

<div class="panel panel-default">
<div class="panel-heading clearfix">
<h3 class="panel-title pull-left">
${RBAC Items}
</h3>
</div>
<div class="list-group">
<a class="list-group-item" ng-click="c.selectItem($index)" ng-repeat="rbac in data.rbac | unique: 'u_portal_identifier">
<h4 class="list-group-item-heading">
{{rbac.u_job_code}}
</h4>
  <p class="list-group-item-text">
Portal Identifier: {{rbac.u_portal_identifier}}
</p>
<!--<p class="list-group-item-text">
{{rbac.u_job_profile_name}}
</p>
  <p class="list-group-item-text">
{{rbac.u_application}}
</p>-->
</a>
</div>
</div>

 

This is the Client script

function($rootScope,$scope) {
  /* widget controller */
  var c = this;
  c.selectItem = function(idx) {
    var id = c.data.rbac[idx].u_portal_identifier;
    console.log('Portal ID: ' + id);
    $rootScope.rbacID = id;
    $rootScope.$emit('selectRBAC', id);
  }
}

 

This is the server script

(function() {
//create an array to populate with notes

data.rbac = [];
var rbacGR = new GlideRecord('u_role_base_access');
rbacGR.addQuery('u_active', true);
rbacGR.query();
while (rbacGR.next()) {

var rbacObj = {};
//use service portal helper method to get some display values
$sp.getRecordDisplayValues(rbacObj, rbacGR, 'u_job_code,u_job_profile_name,u_application,u_portal_identifier,sys_id');

data.rbac.push(rbacObj);
}

})();

 

The image below on the left is what it looks like without the unique and the second image is with the unique.

find_real_file.pngfind_real_file.png

My end goal is when they select one of the options then it will pass the value of the portal identifier of that record to fill in as the filter for a table.  And in that table it will display all the records that have that same portal identifier.

Any help you can provide would be appreciated.