Adding filters to M2M type related list

Subham Mundra1
Tera Expert

Hello there,

 

We have a M2M related list on our Change request table. This related list is also referred from change request table. We now have to restrict this related list to only show those change requests which are closed and has the similar change owner group as the parent change request.

 

Also, we want users to select the change requests in the related list they want to add, we do not want it auto-populated on-load of the form.

 

User should have a edit button where they will only have visibility of the previously closed change requests with similar change Owner groups and can add whichever change request is relevant to them. 

 

Thank You!

2 REPLIES 2

Siddhesh Jadhav
Kilo Sage

Hi @Subham Mundra1 ,

 

you can add default filters to the related list see attached screenshots.

 

SiddheshJadhav_0-1769088346668.pngSiddheshJadhav_1-1769088374128.png

Thanks, and regards

Siddhesh Jadhav

 

ChallaR
Giga Guru

Hi @Subham Mundra1 ,

please find the below steps -

Step‑by‑step

1) Create the self‑referencing M2M

  1. Go to System Definition → Many to Many Definitions (sys_m2m.list).
  2. New:
    • From table: Change Request [change_request]
    • To table: Change Request [change_request]
    • (Let the platform generate the M2M table, e.g., m2m_change_request_change_request.)
  3. Add the related list to the Change form (Configure → Related lists). You should see the new related list with an Edit… button. [servicenowguru.com]

Nothing is auto‑added—the list stays empty until a user selects records via Edit… (standard M2M behavior). [servicenowguru.com]

2) Show only “Edit”, hide “New”

If you only want users to use the slushbucket:

  • Open the List Control for this related list and omit the “New” button (and others) so users must use Edit…. You can do this via list control conditions/scripts. In related list controls, the global variable parent is available if you need it. [servicenow.com]

3) Filter the Edit… slushbucket to Closed + same Owner group

There are two common ways to influence the slushbucket:

  1. Clone and tailor the “Edit…” UI Action (recommended for strict filtering):
  1. Locate the Edit… UI Action that fires for your M2M related list.
    • Best practice: Insert & Stay to create a copy scoped to your specific M2M table so you don’t affect other lists.
  2. In the Script, build the slushbucket URL and add:
    • sysparm_query=<your encoded query>

jvar_no_filter=true (optional) to prevent users from removing the filter in the picker

// This is the UI Action (server-side) script for the related list "Edit..." on your M2M table
// Assumes your change owner group field is 'u_change_owner_group' (adjust as needed).
// Also replace CLOSED_VALUE with the numeric/state value(s) representing "Closed" in your instance.

(function() {
  // Build the base slushbucket URL (pattern per docs)
  var url = "slushbucket.do?sysparm_form=list" +
            "&sysparm_list=" + current.name +
            "&sysparm_view=" + current.view.name +
            "&sysparm_collection=" + current.parent +
            "&sysparm_collection_relationship=" + current.relationship;

  // Build the dynamic encoded query:
  // - Closed state(s): e.g., state=3 OR stateIN<your closed states>
  // - Same owner group as parent: u_change_owner_group=<parent value>
  // - Exclude the parent change itself: sys_id!=<parent sys_id>
  var parentGR = new GlideRecord('change_request');
  parentGR.get(current.collection); // the parent record sys_id

  var closedQuery = "state=3"; // <-- replace 3 with your actual 'Closed' value; use stateIN if needed
  var ownerQuery  = "u_change_owner_group=" + parentGR.getValue('u_change_owner_group');
  var notSelf     = "sys_id!=" + parentGR.getUniqueValue();
  var encoded     = [closedQuery, ownerQuery, notSelf].join("^");

  url += "&sysparm_query=" + encodeURIComponent(encoded);

  // Optional: lock the filter so users can't broaden it
  url += "&jvar_no_filter=true";

  action.setRedirectURL(url);
})();
``

 

  • Passing sysparm_query and jvar_no_filter=true to the slushbucket is a standard technique used to constrain the Edit… dialog and keep the filter read‑only. [servicenow.com], [servicenow.com]
  • The pattern for launching the slushbucket from a list/related list UI action is documented (constructing slushbucket.do?... with collection and relationship). [servicenow.com]

 

NOTE: Determine your Closed state value(s) on Change Request (they vary by implementation). Replace state=3 with the correct value or use stateIN<comma-separated closed values>.
(You can inspect Choices on the state field to confirm.)

B. “Edit Default Filter” (lightweight, but user‑editable):
You can set a default filter for the slushbucket via UI Controls, but users can often clear/modify it. For strict enforcement, use option A above. [servicenow.com]


4) Enforce the rule server‑side (defense in depth)

Add a Before Insert Business Rule on your M2M table to block linking if the target Change isn’t Closed or the owner group differs. This guarantees compliance even through APIs/imports

 

This pattern ensures the integrity of your M2M data no matter how records are inserted. (General M2M and slushbucket usage documented; BR here is your custom enforcement.) [servicenowguru.com], [servicenowguru.com]
// Table: m2m_change_request_change_request (replace with your actual M2M table name)
// When: before insert
(function executeRule(current, previous /*null when async*/) {

  // 'to' side field name is the reference to change_request created by the M2M definition
  // Check your M2M table dictionary to confirm the reference field names.
  var TARGET_FIELD = 'to_change_request'; // <-- replace with actual field name
  var OWNER_FIELD  = 'u_change_owner_group'; // <-- your owner group field on change_request
  var CLOSED_VALUE = 3; // <-- your actual 'Closed' choice value

  var parentGR = new GlideRecord('change_request');
  if (!parentGR.get(current.from_change_request)) {
    current.setAbortAction(true);
    gs.addErrorMessage('Parent Change not found.');
    return;
  }

  var targetGR = new GlideRecord('change_request');
  if (!targetGR.get(current[TARGET_FIELD])) {
    current.setAbortAction(true);
    gs.addErrorMessage('Target Change not found.');
    return;
  }

  var sameGroup = parentGR.getValue(OWNER_FIELD) == targetGR.getValue(OWNER_FIELD);
  var isClosed  = String(targetGR.getValue('state')) == String(CLOSED_VALUE);

  if (!isClosed || !sameGroup) {
    current.setAbortAction(true);
    gs.addErrorMessage('Only Closed Changes with the same Owner group can be related.');
  }
})();
``