Search results

Optimize Dashboard Listing with JavaScript Embedding

Want to embed multiple dashboards without generating a new token every time? This guide shows you how to:

  • Fetch dashboards dynamically
  • Generate a reusable embed token
  • Render dashboards efficiently

Why Optimize?

Do you really need to call the server for every dashboard render?

Nope! You can fetch the dashboard list once, reuse the token, and render dashboards on demand.

Step 1: Fetch Dashboard List from Server

Use this AJAX call to get the list of dashboards:

function Init() {
  const http = new XMLHttpRequest();
  http.open("GET", getDashboardsUrl, true);
  http.responseType = 'json';
  http.setRequestHeader("Content-type", "application/json");
  http.onreadystatechange = function () {
    if (http.readyState === 4 && http.status === 200) {
      ListDashboards(http.response);
    }
  };
  http.send();
}

Step 2: Backend API to Serve Dashboards

To retrieve the list of dashboards from the Bold BI server, we are using v5.0 Rest API:

[HttpGet]
[Route("GetDashboards")]
public string GetDashboards() {
  var token = GetToken();
  using (var client = new HttpClient()) {
    client.BaseAddress = new Uri(GlobalAppSettings.EmbedDetails.ServerUrl);
    client.DefaultRequestHeaders.Add("Authorization", $"{token.TokenType} {token.AccessToken}");
    var result = client.GetAsync($"/api/{GlobalAppSettings.EmbedDetails.SiteIdentifier}/v5.0/dashboards").Result;
    return JObject.Parse(result.Content.ReadAsStringAsync().Result)["Data"].ToString();
  }
}
public Token GetToken()
{
	using (var client = new HttpClient())
	{
		client.BaseAddress = new Uri(GlobalAppSettings.EmbedDetails.ServerUrl);
		client.DefaultRequestHeaders.Accept.Clear();

		var content = new FormUrlEncodedContent(new[]
		{
			new KeyValuePair<string, string>("grant_type", "embed_secret"),
			new KeyValuePair<string, string>("Username", GlobalAppSettings.EmbedDetails.UserEmail),
			new KeyValuePair<string, string>("embed_secret", GlobalAppSettings.EmbedDetails.EmbedSecret)
		});
		var result = client.PostAsync(GlobalAppSettings.EmbedDetails.ServerUrl + "/api/" + GlobalAppSettings.EmbedDetails.SiteIdentifier + "/token", content).Result;
		string resultContent = result.Content.ReadAsStringAsync().Result;
		var response = JsonConvert.DeserializeObject<Token>(resultContent);
		return response;
	}
}

Step 3: Build the Sidebar Dynamically

Render the dashboard list in the sidebar and load the first dashboard:

function ListDashboards(data) {
  if (data?.length) {
    getDashboardAccessToken(data[0].Id, 'view', '100000');
    data.forEach(d => {
      const div = document.createElement("div");
      div.innerHTML = d.Name;
      div.className = "dashboard-item";
      div.setAttribute("onclick", `renderDashboard('${d.Id}')`);
      document.getElementById("panel").appendChild(div);
    });
  }
}

Step 4:Construct the Embed Query String

Create a query string that includes the dashboard ID, access mode, expiration time, and a unique nonce. This ensures each request is secure and time-bound.

function getDashboardAccessToken(dashboardId, mode, expirationTime) {
    const queryString = generateEmbedQueryString(dashboardId, mode, expirationTime);
    const payload = buildRequestPayload(queryString);
    sendAuthorizationRequest(payload, dashboardId);
    }
    function generateEmbedQueryString(dashboardId, mode, expirationTime) {
    return [
        `embed_nonce=${generateUUID()}`,
        `embed_dashboard_id=${dashboardId}`,
        `embed_mode=${mode}`,
        `embed_timestamp=${Math.floor(Date.now() / 1000)}`,
        `embed_expirationtime=${expirationTime}`
    ].join('&');
    }

Step 5: Generate a UUID

A UUID is used as a nonce to uniquely identify each request.

    function generateUUID() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            const r = Math.random() * 16 | 0;
            const v = c === 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }

Step 6: Build the Request Payload:

The payload includes the query string and the API URL of the Bold BI server.

    function buildRequestPayload(queryString) {
      return {
        embedQuerString: queryString,
        dashboardServerApiUrl: `${rootUrl}/api/${siteIdentifier}`
      };
    }

Step 7: The Authorization Request

Send the payload to your backend authorization server, which will generate the token and return it to the client.

function sendAuthorizationRequest(payload, dashboardId) {
  $.ajax({
    url: authorizationServerUrl,
    type: "POST",
    async: true,
    data: JSON.stringify(payload),
    contentType: "application/json",
    success: function (response) {
      try {
         const parsed = typeof response === 'string' ? JSON.parse(response) : response;
        let accessToken, expires;
        console.log(parsed);

        if (Array.isArray(parsed?.Data)) { // For multiTab dashboard
            for (const item of parsed.Data) {
                if (item.access_token) {
                    accessToken = item.access_token;
                    isMultiTab = true;
                    expires = item[".expires"];
                    break;
                }
            }
        } 
        else{
            accessToken = parsed.Data.access_token;
            expires = parsed.Data[".expires"];
        }

        if (accessToken) {
            token = accessToken;
            tokenExpiry = new Date(expires);
            renderDashboard(dashboardId);
        } else {
            alert("Access token not found in response.");
        }
    } catch (error) {
        alert("Error parsing response:", error);
    }
    },
  });
}

Step 8: Backend Token Generation Logic:

On the server side, the embed query is signed and sent to the Bold BI server to retrieve the token.

    [HttpPost]
    [Route("AuthorizationServer")]
    public string AuthorizationServer([FromBody] object embedQuerString)
    {
        var embedClass = JsonConvert.DeserializeObject<EmbedClass>(embedQuerString.ToString());
        var embedQuery = embedClass.embedQuerString;

        embedQuery += "&embed_user_email=" + GlobalAppSettings.EmbedDetails.UserEmail;

        double timeStamp = (int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
        embedQuery += "&embed_server_timestamp=" + timeStamp;

        var embedDetailsUrl = "/embed/authorize?" + embedQuery + "&embed_signature=" + GetSignatureUrl(embedQuery);

        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri(embedClass.dashboardServerApiUrl);
            client.DefaultRequestHeaders.Accept.Clear();

            var result = client.GetAsync(embedClass.dashboardServerApiUrl + embedDetailsUrl).Result;
            return result.Content.ReadAsStringAsync().Result;
        }
    }

Step 9: Render The Dashboard

The renderDashboard() method uses the generated token via the embedToken property. It also checks the token’s expiration time and automatically fetches a new token if the current one has expired.

function renderDashboard(dashboardId) {
 if (!token || isTokenExpired()) {
   console.log("Token expired or missing. Fetching new token...");
   getDashboardAccessToken(dashboardId, 'view', '100000');
 }
   this.dashboard = BoldBI.create({
       serverUrl: rootUrl + "/" + siteIdentifier,
       dashboardId: dashboardId,
       embedContainerId: "dashboard",
       embedToken: token,
       isMultiTabDashboard: isMultiTab
   });

   this.dashboard.loadDashboard();
};
function isTokenExpired() {
 if (!tokenExpiry) return true;
 return new Date() >= new Date(tokenExpiry);
}

Benefits

  • Fetch dashboards once
  • Reuse tokens until they expire
  • Render dashboards instantly with minimal server load