BookmarkSubscribeRSS Feed

Using the Data-Driven Content Object in Visual Analytics: Viewing Multiple Photos

Started ‎11-20-2023 by
Modified ‎11-20-2023 by
Views 729

This is the fourth part of a four part series that discusses how to display product photos in SAS Visual Analytics.

 

 

Scenario

 

In previous posts, we created a visualization to display an image of a selected product in a Visual Analytics report or the company logo if no product was selected. In this post, we will see how to display multiple photos if more than one product (or no product) is selected in the report. To do this, we will need to restructure the data passed from Visual Analytics (in JSON format) to an array of objects so it can be more easily used in the JavaScript code and then update the code to display an image gallery instead of a single image.

 

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.

 

Updating Sample Data

 

First, we need to update the sample data message to contain multiple rows instead of a single row.

 

 

01_niball_ddc4_updating_sample_data.png

 Select any image to see a larger version.

Mobile users: To view the images, select the "Full" version at the bottom of the page.

 

Notice that the rowCount, availableRowCout, and data values are updated to show multiple rows. For more information about how data is passed from Visual Analytics to the third-party visualization, see the third post in this series.

 

Restructuring the Data

 

Now that we are using more than one row of data in our JavaScript code, we need to restructure the data from a two-dimensional array to an array of objects. This is done if the data passed from Visual Analytics is valid (which is determined by referencing the validateRoles function in the contentUtil.js utility).

 

02_niball_ddc4_restructuring_data.png

 

Now, the data values can be referenced using data[i].item, where data[0].item references the first data value (the value Figure from the sample data), data[1].item references the second data value (the value Game from the sample data), and so on.

 

Gallery of Images

 

Finally, the JavaScript code needs to be updated to display one image (at full width) if one product line is selected, and a gallery of images (two per row) if more than one product line is selected.

 

03_niball_ddc4_image_gallery.png

 

For this example, a div (with a class of row) is created to hold all images (rowDiv) and appended to the web page inside the div (with an id of photo).

 

Then, a new div is created for each image using the for loop (colDiv).

 

If one row is passed, the div (and the image) spans the entire width (using the Bootstrap class of col-12). If more than one row is passed, the div (and each image) spaces half the width (using Bootstrap class of col-6).

 

Then, a Bootstrap class of py-2 is added to insert padding on the top and bottom of each image and colDiv is added to the DOM (using the appendChild method).

 

Lastly, a new img element is created for each row of data passed from Visual Analytics (using the createElement property), attributes of the element are modified (src and alt), and the element is added to the DOM (using the appendChild method).

 

The src attribute points to the image for the selected product line in the images folder. Note that the toLowerCase method is needed convert the product line passed from Visual Analytics (which is proper case) to the case used for the image file (which is in lower case). The alt attribute is the value of the selected product line. A Bootstrap class (img-fluid) is also added to the image to ensure the image takes up 100% of the width of the div (colDiv).

 

Testing

 

When the code is complete, you can test it in Visual Analytics. Notice that page prompts have been relocated to the left of the canvas and the page prompt has been changed to a list control (so users can select multiple product lines). In addition, a data-driven content object (that references the code) is included on the canvas.

 

04_niball_ddc4_all_product_lines.png

 

Notice when no product line is selected, images for all product lines are displayed in the data-driven content object.

 

Pro Tip: Recall that to use list controls in the page prompt or report prompt areas, the location of the prompts need to be on the left or the right or a prompt container needs to be used. In this example, I changed the location of the page prompt container to the left side.

 

When one product line is selected, an image for that product line is displayed and it takes up the full width of the object.

 

05_niball_ddc4_one_product_line.png

 

When more than one product line is selected, images for those product lines are displayed and take up half the width of the object and subsequent images are added below the others.

 

06_niball_ddc4_multiple_product_lines.png

 

Summary

 

You can use similar code to restructure data passed from Visual Analytics from a two-dimensional array to an array of objects and to display multiple images in your report.

 

References

 

Documentation: Working with Data-Driven Content

 

Programming Considerations for Data-Driven Visualizations

 

SAS GitHub repository for third-party visualizations 

 

Book: Interactive Reports in SAS Visual Analytics 

 

 

Find more articles from SAS Global Enablement and Learning here.

 

Code

 

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 Photos -->
    <div class="container" id="photo">

<!--        <div class="row">
            <div class="col py-2">
              <img src="images/figure.jpg" alt="Figure" class="img-fluid">
            </div>
            <div class="col py-2">
              <img src="images/game.jpg" alt="Game" class="img-fluid">
            </div>
            <div class="col py-2">
              <img src="images/plush.jpg" alt="Plush" class="img-fluid">
            </div>
            <div class="col py-2">
                <img src="images/bead.jpg" alt="Bead" class="img-fluid">
              </div>
          </div>
        -->
    </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": "dd55",
                "rowCount": 5,
                "availableRowCount": 5,
                "data": [
                    [
                        "Figure"
                    ],
                    [
                        "Game"
                    ],
                    [
                        "Plush"
                    ],
                    [
                        "Beach"
                    ],
                    [
                        "Bead"
                    ]
                ],
                "columns": [
                    {
                        "name": "bi79",
                        "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 rowDiv;  // Div for row
            let colDiv;  // Div for col
            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)" 
                );
                } else {
                    
                    // Restructure data from 2d array to array of objects
                    data = [];

                    for (let i = 0; i < vaMessage.data.length; i++) {
                        data.push({
                            item: vaMessage.data[i][0],
                            index: i
                        });
                    }
                }

                // Add photo to DOM
                const photoDiv = document.getElementById("photo");
                photoDiv.innerText = "";

                const rowDiv = document.createElement("div");
                rowDiv.classList.add("row");
                photoDiv.appendChild(rowDiv);

                for (let i = 0; i < data.length; i++) {
                    const colDiv = document.createElement("div");
                    if (rowCount == 1) {
                        colDiv.classList.add("col-12");
                    } else {
                        colDiv.classList.add("col-6");
                    }
                    colDiv.classList.add("py-2");

                    rowDiv.appendChild(colDiv);

                    itemPhoto = document.createElement("img");
                    itemPhoto.src="images/"+data[i].item.toLowerCase()+".jpg";
                    itemPhoto.alt = data[i].item;
                    itemPhoto.classList.add("img-fluid");

                    colDiv.appendChild(itemPhoto);
                }
            };

            /******************************** Helper functions *****************************************/
            function inIframe() {
                try {
                    return window.self !== window.top;
                } catch (e) {
                    return true;
                }
            };

        });
    </script>
</body>
</html>

 

 

 

Version history
Last update:
‎11-20-2023 10:31 AM
Updated by:
Contributors

SAS Innovate 2025: Register Now

Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!

Free course: Data Literacy Essentials

Data Literacy is for all, even absolute beginners. Jump on board with this free e-learning  and boost your career prospects.

Get Started

Article Tags