cancel
Showing results for 
Search instead for 
Did you mean: 
JeremyFriedel
Sisense Team Member

Passing Filters via URL Parameters for Dashboards with Separate Datasources

 

Sisense includes a native included feature and format for passing URL filters via URL parameters, as documented here. By default, this functionality copies filters in full, including the datasource parameter of the filter, and includes every filter automatically. It results in very long URL's, and includes many parameters that are not always required, as the full filter object is included. Previous Knowledge Base articles articles have discussed how similar behavior can be recreated customized via scripting for more flexible usage and shorter URL's. However, those approaches applied only to member-type filters, excluding other filter types and multi-level dependent filters.

The code shared below demonstrates a more flexible filter modification via URL modification approach. It includes both creating URL parameters and reading URL parameters for filter modification, whether this code is in a script or plugin. This method applies to all filter types and can be used to transfer filters between dashboards using different datasources. This code works in both dashboard and widget scripts as well as Sisense Add-ons/Plugins. If your datasources use different dimension names, this code can be adopted to map and match the aligned dimensions.

This code uses the full filter JAQL parameter object, if any filter parameters outside the JAQL parameter are needed they can be added via slight modification of this code.

This code below includes code for:

  • Reading URL Parameters:
    The decodeAllFilterParams function updates dashboard filters by reading URL parameters, using keys such as FilterParam and FilterMLParam concatenated with a sanitized version of the filter’s dimension. This is then used to modify the dashboard's filters.

  • Writing URL Parameters:
    The redirectToDashboard function encodes all current filters back into the URL when a redirect occurs. This ensures that filters persist across dashboard changes even when the underlying datasource differs.

  • Managing the Redirect Flag:
    Functions like redirectFlagPresent and removeRedirectFlag check for and remove a special flag in the URL (i.e., "justRedirected"). This flag prevents infinite redirect loops. These functions are only required if your code includes redirect functionality.

 

 
/**
 * Returns a URL-encoded version of the given string.
 * (This removes special characters and spaces that do not belong in a URL.)
 */
function sanitizeDim(str) {
  return encodeURIComponent(str || "");
}

/**
 * Reads URL parameters to update existing dashboard filters.
 * - For single-level filters, the key is "FilterParam" + sanitized(filter.jaql.dim).
 * - For multi-level filters, the key is "FilterMLParam" + sanitized(level.dim).
 * Updates only filters whose dimension already exists on the dashboard.
 */
function decodeAllFilterParams() {
  if (!dashboard.filters || !dashboard.filters.$$items) return;
  const url = new URL(window.location.href);
  dashboard.filters.$$items.forEach(filter => {
    if (filter.jaql) {
      const key = "FilterParam" + sanitizeDim(filter.jaql.dim);
      if (url.searchParams.has(key)) {
        const value = url.searchParams.get(key);
        try {
          const parsed = JSON.parse(value);
          console.log("Updated filter.jaql for dimension", filter.jaql.dim, "with", parsed);
          filter.jaql = parsed;
        } catch (err) {
          console.log("Error parsing parameter for key", key, err);
        }
      }
    }
    if (filter.levels && Array.isArray(filter.levels)) {
      filter.levels.forEach(level => {
        const key = "FilterMLParam" + sanitizeDim(level.dim);
        if (url.searchParams.has(key)) {
          const value = url.searchParams.get(key);
          try {
            const parsed = JSON.parse(value);
            console.log("Updated multi-level filter for dimension", level.dim, "with", parsed);
            level.filter = parsed;
          } catch (err) {
            console.log("Error parsing multi-level parameter for key", key, err);
          }
        }
      });
    }
  });
}

/**
 * Checks if the redirect flag ("justRedirected") is present in the URL.
 */
function redirectFlagPresent() {
  const currentUrl = new URL(window.location.href);
  return currentUrl.searchParams.get("justRedirected") === "true";
}

/**
 * Redirects to a different dashboard by updating the URL.
 * It re-encodes all current filters into the URL so that the receiving dashboard can read them.
 * - Single-level filters: Key = "FilterParam" + sanitized(filter.jaql.dim)
 * - Multi-level filters: Key = "FilterMLParam" + sanitized(level.dim)
 * Additionally, a redirect flag ("justRedirected") is added to prevent infinite loops.
 */
