This is the third part of a four-part series that discusses how to display product photos in SAS Visual Analytics.
• Basics
• Debugging (this article!)
Recall from previous posts that you would like to display an image of a selected product in a report. In this post, we will see how to add sample data to your code so the visualization can be displayed outside of SAS Visual Analytics. This is helpful for debugging the code before you use it in Visual Analytics. Note: More detail about the requirements and steps for using the data-driven content object in Visual Analytics can be found in the first post in this series. Note: The entire code is available at the end of this post.
Recall that data messages are sent from Visual Analytics to the data-driven content object in JSON format. We will create a sample data message (also in JSON format) so we can render the visualization outside of Visual Analytics. Note: Communication between Visual Analytics and the third-party visualization is discussed in more detail in the second post in this series.
To do this, we need to see the Product Line data for this example in JSON format. The SAS GitHub repository for third-party visualizations includes a visualization (jsonDataViewer.html) that can be used to display your data as a JSON object.
In Visual Analytics, add a data-driven content object to the canvas that references the jsonDataViewer sample from the SAS GitHub repository. This is the value I entered in the URL field on the Options pane: https://sassoftware.github.io/sas-visualanalytics-thirdpartyvisualizations/samples/jsonDataViewer.html .
Select any image to see a larger version.
Mobile users: To view the images, select the "Full" version at the bottom of the page.
Note: Details on how to reference samples can be found in the samples folder on the SAS GitHub repository for third-party visualizations.
Then, on the Roles pane, assign Product Line to the Variables role.
The data-driven content object displays the Product Line data in JSON format, which is how data is passed from Visual Analytics to the third-party visualization. You can filter or edit the result to return a smaller number of rows. For this example, we only want to return one row of data so we can select a Product Line value from the page prompt (Beach).
We can use the data from the jsonDataViewer sample to add a sample data message to the code.
Recall that in the code for this visualization, if one row is passed from Visual Analytics that means a single product line is selected and an image of that product line appears. If more than one row is passed from Visual Analytics that means no product line is selected and the company logo appears. For this example, we want to test the code when one row is returned, so only one row is included in the sample data message.
Next, code needs to be added to display the sample data (rather than the data passed from Visual Analytics) when the visualization is viewed outside of Visual Analytics.
The if statement checks to see if the function returns not true (! means not). If it does, then the sample data (sampleData) is passed to the onDataReceived function. Recall that the onDataReceived function initializes data variables, validates the data, and adds an image to the web page.
The inIframe function checks to see if the visualization is being rendered in an iFrame.
If the visualization is used inside Visual Analytics (in a data-driven content object), it’s rendered inside an iFrame (the window of the object is not the same as the topmost window in the window hierarchy, window.self !== window.top). If this is the case, the data passed from Visual Analytics is used. If the visualization is viewed outside of Visual Analytics (to test the validity of the code), it’s not rendered inside an iFrame (the window of the object is the same as the topmost window in the hierarchy). If this is the case, the sample data is used.
When the code is complete, you can test it by viewing the HTML file in a browser (outside of Visual Analytics).
Because the visualization is not viewed in an iFrame (outside of Visual Analytics), sample data is used. Recall that sample data has a rowCount of 1 and data for the Beach product line, so the Beach image is displayed. Note: For testing purposes, I used the FiveServer extension in Visual Studio Code to use my machine as a web server.
To test all scenarios, change the rowCount for the sample data message to 2.
When you view the updated HTML file in the browser, the company logo is displayed.
Notice, for this example, you don’t have to add additional data items to the sample data message. The value of rowCount determines whether a product line image is displayed or the company logo is displayed.
You can use this code to view any third-party visualization outside of Visual Analytics for debugging purposes. Remember to use the jsonDataViewer sample from the SAS GitHub repository for third-party visualizations to create the sample data in JSON format.
In the next part of this series, we will discuss how to view multiple photos if more than one product line is selected in the Visual Analytics report.
Documentation: Working with Data-Driven Content
Documentation: Programming Considerations for Data-Driven Visualizations
Resource: SAS GitHub repository for third-party visualizations
Book: Interactive Reports in SAS Visual Analytics
Below is the code in its entirety.
!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- Bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<!-- Utilities for DDC -->
<script type="text/javascript" src="util/messagingUtil.js"></script>
<script type="text/javascript" src="util/contentUtil.js"></script>
</head>
<body>
<!-- Container for Photo -->
<div class="container" id="photo">
</div>
<!-- Custom JavaScript -->
<script>
// Listening for HTML document completely loaded and parsed, best practice
document.addEventListener("DOMContentLoaded", function() {
/******************************* Declare variables ***************************************/
// Sample data message to render outside of VA for debugging
const sampleData = {
"version": "1",
"resultName": "dd60",
"rowCount": 2,
"availableRowCount": 1,
"data": [
[
"Beach"
]
],
"columns": [
{
"name": "bi69",
"label": "Product Line",
"type": "string"
}
]
};
// Dynamic data variables
let vaMessage; // Data message from VA
let vaResult; // Name of DDC object, required to send messages back to VA
let rowCount; // Number of rows passed from VA
let data; // Data to be parsed from VA data message
// Selection variables
let photoDiv; // Div for photo
let itemPhoto; // Element for photo
/******************************* Setup Callback Functions ***************************************/
// Attach event for data message from VA
va.messagingUtil.setOnDataReceivedCallback(onDataReceived);
// If not being rendered in iFrame (outside of VA), render with sample data
if (!inIframe()) {
onDataReceived(sampleData);
}
// Take action on received data
function onDataReceived(messageFromVA) {
// Initialize data variables
vaMessage = messageFromVA;
vaResult = messageFromVA.resultName;
rowCount = messageFromVA.rowCount;
// Validate data roles
if (
!va.contentUtil.validateRoles(
messageFromVA,
["string"], // data from DDC object
["number"] // additional field for brush column (not needed in this example)
)
) {
// If roles are invalid or no data is being passed, display a message in VA about required roles (requires https)
va.messagingUtil.postInstructionalMessage(
vaResult,
"Display Photo expects columns to be assigned in this order:\n" +
" 1. Item (string)"
);
}
// Add photo to DOM
const photoDiv = document.getElementById("photo");
photoDiv.innerText = "";
// Display photo for selected item
if (
rowCount == 1
) {
itemPhoto = document.createElement("img");
itemPhoto.src="images/"+vaMessage.data[0][0].toLowerCase()+".jpg";
itemPhoto.alt = vaMessage.data[0][0];
itemPhoto.classList.add("img-fluid");
photoDiv.appendChild(itemPhoto);
} else {
itemPhoto = document.createElement("img");
itemPhoto.src="images/insight_toys_logo.png";
itemPhoto.alt = "Insight Toys Logo";
itemPhoto.classList.add("img-fluid");
photoDiv.appendChild(itemPhoto);
};
};
/******************************** Helper functions *****************************************/
function inIframe() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
};
});
</script>
</body>
</html>
Thanks for the information!
@melissa69 sure!
Thanks
@Cayden my pleasure!
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
Data Literacy is for all, even absolute beginners. Jump on board with this free e-learning and boost your career prospects.