Join the #BuildWithBuildAgent Challenge! Get recognized, earn exclusive swag, and inspire the ServiceNow Community with what you can build using Build Agent.  Join the Challenge.

Change Application scope of instance using Script

Fazeel Ahmed
Tera Contributor

Here is the scenario, I have a widget that contains a button. When I click the button, it will change the application scope from global to Human Resource Core. I don't know how to do it through scripting. I tried to do it through user preference table where I set the sys id of HR Core Application in apps.current_app record, the scope only changes when I restart the session.   

1 ACCEPTED SOLUTION

-O-
Kilo Patron

Here's how one could do it - obviously adapting code SN already wrote:


create new Angular Provider record:

  • Type: Factory
  • Name: applicationScope
  • Client script:

 

function applicationScope ($rootScope, $http) {
	var fetchedInitialData = false;
	var initialized = false;

	var applicationData = {
		current: {},
		currentId: '',
		list: [],
		showInHeader: false,
	};

	$rootScope.$on('concourse.application.changed', concourseApplicationChanged);

	return {
		'applicationData': applicationData,
		'getApplicationList': getApplicationList,
		'hasFetchedData': hasFetchedData,
		'initialize': initialize,
		'updateCurrent': updateCurrent,
	};

	function concourseApplicationChanged (evt, current) {
		applicationData.current = current;
		applicationData.currentId = current.sysId;
	}

	function getApplicationList () {
		fetchedInitialData = true;
		return $http
			.get('/api/now/ui/concoursepicker/application?cache=' + new Date().getTime())
			.then(getApplicationListResponse);
	}

	function getApplicationListResponse (response) {
		if (response && response.data && response.data.result) {
			applicationData.list = response.data.result.list;

			if (response.data.result.current && response.data.result.current != applicationData.currentId) {
				var apps = response.data.result.list;
				var curr = response.data.result.current;

				for (var i = 0; i < apps.length; i++) {
					if (curr == apps[i].sysId) {
						applicationData.current = apps[i];
						applicationData.currentId = apps[i].sysId;
						break;
					}
				}

				triggerChangeEvent();
			}
		}
	}

	function hasFetchedData () {
		return fetchedInitialData;
	}

	function initialize (current) {
		if (initialized)
			return;
		initialized = true;
		applicationData.list = [current];
		applicationData.current = current;
		applicationData.currentId = current.sysId;
	}

	function triggerChangeEvent () {
		$rootScope.$broadcast('concourse.application.changed', applicationData.current);
	}

	function triggerRefreshFrameEvent () {
		$rootScope.$broadcast('concourse.application.refresh', {});
	}

	function updateCurrent () {
		var apps = applicationData.list;
		var curr = applicationData.currentId;

		for (var i = 0; i < apps.length; i++) {
			if (curr == apps[i].sysId) {
				applicationData.current = apps[i];
				break;
			}
		}

		$http
			.put('/api/now/ui/concoursepicker/application', { 'app_id': applicationData.currentId })
			.then(updateCurrentResponse);
	}

	function updateCurrentResponse (response) {
		if (response && response.data && response.data.result && response.data.result.app_id) {
			triggerRefreshFrameEvent();
			triggerChangeEvent();
		}
	}
}

 


create new Angular Provider record:

  • Type: Directive
  • Name: applicationScopePicker
  • Client script:

 

function applicationScopePicker (applicationScope, snCustomEvent) {
	"use strict";

	return {

		'controller': function ($scope) {
			$scope.app = applicationScope.applicationData;
			$scope.refreshApplicationPicker = refreshApplicationPicker;
			$scope.updateCurrent = updateCurrent;

			snCustomEvent.observe('glide:ui_notification.application_change', applicationChanged);
			snCustomEvent.observe('sn:change_application', changeApplication);
			snCustomEvent.observe('sn:refresh_application_picker', refreshApplicationPicker);

			if ($scope.current) {
				applicationScope.initialize($scope.current);
			}

			function applicationChanged () {
				applicationScope.getApplicationList();
			}

			function changeApplication (appId) {
				applicationScope.getApplicationList().then(getApplicationListResponder);
			}

			function getApplicationListResponder (appId) {
				return function getApplicationListResponse () {
					applicationScope.applicationData.currentId = appId;
					$scope.updateCurrent();
				};
			}

			function refreshApplicationPicker () {
				applicationScope.getApplicationList();
			}

			function updateCurrent () {
				applicationScope.updateCurrent();
			}
		},

		'link': function (scope, element) {
			element.on('mouseover', mouseOver);

			function mouseOver () {
				if (!applicationScope.hasFetchedData())
					applicationScope.getApplicationList();
			}
		},

		'replace': false,

		'restrict': 'E',

		'scope': {
			'current': '=',
			'hintText': '=',
			'titleText': '=',
		},

		'template': getTemplate(),
	};

	function getTemplate () {
		return '\n' +
			'<div class="applicationScopePicker">\n' +
			'	<div class="input-group">\n' +
			'		<span class="input-group-addon">\n' +
			'			<a class="icon-application-generic" data-name="current-application-picker" data-title-text="{{ titleText }}" data-toggle="tooltip" href="/sys_scope.do?sys_id={{ app.current.sysId }}" target="gsft_main">\n' +
			'				<span class="sr-only">{{ hintText }}</span>\n' +
			'			</a>\n' +
			'		</span>\n' +
			'		<select class="form-control" ng-change="updateCurrent()" ng-model="app.currentId" ng-options="a.sysId as a.name for a in app.list" title="{{ titleText }}"></select>\n' +
			'	</div>\n' +
			'</div>\n' +
			'';
	}
}
  • Related list [Required Providers]: add the previously created Factory - applicationScope

 


create a new Widget record:

  • Body HTML template:

 

<div style="padding-top: 2.5em;">
	<application-scope-picker
		current="data.currentScope"
		hint-text="data.messages.hint"
		title-text="data.messages.title" />
</div>

 


  • Server script:

 

(function (data) {
	data.currentScope = getCurrentScope(gs.getCurrentApplicationId());
	data.messages = getMessages();

	function getCurrentApplicationDisplayValue (uniqueValue) {
		var gr = new GlideRecord('sys_scope');

		return gr.get(uniqueValue) ? gr.getDisplayValue() : uniqueValue;
	}

	function getCurrentScope (currentApplicationId) {
		return {
			'name': getCurrentApplicationDisplayValue(currentApplicationId),
			'sysId': currentApplicationId,
		};
	}

	function getMessages () {
		return {
			'hint': gs.getMessage('View current application under development'),
			'title': gs.getMessage('Show selected application'),
		};
	}
})(data);
  • Related list [Angular Providers]: add the previously created directive: applicationScopePicker

 


The result:

2023-02-14-1.png

2023-02-14-2.png

This literally does the exact same thing as the application picker in the main UI, so as soon as the select box is updated with a new option, the scope is changed for the current user.

View solution in original post

7 REPLIES 7

-O-
Kilo Patron

I forgot to mention one important thing, in related list [Angular Providers] for the widget, one must select applicationScopePicker.

Fazeel Ahmed
Tera Contributor

I did ended up finding an alternate way for my problem that uses glideupdateset api but after exploring a bit more, I found your solution to be much more compatible for my works. Thanks.

You're welcome!