function redirectToDashboard(newDashboardId) {
  if (redirectFlagPresent()) {
    console.log("Redirect flag is present; skipping additional redirect.");
    return;
  }
  const currentDashboardId = dashboard.oid;
  if (!currentDashboardId) {
    console.log("No current dashboard ID; cannot redirect.");
    return;
  }
  const currentUrl = window.location.href;
  if (!currentUrl.includes(currentDashboardId)) {
    console.log("Current URL does not contain the dashboard ID; skipping redirect.");
    return;
  }
  const replacedUrl = currentUrl.replace(currentDashboardId, newDashboardId);
  const urlObj = new URL(replacedUrl);

  // Re-add all current filters.
  if (dashboard.filters && dashboard.filters.$$items) {
    dashboard.filters.$$items.forEach(filter => {
      if (filter.jaql) {
        const key = "FilterParam" + sanitizeDim(filter.jaql.dim);
        urlObj.searchParams.set(key, JSON.stringify(filter.jaql));
      }
      if (filter.levels && Array.isArray(filter.levels)) {
        filter.levels.forEach(level => {
          const key = "FilterMLParam" + sanitizeDim(level.dim);
          if (level.filter) {
            urlObj.searchParams.set(key, JSON.stringify(level.filter));
          } else {
            urlObj.searchParams.set(key, JSON.stringify(level));
          }
        });
      }
    });
  }

  // Add the redirect flag.
  urlObj.searchParams.set("justRedirected", "true");

  const finalUrl = urlObj.toString();
  console.log("Redirecting to URL:", finalUrl);
  window.location.href = finalUrl;
}

/**
 * Removes the redirect flag ("justRedirected") from the URL using history.replaceState,
 * allowing future filter changes to trigger a redirect.
 */
function removeRedirectFlag() {
  const url = new URL(window.location.href);
  if (url.searchParams.has("justRedirected")) {
    url.searchParams.delete("justRedirected");
    window.history.replaceState(null, "", url.toString());
    console.log("Removed redirect flag from URL.");
  }
}



An example of a URL including multiple filters encoded in this matter:

?FilterParam%255BMainTable.email%255D=%7B"table":"MainTable","column":"email","dim":"%5BMainTable.email%5D","datatype":"text","merged":true,"datasource":%7B"address":"LocalHost","title":"Old%20Data%20Script%20Test","id":"localhost_aOldIAAaDataIAAaScriptIAAaTest","database":"aOldIAAaDataIAAaScriptIAAaTest","fullname":"localhost%2FOld%20Data%20Script%20Test","live":false%7D,"firstday":"mon","locale":"en-us","title":"email","collapsed":true,"isDashboardFilter":true,"filter":%7B"explicit":true,"multiSelection":true,"members":%5B"aabramofaz@amazon.com"%5D%7D%7D&FilterMLParam%255BMainTable.first_name%255D=%7B"explicit":true,"multiSelection":true,"members":%5B"Abeu","Abigale"%5D%7D&FilterMLParam%255BMainTable.gender%255D=%7B"explicit":false,"multiSelection":true,"all":true%7D&FilterParam%255BMainTable.Revenue%2520Date%2520(Calendar)%255D=%7B"table":"MainTable","column":"Revenue%20Date","dim":"%5BMainTable.Revenue%20Date%20(Calendar)%5D","datatype":"datetime","merged":true,"datasource":%7B"address":"LocalHost","title":"New%20Data%20Script%20Test","id":"localhost_aNewIAAaDataIAAaScriptIAAaTest","database":"aNewIAAaDataIAAaScriptIAAaTest","fullname":"localhost%2FNew%20Data%20Script%20Test","live":false%7D,"firstday":"mon","locale":"en-us","level":"days","title":"Days%20in%20Revenue%20Date","collapsed":true,"isDashboardFilter":true,"filter":%7B"explicit":true,"multiSelection":true,"members":%5B"2024-12-11T00:00:00","2025-03-12T00:00:00"%5D%7D%7D&justRedirected=true


This includes the full filter information for the filters shown in the screenshot below:
Screenshot 2025-03-24 at 9.26.59 PM.png

 




This code above shows a flexible approach to passing and modifying filters via URL parameters in Sisense. By leveraging custom scripting, you can transfer filters, regardless of their type, between dashboards even if they are backed by separate datasources. This level of control is particularly useful for scenarios where dashboards need to transfer filters seamlessly, preserving filter context during navigation.

In practice, you can adapt this code to your specific needs, including mapping dimensions when datasources differ or integrating it within dashboard or widget scripts and plugins. This can be used to enhance user experience by maintaining filter continuity, but it also can allow the implementation of advanced routing logic and customize dashboard interactions. 

Whether you are transferring filters between a legacy dashboard and a new dashboard or building dynamic filter-preserving navigation for a custom applications, this solution provides a good starting code for many types of customizations.


Version history
Last update:
‎03-24-2025 06:34 PM
Updated by:
Contributors