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

Hi 

Can you check if this works

Updated HTML Code:

<div class="panel panel-default">
<div class="panel-heading clearfix">
<h3 class="panel-title pull-left">
${RBAC Items}
</h3>
  <div class="panel-body"> 
    <input placeholder="Filter" class="form-control" ng-model="rbacFilter" /> 
  </div>
</div>
<div class="list-group">
<a class="list-group-item" ng-click="c.selectItem($index)" ng-repeat="rbac in data.rbac | filter: rbacFilter">
<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>

Also, if this doesn't work. Revert back to your original HTML & modify the client side as below:

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

Let me know if it helps

Dazler
Mega Sage

Hi, 

I am not sure if I am explaining correctly.  I don't want them to add the filter.  I want the list to automatically hide the duplicates upon loading the page and when they select the option that they want to see more about then that portal identifier passes to a 2nd widget that is a table that takes that portal identifier and uses it in its filters to display the records.

Yes, got that. Did modifying client script help? Tried to reset the ID.

Currently, the issue is with the 1st widget emitting the wrong/previous ID right?

It did not work.  I am thinking that I need to adjust my server script to remove the duplicates in the array before it gets to the html body.

I just got to figure that out now.  Thank you for your help.