Welcome to Community Week 2025! Join us to learn, connect, and be recognized as we celebrate the spirit of Community and the power of AI. Get the details  

How to Dynamically Set Reference Qualifier on a GlideModal Form?

sheikhans
Tera Contributor

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!

1 REPLY 1

Community Alums
Not applicable

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:

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:

MIftikhar_0-1760977252812.pngMIftikhar_1-1760977275923.png

now when you select department, manager field will be apeared and only show the users with respect to the department:

MIftikhar_2-1760977359799.png

MIftikhar_3-1760977393312.png

Hope this helps!

 

If my response helped, please mark it as the accepted solution and helpful so others can benefit as well.