Bloomreach Angular SDK
Bloomreach Angular SDK provides simplified headless integration with Bloomreach Experience Manager for Angular-based applications. This library interacts with the Page Model API and Bloomreach SPA SDK and exposes a simplified declarative Angular interface over the Page Model.
What is Bloomreach Experience Manager?
Bloomreach Experience Manager (brXM) is an open and flexible CMS designed for developers and marketers. As the original headless CMS, brXM allows developers to build quickly and integrate with the systems. While it’s built for speed, it also provides top-notch personalization and channel management capabilities for marketers to drive results.
Features
- brXM Page Angular component;
- brXM Components Angular directive;
- Manage Content Button;
- Manage Menu Button;
- TransferState support;
- Angular Universal support.
Get Started
Installation
To get the SDK into your project with NPM:
npm install @bloomreach/ng-sdk
And with Yarn:
yarn add @bloomreach/ng-sdk
Usage
The following code snippets render a simple page with a Banner component.
src/app/app.module.ts
In the NgModule
metadata, it needs to import BrSdkModule
and specify all the brXM components as module entry components.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { BrSdkModule } from '@bloomreach/ng-sdk';
import { AppComponent } from './app.component';
import { BannerComponent } from './banner/banner.component';
@NgModule({
bootstrap: [AppComponent],
declarations: [
AppComponent,
BannerComponent,
],
entryComponents: [
BannerComponent,
],
imports: [
BrowserModule,
BrSdkModule,
],
})
export class AppModule {}
src/app/app.component.ts
In the app-root
component, it needs to pass the configuration and brXM components mapping into the br-page
component inputs.
import { Component } from '@angular/core';
import { Location } from '@angular/common';
import { BrPageComponent } from '@bloomreach/ng-sdk';
import { BannerComponent } from './banner/banner.component';
@Component({
selector: 'app-root',
template: `
<br-page [configuration]="configuration" [mapping]="mapping">
<ng-template let-page="page">
<header>
<a [href]="page.getUrl('/')">Home</a>
<ng-container brComponent="menu"></ng-container>
</header>
<section>
<ng-container brComponent="main"></ng-container>
</section>
<footer>
<ng-container brComponent="footer"></ng-container>
</footer>
</ng-template>
</br-page>
`,
})
export class AppComponent {
configuration: BrPageComponent['configuration'];
mapping = {
Banner: BannerComponent,
};
constructor(location: Location) {
this.configuration = {
cmsBaseUrl: 'http://localhost:8080/site',
request: { path: location.path() },
};
}
}
src/app/banner/banner.component.ts
Finally, in the app-banner
component, it can consume the component data via the component
input.
import { Component, Input } from '@angular/core';
import { Component as BrComponent } from '@bloomreach/spa-sdk';
@Component({
selector: 'app-banner',
template: 'Banner: {{ component.getName() }}',
})
export class BannerComponent {
@Input() component!: BrComponent;
}
Configuration
The br-page
component supports several options you may use to customize page initialization.
These options will be passed to the initialize
function from @bloomreach/spa-sdk
.
See here for the full configuration documentation.
Mapping
The br-page
component provides a way to link Angular components with the brXM ones.
It requires to pass the mapping
property that maps the component type with its representation.
-
The Container Items can be mapped by their labels.
import { NewsListComponent } from './news-list/news-list.component'; export class AppComponent { mapping = { 'News List': NewsListComponent, }; }
-
The Containers can be only mapped by their type, so you need to use constants from
@bloomreach/spa-sdk
. By default, the Angular SDK provides an implementation for all the container types as it is defined in the documentation.import { TYPE_CONTAINER_INLINE } from '@bloomreach/spa-sdk'; import { InlineContainerComponent } from './inline-container/inline-container.component'; export class AppComponent { mapping = { [TYPE_CONTAINER_INLINE]: InlineContainerComponent, }; }
From within the Container component, the Container Items can be accessed via the
getChildren
method. This can be used to reorder or wrap child elements.import { Component, Input } from '@angular/core'; import { Component as BrComponent } from '@bloomreach/spa-sdk'; @Component({ selector: 'app-inline-container', template: ` <div> <span *ngFor="let child of component.getChildren()"> <ng-container [brComponent]="child"></ng-container> </span> </div> `, }) export class InlineContainerComponent() { @Input() component!: BrComponent; }
-
The Components can be mapped by their names. It is useful for a menu component mapping.
import { MenuComponent } from './menu/menu.component'; export class AppComponent { mapping = { menu: MenuComponent, }; }
-
By default, container items that are not mapped will be rendered as a warning text. There is an option to override the fallback.
import { TYPE_CONTAINER_ITEM_UNDEFINED } from '@bloomreach/spa-sdk'; import { FallbackComponent } from './fallback/fallback.component'; export class AppComponent { mapping = { [TYPE_CONTAINER_ITEM_UNDEFINED]: FallbackComponent, }; }
Inline Mapping
There is also another way to render a component. In case you need to show a static component or a component from the abstract page, you can use inline component mapping.
@Component({
selector: 'app-root',
template: `
<br-page>
<ng-template>
<app-menu *brComponent="'menu'; let component" [component]="component">
</app-menu>
</ng-template>
</br-page>
`,
})
export class AppComponent {}
In a brXM component, it is also possible to point where the component's children are going to be placed.
@Component({
selector: 'app-footer',
template: `
<div>
@copy; Bloomreach
<ng-container brComponent></ng-container>
</div>
`,
})
export class FooterComponent {}
The component data in case of inline mapping can be accessed via the template context.
@Component({
selector: 'app-root',
template: `
<br-page>
<ng-template>
<ul *brComponent="'menu'; let component; let page=page">
<li><a [href]="page.getUrl('/')">Home</a></li>
<li *ngFor="let item of component.getModels()">...</li>
</ul>
</ng-template>
</br-page>
`,
})
export class AppComponent {}
Buttons
- Manage menu button can be placed inside a menu component using
brManageMenuButton
directive.import { Component, Input } from '@angular/core'; import { Component as BrComponent, Menu, Page, Reference } from '@bloomreach/spa-sdk'; interface MenuModels { menu: Reference; } @Component({ selector: 'app-menu', template: ` <ul [ngClass]="{ 'has-edit-button': page.isPreview() }"> <!-- ... --> <ng-container [brManageMenuButton]="menu"></ng-container> </ul> `, }) export class MenuComponent { @Input() component!: BrComponent; @Input() page!: Page; get menu() { const { menu } = this.component.getModels<MenuModels>(); return menu && this.page.getContent<Menu>(menu); } }
- Manage content button can be placed inside a component using
brManageContentButton
directive with non-empty input.import { Component, Input } from '@angular/core'; import { Component as BrComponent, Document, Page, Reference } from '@bloomreach/spa-sdk'; interface BannerModels { document: Reference; } @Component({ selector: 'app-banner', template: ` <div [ngClass]="{ 'has-edit-button': page.isPreview() }"> <!-- ... --> <ng-container [brManageContentButton]="document" documentTemplateQuery="new-banner-document" folderTemplateQuery="new-banner-folder" parameter="document" root="banners" [relative]="true"> </ng-container> </div> `, }) export class BannerComponent { @Input() component!: BrComponent; @Input() page!: Page; get document() { const { document } = this.component.getModels<BannerModels>(); return document && this.page.getContent<Document>(document); } }
- Add new content button can be placed inside a component using
brManageContentButton
directive but without passing a content entity.import { Component } from '@angular/core'; @Component({ selector: 'app-news', template: ` <div [ngClass]="{ 'has-edit-button': page.isPreview() }"> <!-- ... --> <ng-container [brManageContentButton] documentTemplateQuery="new-news-document" folderTemplateQuery="new-news-folder" root="news"> </ng-container> </div> `, }) export class NewsComponent { @Input() component!: BrComponent; @Input() page!: Page; // ... }
State Transfering
The br-page
component supports TransferState without any extra configuration.
To use it in Angular Universal applications, import ServerTransferStateModule on the server and BrowserTransferStateModule on the client.
If you would like to disable the feature, just pass false
into the stateKey
input.
<br-page [stateKey]="false"></br-page>
Reference
The Angular SDK is using Bloomreach SPA SDK to interact with the brXM. The complete reference of the exposed JavaScript objects can be found here.
br-page
This is the entry point to the page model. This component requests and initializes the page model, and then renders the page root component recursively.
Type | Property | Required | Description |
---|---|---|---|
input | configuration |
yes | The configuration of the SPA SDK. |
input | mapping |
yes | The brXM and Angular components mapping. |
input | page |
no | Preinitialized page instance or prefetched page model. Mostly that should be used to transfer state from the server-side to the client-side. |
input | stateKey |
no | The TransferState key is used to transfer the state from the server-side to the client-side. By default, it equals to brPage . If false is passed then the state transferring feature will be disabled. |
output | state |
no | The current state of the page component. |
This component also supports a template transclusion. <ng-template>
from the component contents will be rendered in the root component context.
Variable | Description |
---|---|
$implicit |
The root component. |
component |
The root component. |
page |
The current page instance. |
brComponent
This directive points to where children or some component should be placed. brComponent
can be used inside br-page
or mapped components only. If it is being used as a structural directive, then the template will be rendered in the context of every matched component. Otherwise, it will try to render all children components recursively.
Property | Required | Description |
---|---|---|
brComponent |
no | The component instance or a path to a component. The path is defined as a slash-separated components name chain relative to the current component (e.g. main/container ). If it is omitted, all the children will be rendered. |
The template context holds references to the current component and the current page instance.
Variable | Description |
---|---|
$implicit |
The current component. |
component |
The current component. |
page |
The current page instance. |
brManageContentButton
This directive places a button on the page that opens the linked content in the document editor or opens a document editor to create a new one. The button will only be shown in preview mode.
Property | Required | Description |
---|---|---|
brManageContentButton |
no | The content entity to open for editing. |
documentTemplateQuery |
no | Template query to use for creating new documents. |
folderTemplateQuery |
no | Template query to use in case folders specified by path do not yet exist and must be created. |
path |
no | Initial location of a new document, relative to the root . |
parameter |
no | Name of the component parameter in which the document path is stored. |
relative |
no | Flag indicating that the picked value should be stored as a relative path. |
root |
no | Path to the root folder of selectable document locations. |
brManageMenuButton
This directive places a button on the page that opens the linked menu in the menu editor. The button will only be shown in preview mode.
Property | Required | Description |
---|---|---|
brManageContentButton |
yes | The related menu model. |
Links
FAQ
- Information about common problems and possible solutions can be found on the troubleshooting page.
- Information about the recommended setup can be found on the best practices page.
License
Published under Apache 2.0 license.