New Async API + React HTML Components

This page covers the changes added to the Buzzy Frame API, giving full control to the user when calling API endpoints. Usage of such an API is explored as React components.

What's new?

The new iframe API gives users access to pre-existing sandbox API endpoints without the need for message handling. We've exposed a set of asynchronous functions that can be awaited, this ensures that HTML components can be more deterministic as a result is guaranteed. Buzzy handles the message handling, making your HTML code snippets simpler!

This feature is current opt-in and you must enable it through the HTML field settings under "Enable Async API".

Script Placement

Warning

You must place the <script> inside the <body> of your custom HTML code.

This is important as the Buzzy Frame API is loaded in the head and may not be available due to loading order.

Available Asynchronous Functions

Insert a Row

Usage

insertMicroappRow({ body })

Return

{ rowID }

Update a Row

Usage

updateMicroappRow({ body })

Return

{
    body: { status: 'success', message: 'MicroApp Record Updated' },
}

Remove a Row

Usage

removeMicroappRow({ rowID })

Return

None.

Fetch Embedded Row Data

Usage

fetchEmbeddedRowData({
    microAppID,
    embeddingRowID,
    subLimit,
    viewFilters,
    viewSort,
  })

Return

Resource as JSON.

Fetch All Embedded Data

Usage

fetchAllEmbeddedData(microAppID)

Return

Object with all data tables and sub tables resolves to N levels.

Fetch Data Table Rows 🆕

Usage

fetchDataTableRows({
    microAppID,
    embeddingRowID,
    viewFilters,
    queryOptions,
    viewSort,
  })

Return

Array of data table rows as JSON.

Query Options (Pagination) and Filtering

When fetching embedded row data, you might to do pagination, filtering, and/or limit the number of results that you get. Buzzy uses MongoDB under the hood, as such, view filters follow the rules for MongoDB queries.

Query Options (Pagination and Data Limits)

Query options allow you to control the number of results you get back from the query, as well as allowing you to do pagination using the skip parameter. Common supported MongoDB aggregation parameters:

More info on Sorting: The data fetched from the Microapp can be sorted. The order and field parameters are used in the searchFilter to specify which Microapp sort field and if it is ascending or descending. The sort fields are configured in the Microapp Results Tab. Sort field 1 corresponds to the field value 1 and so on. Where order is either 1 = ascending or -1 = descending.

Example fetching microapp rows using Sort Field 1.

queryOptions: [
	{
		"resourceID": "<microapp ID goes here>",
		"order": 1,
		"field": "1",
		"skip": "5",
		"limit": 100
	}
]

View Filters (Filtering)

In cases where you might want to filter the results based on the value of a field, you can query based on the value of a field of the data table row. viewFilters is an array of view filters to be used, as such you may filter many views as they are scoped to the data table ID (resourceID).

Combining the Above Concepts, see the following example with filtering and pagination included:

const recipeData = await buzzyFrameAPI.fetchDataTableRows(
  {
    microAppID: recipeMetadata.id, 
    viewFilters:[ {sortVal: {$regex: 'Lasagna'}} ],
    queryOptions:[{resourceID: recipeMetadata.id, limit: 1, skip: 0}]
  }
);

In the example above, the query will fetch rows where the sortVal contains the word "Lasagna", in this case, the sortVal is referencing the data table row title (the recipe title). The query options them limit the rows to only 1 row, while skipping 0. If we were doing some pagination, when a user clicked a "Next Page" button, we could change the skip to 1 so it would get the next recipe that satisfies the filters.

Listen for Updates

Usage

addMicroappListener({ 
    microAppID, 
    listener 
})

Register a listener function for a given microapp (aka data table).

Whenever data is added or updated that fits the query you have running on the given microapp, the supplied listener function will be executed. In the native app version, this function will also be run if the user initiates a refresh by dragging down on the screen.

Typically this would be used to re-run your query and set the results to a state variable, and thus trigger re-rendering of your fetched data whenever it is updated.

The listener will be passed on object with these properties:

  • microAppID - the id that passed into the listener

  • isUserRefresh - true if this is a user initiated refresh

  • timestamp - timestamp of the update that triggered the listener

Code Example

Preamble

Buzzy HTML Field Settings

Due to the usage of React, we want to disable handlebars and opt-in to the new Async API. This is easily accomplished in the settings panel of the HTML field definition:

Our Example Data table Layout

As a reference for the code snippet, the fields and set up of the data tables referenced in the code example are provided below:

Fruit Reports (Data table)

  • Fruit Report (Text)

  • Date (Datetime)

  • Fruit Counters (Sub table)

  • Graphic-HTML (HTML Field)

Fruit Counters (Sub table)

  • Fruit Type (Select list)

  • Number (Count)

  • Date Counted (Datetime)

Code

<!DOCTYPE html>
<html>
  <head>
    <title>HTML Example</title>
    <script
      src="https://unpkg.com/react@18/umd/react.production.min.js"
      crossorigin
    ></script>
    <script
      src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"
      crossorigin
    ></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.3.3/dist/chart.umd.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel" data-presets="env,react">
      // Initializing the global BuzzyFrameAPI class
      // This gives us access to API methods to do CRUD operations
      const buzzyFrameAPI = new BuzzyFrameAPI();

      // React component
      function App() {
        const { useEffect, useState } = React;

        const [frameToken, setFrameToken] = useState(null);
        const [fruitData, setFruitData] = useState(null);

        useEffect(() => {
          let isUnmounted = false;

          async function initData() {
            try {
              const initData = await buzzyFrameAPI.initialise();
              const {
                rowJSON = {},
                frameToken,
                id,
                resolveData,
              } = initData || {};

              if (rowJSON?._id && rowJSON["Fruit Counters"]) {
                const fruitTableRowData =
                  await buzzyFrameAPI.fetchDataTableRows({
                    microAppID: rowJSON["Fruit Counters"],
                    embeddingRowID: rowJSON?._id,
                  });

                if (!isUnmounted) {
                  setFruitData(fruitTableRowData);
                }
              }
            } catch (error) {
              console.log("[IFRAME] Error occured in initData().", error);
            }
          }

          initData();

          return () => {
            isUnmounted = true;
          };
        }, []);

        const PieChart = ({ data }) => {
          const labels = data.map((fruit) => fruit["Fruit Type"]);
          const counts = data.map((fruit) => fruit["Count"]);

          const chartData = {
            labels,
            datasets: [
              {
                data: counts,
                backgroundColor: [
                  "#FF6384",
                  "#36A2EB",
                  "#FFCE56",
                  "#4CAF50",
                  // Add more colors if needed
                ],
              },
            ],
          };

          const canvas = document.createElement("canvas");
          document.getElementById("root").appendChild(canvas);

          new Chart(canvas.getContext("2d"), {
            type: "doughnut",
            data: chartData,
          });
        };

        return (
          <div>
            {fruitData ? <PieChart data={fruitData} /> : <p>Loading...</p>}
          </div>
        );
      }

      ReactDOM.render(<App />, document.getElementById("root"));
    </script>
  </body>
</html>

Result

With some data table rows, the above example provides the following output in Buzzy:

Limitations

The current iteration of the new API is still under development. There are a number of key issue yet to be resolved:

Last updated