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.
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. ThePageModelManager
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 thejcr: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 componentdata-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 gridcolumnClassNames:
Provided class names for the responsive column