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

Oh, yes! That's correct. Glad that it's fixed. If you want to play around with array/object, do have a look at the 'ArrayObjectUtil' script include, if you have it in your instance

Hi,

I was wondering if I could get your assistance again on the server script.

This is the script that I came up with to use the arrayUtil in my Server script.

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

//data.rbac = [];
	data.rbacInfo = [];
	
	var uniqueVal = [];
var arrayVal = [];
	var rbacGR = new GlideRecord('u_role_base_access');
	rbacGR.addQuery('u_active', true);
	rbacGR.query();
	
	while (rbacGR.next()) {
	
			arrayVal.push(rbacGR.u_portal_identifier.toString());

		
	}

	var arrUtil = new ArrayUtil();
uniqueVal = arrUtil.unique(arrayVal);

	console.log("length: " + uniqueVal.length); /* print lengths */

/* // print the unique elements
	for(var i = 0; i< uniqueVal.length; i++){
	console.log(uniqueVal[i]);
	}
	*/
	
data.rbac = uniqueVal;

})();

I got it to work, but the problem that is occurring is only 1 field is needed to determine if it is a duplicate and I am not able to pass any other values from the records to the html body.

This is what I am seeing using the new array that I added.

find_real_file.png

Any help you can provide would be appreciated.  This challenge has been a great learning lesson for me to improve.

Oh yes sure!

2 things:

1. Can you do a console log in your previous server script to get what is populated in

var rbacObj = {};
data.rbac.push(rbacObj);

want to see how your data is formatted in the rbacObj & data.rbac

2. Check if you are having a script include named 'ArrayObjectUtil' in your instance

 

 

Thank you again for your help.

Here is the console log for rbacObj

find_real_file.png

This is the console log for data.rbac

find_real_file.png

I checked the script include and there is only the ArrayUtil script include.

The bound data should not be the same for the left side list and the main list. The left side list should show the records from the table to which column Job Code points to. If that is not the case and Job Code is neither a reference nor a choice, you should still bind the two lists to separate data/$scope properties. You should have a property that holds all records as displayed in the list and another property that holds the unique values from the Job Code column. In other words either on server side, after loading the table, you should extract the unique values into a separate property of data, or you could do it client side too. In other words, besides data.rbac you should have a data.jobCodes (or whatever name you want for it) where the latter has been build based on the former. And, of course, use data.jobCodes to build the side-bar.