PageModelManager

The PageModelManager library is provided as an NPM package to be used by an SPA project. It accompanies the SPA and serves as a data model manager.

On behalf of the SPA, it abstracts the retrieval and management of the JSON structure that represents the actual content structure. It is also responsible for syncing with the SPA to let it know when it has to re-render its components.

See the NPM package @adobe/aem-spa-page-model-manager

When initializing the PageModelManager, the library first loads the provided root model of the App (via parameter, meta property, or current URL). If the library identifies that the model of the current page is not part of the root model it fetches and include it as the model of a child page.

page_model_consolidation

ComponentMapping

The ComponentMapping module is provided as an NPM package to the front-end project. It stores front-end components and provides a way for the SPA to map front-end components to AEM resource types. This enables a dynamic resolution of components when parsing the JSON model of the application.

Each items present in the model contains a :type field that exposes an AEM resource type. When mounted, the front-end component can render itself using the fragment of model it has received from the underlying libraries.

Dynamic Model to Component Mapping

For details about how the dynamic model to component mapping occurs in the JavaScript SPA SDK for AEM see the article Dynamic Model to Component Mapping for SPAs.

Framework-Specific Layer

A third layer must be implemented for each front end framework. This third library is responsible for interacting with the underlying libraries and provide a series of well-integrated and easy-to-use entry points for interacting with the data model.

The remainder of this document describes the requirements of this intermediary framework specific layer and aspires to be framework-independent. By respecting the following requirements, a framework-specific layer can be provided for the project components to interact with the underlying libraries in charge of managing the data model.

General Concepts

Page Model

The content structure of the page is stored in AEM. The model of the page is used to map and instantiate SPA components. The SPA developers create SPA components which they map to AEM components. To do this, they use the resource type (or path to the AEM component) as a unique key.

The SPA components must be in sync with the page model and be updated with any changes to its content accordingly. A pattern using dynamic components must be used to instantiate components on the fly following the provided page model structure.

Meta Fields

The page model uses the JSON Model Exporter, which is itself based on the Sling Model API. The exportable sling models expose the following list of fields to enable the underlying libraries interpret the data model:

  • :type: Type of the AEM resource (default = resource type)

  • :children: Hierarchical children of the current resource. Children are not part of the inner content of the current resource (can be found on items representing a page)

  • :hierarchyType: Hierarchical type of a resource. The PageModelManager currently supports the page type

  • :items: Child content resources of the current resource (nested structure, only present on containers)

  • :itemsOrder: Ordered list of the children. The JSON map object does not guaranty the order of its fields. By having both the map and the current array the consumer of the API has the benefits of both structures

  • :path: Content path of an item (present on items representing a page)

Framework-Specific Module

Separating concerns helps to facilitate project implementation. Therefore an npm-specific package should be provided. This package is responsible for aggregating and exposing the base modules, services, and components. These components must encapsulate the data model management logic and provide access to the data the project’s component is expecting. The module is also responsible for transitively exposing useful entry points of the underlying libraries.

To facilitate the interoperability of the libraries, Adobe advise the framework-specific module to bundle the following libraries. If necessary, the layer can encapsulate and adapt the underlying APIs before exposing them to the project.

Implementations

React

npm module: @adobe/aem-react-editable-components

Angular

npm module: @adobe/aem-angular-editable-components

Main Services and Components

The following entities should be implemented in accordance with the guidelines specific to each framework. Based on the framework architecture, the implementation can vary widely, but the described functionalities must be provided.

The Model Provider

Project components must delegate access to the fragments of a model to a Model Provider. The Model Provider is then in charge of listening for changes made to the specified fragment of the model and return the updated model to the delegating component.

To do this, the Model Provider must register to the [PageModelManager](https://experienceleague.adobe.com/docs/experience-manager-65-lts/content/implementing/developing/spas/spa-blueprint.html?lang=en#pagemodelmanager). Then when a change occurs it receives and pass the updated data to the delegating component. By convention, the property made available to the delegating component that will carry the fragment of model is named cqModel. The implementation is free to provide this property to the component but should consider aspects such as the integration with framework architecture, discoverability, and ease of use.

The Component HTML Decorator

The Component Decorator is responsible for decorating the outer HTML of the element of each component instances with a series of data attributes and class names expected by the Page Editor.

Component Declaration

The following meta data must be added to the outer HTML element produced by the project’s component. They enable the Page Editor to retrieve the corresponding editing configuration.

  • data-cq-data-path: Path to the resource relative to the jcr:content

Editing Capability Declaration and Placeholder

The following meta data and class names must be added to the outer HTML element produced by the project’s component. They enable the Page Editor to offer related functionalities.

  • cq-placeholder: Class name that identifies the placeholder for an empty component
  • data-emptytext: Label to be displayed by the overlay when a component instance is empty

Placeholder for Empty Components

Each component must be extended with a functionality that will decorate the outer HTML element with data attributes and class names specific to placeholders and related overlays when the component is identified as empty.

About the Emptiness of a Component

  • Is the component logically empty?
  • What should be the label displayed by the overlay when the component is empty?

Container

A container is a component meant to contain and render child components. To do so, the container iterates over the :itemsOrder, :items and :children properties of its model.

The container dynamically gets the child components from the store of the ComponentMapping library. The container then extends the child component with the Model Provider capabilities and finally instantiate it.

Page

The Page component extends the Container component. A container is a component meant to contain and render child components including child pages. To do so, the container iterates over the :itemsOrder, :items, and :children properties of its model. The Page component dynamically gets the child components from the store of the ComponentMapping library. The Page is responsible for instantiating child components.

Responsive Grid

The Responsive Grid component is a container. It contains a specific variant of the Model Provider representing its columns. The Responsive Grid and its columns are responsible for decorating the outer HTML element of the project’s component with the specific class names contained in the model.

The Responsive Grid component should come pre-mapped to its AEM counterpart as this component is complex and rarely customized.

Specific Model Fields

  • gridClassNames: Provided class names for the responsive grid
  • columnClassNames: Provided class names for the responsive column