Edit in GitHubLog an issue

Add aggregator

The purpose of the configuration aggregator (configAggregator) is to retrieve properties from a content type's HTML and return those properties as a flat object of key:values. The framework (specifically the <ContentTypeFactory />) then passes this object to your component, where you can assign the properties to your component's corresponding properties for rendering within a PWA Studio app.

Aggregator Overview

Content type HTML

The key to building out your aggregator is knowing the structure, content, and properties available in your content type's master format. You can do this the hard way by looking in the database of your Adobe Commerce or Magento Open Source backend instance (specifically, the content field in the cms_page and cms_block tables). But there is a better way.

Protip: Place a console.log(node) at the top of your configAggregator function so you can see exactly what your content type's HTML (the HTMLElement passed by the framework) looks like. In the ExampleQuote starter code, you would add it as shown here:

Copied to your clipboard
export default (node, props) => {
console.log(node);
return {
// Retrieve properties from node here
};
};

The console output should look something like this:

Content type HTML

The node (HTMLElement) passed to the aggregator contains only your content type's HTML from the master format, not the entire master format as you would find in the database.

The Aggregator

The interface for a configAggregator is:

Copied to your clipboard
(node: HTMLElement, props: {contentType: string, appearance: string}) => {[key: string]: any}

To recap, the purpose of your component's aggregator is to collect (aggregate) properties from your content type's HTML and return a property object for use in your component. The object you return should contain all the text, html, inline styles, and classes you need to faithfully reproduce your content type as a component in PWA Studio.

Let's look at our Quote content HTML in detail (color coded for easier analysis) to determine the properties we want to pass to our component:

Master format HTML

Here's what we think we need for our component:

  • The inline styles from the main node
  • The text content from the <blockquote> and author nodes
  • The HTML content from the description node
  • The CSS classes from all three child nodes (<blockquote>, author, and description)

To retrieve these properties, you'll want to use a combination of HTMLElement DOM properties along with our utility functions, as shown next in the example.

Example aggregator

Here is the aggregator we use for the ExampleQuote component:

Copied to your clipboard
import { getAdvanced } from "../../utils";
export default (node, props) => {
console.log(node);
return {
quote: node.childNodes[0].textContent,
author: node.childNodes[1].textContent,
description: node.childNodes[2].innerHTML,
...getAdvanced(node),
};
};

First we import the utility functions we want to use. In our case, we know that our Quote content type provides end users with the Advanced form section. So we import the getAdvanced() function from utils.js. This function is a wrapper that just runs a number of other utility functions that can be used independently if these values are on different nodes:

Copied to your clipboard
export function getAdvanced(node) {
return {
...getPadding(node),
...getMargin(node),
...getBorder(node),
...getTextAlign(node),
...getCssClasses(node),
...getIsHidden(node),
};
}

Then we use the element names from the content type HTML (color coded in green above) as our property key names: quote, author, and description. Naming your properties like this helps to identify where the data in the component comes from.

Next, we use the textContent and innerHTML DOM properties to grab the text and html values from the appropriate childNodes.

Finally, we use the getAdvanced() utility function to retrieve all the property values from the Advanced section of our content type's form and use the spread operator (...) to expand them into the current object.

Retrieving data from different Appearances

If your content type has different appearances, the HTML for each appearance will also differ. To handle these differences, we provide the appearance within the props argument so you can modify your queries in order to retrieve data from the correct node.

For our Quote content type, we only have one appearance (the default); so we do not need to use it. However, the Row content type has three appearances, so it uses a conditional based on the props.appearance value to determine the correct node to use, as shown here:

Copied to your clipboard
// Targeting appearances in the Row aggregator
export default (node, props) => {
// Determine which node holds the data for the appearance
const dataNode =
props.appearance === 'contained' ? node.childNodes[0] : node;
return {
minHeight: dataNode.style.minHeight ? dataNode.style.minHeight : null,
...
};

Test the aggregator

The best way to see the properties returned by your aggregator is using console.log(). For our Quote aggregator, we can do something like this:

Copied to your clipboard
import { getAdvanced, getCssClasses, getBackgroundImages } from "../../utils";
export default (node, props) => {
console.log(node);
const propObject = {
quote: node.childNodes[0].textContent,
author: node.childNodes[1].textContent,
description: node.childNodes[2].innerHTML,
...getAdvanced(node),
};
console.log(propObject);
return propObject;
};

Adding console.log(node) at the beginning of your aggregator function, and at he end (console.log(propObject)), helps to show you the HTML you receive and the object you return. The property object returned from our aggregator looks like this:

Aggregator object console output

  • Privacy
  • Terms of Use
  • Do not sell or share my personal information
  • AdChoices
Copyright © 2024 Adobe. All rights reserved.