Passing Filters via URL Parameters for Dashboards with Separate Datasources
/**
* 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:
This includes the full filter information for the filters shown in the screenshot below:
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.