How to Dynamically Set Reference Qualifier on a GlideModal Form?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
Hi everyone,
I’m currently working on a custom Service Portal implementation and I’ve hit a bit of a wall.
I’m trying to dynamically filter a reference field on a GlideModal form (not the regular form view) based on a value selected in a different field within the same modal. For example, once a user selects a "Department", I want the "Manager" reference field to only show users from that department.
I know how to use a reference qualifier on a regular form field using g_form.setReferenceQual(), but I’m not sure how (or if) this translates to a modal form in the Service Portal. Has anyone done something similar or have any suggestions?
Thanks in advance!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 weeks ago - last edited 2 weeks ago
Hi @sheikhans,
I tried to achieve your requirement through GlideModal API but it couldn't work then I saw the given posts it is not supported for service portals:
- GlideDialogWindow in Service Portal - Page 2 - ServiceNow Community
- GlideDialogWindow/GlideModal are not working on se… - ServiceNow Community
So you can achieve this through spModal client side API. Here's how I implemented your requirement in my instance.
You'll need 2 widgets, one parent and one child.
For Parent Widget:
Create a new widget (Service Portal > Widgets), Name your widget and add the ID, open it in widget editor for better coding experience. Paste the following code in:
HTML:
<button class="btn btn-primary" ng-click="c.openModal()">
Click here
</button>
<div>
<strong>Last selection:</strong>
<div ng-if="c.last">Dept: {{c.last.department.display}} ({{c.last.department.value}}), Manager: {{c.last.manager.display}}</div>
</div>
CLIENT SCRIPT:
function(spModal) {
var c = this;
c.last = null;
c.openModal = function() {
spModal.open({
widget: 'child_widget',
title: 'Select Department and Manager',
size: 'lg',
// optional: any inputs you want to pass to the modal
// widgetInput: { someParam: 'value' } OR 'widget_input' depending on instance; spModal will provide input to widget.server (use c.data in server)
widgetInput: {}
}).then(function(result){
// result is whatever modal widget passes back when closed with close(result)
if (result && result.department && result.manager) {
c.last = result;
// do whatever you want here: create record, call API, etc.
console.log('Selected:', result);
}
}, function(){
// dismissed
});
}
}
Note: on this line of above code " widget: 'child_widget'," I've used my child widget since its id is "child_widget".
For Child Widget:
Create a new widget (Service Portal > Widgets), Name your widget and add the ID (will be using this ID in Parent widget), open it in widget editor for better coding experience. Paste the following code in:
HTML:
<div class="modal-body">
<form name="deptManagerForm" novalidate>
<div class="form-group">
<label>Department</label>
<select class="form-control" ng-model="c.department" ng-change="c.onDepartmentChange()" required>
<option value="">-- Select Department --</option>
<option ng-repeat="d in c.departments" value="{{d.value}}">{{d.display}}</option>
</select>
</div>
<div class="form-group" ng-if="c.department !== null">
<label>Manager (users in selected department)</label>
<select class="form-control" ng-model="c.manager" required ng-disabled="!c.managers.length">
<option value="">-- Select Manager --</option>
<option ng-repeat="m in c.managers" value="{{m.value}}">{{m.display}}</option>
</select>
<div ng-if="!c.managers.length && c.department">No users found in this department.</div>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-default" ng-click="c.cancel()">Cancel</button>
<button class="btn btn-primary" ng-click="c.save()" ng-disabled="!c.department || !c.manager">Save</button>
</div>
CLIENT SCRIPT:
function($scope) {
var c = this;
c.department = null; // selected dept sys_id
c.departments = []; // list from server
c.managers = [];
c.manager = null; // selected manager sys_id
// runs once on load
c.$onInit = function() {
// serverData is available as c.data (the server script returned data)
c.departments = (c.data && c.data.departments) || [];
};
// when department changes, fetch managers for that dept
c.onDepartmentChange = function() {
c.managers = [];
c.manager = null;
if (!c.department) return;
// call server action to get managers for selected department
c.server.get({
action: 'getManagers',
dept: c.department
}).then(function(response) {
// response.data is the widget data for that server call
// In server script we filled data.managers, so it is returned
c.managers = (response && response.data && response.data.managers) || [];
}, function(err){
console.error('Failed to fetch managers', err);
});
};
c.save = function() {
// Build result object to return to parent via spModal promise
var result = {
department: {
value: c.department,
display: (function(){
var d = c.departments.find(function(it){ return it.value === c.department; });
return d ? d.display : '';
})()
},
manager: {
value: c.manager,
display: (function(){
var m = c.managers.find(function(it){ return it.value === c.manager; });
return m ? m.display : '';
})()
}
};
// close modal and pass result
spModal.close(result);
};
c.cancel = function() {
spModal.dismiss('cancel');
};
}
SERVER SCRIPT:
(function() {
data.departments = []; // will return dept list
data.action = input.action; // input.action used if needed
// if caller passes action, handle it (when client uses c.server.get)
// But the common pattern: client will call c.server.get({ action: 'getManagers', dept: deptSysId })
// Server receives params in `input` object.
// Provide departments on initial load
var deptGR = new GlideRecord('cmn_department');
deptGR.addActiveQuery();
deptGR.orderBy('name');
deptGR.query();
while (deptGR.next()) {
data.departments.push({
value: deptGR.getUniqueValue(),
display: deptGR.getDisplayValue()
});
}
// If client is requesting managers via server.get, use the following.
// Important: when c.server.get is called from the client with parameters,
// they appear in the `input` object (e.g. input.dept)
if (input && input.action && input.action == 'getManagers') {
data.managers = [];
var deptId = input.dept; // sys_id of department
if (deptId) {
var userGR = new GlideRecord('sys_user');
userGR.addQuery('active', true);
userGR.addQuery('department', deptId);
// Optionally narrow to users who are managers by title or a boolean field:
// userGR.addQuery('title', 'CONTAINS', 'Manager');
userGR.orderBy('name');
userGR.query();
while (userGR.next()) {
data.managers.push({
value: userGR.getUniqueValue(),
display: userGR.getDisplayValue(),
email: userGR.email.toString()
});
}
}
// When using c.server.get, the response is what the client sees in the promise result (data.managers here).
return;
}
// Default page load returns data.departments (and any other data in `data`).
})();
Now you can add the parent widget in your portal page.
OUTPUT:
now when you select department, manager field will be apeared and only show the users with respect to the department:
Hope this helps!
If my response helped, please mark it as the accepted solution and helpful so others can benefit as well.
