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.