Bloomreach Experience SDK for Angular
SDK for powering content and components in Angular applications by BloomReach Experience.
This library makes integrating an Angular app with BloomReach Experience a breeze. It
supports both client-side- and server-side rendered/universal Angular apps.
BloomReach Experience allows you to use an external front-end such as Angular for
rendering while still providing a native-like authoring experience, such as integrated
preview, in-context editing, drag & drop, server-side personalization. For more
information on this approach, see A new approach to integrating SPA's with WCM: Fixing
what's wrong with headless integrations.
Install
npm install bloomreach-experience-ng-sdk jsonpointer path-to-regexp --save
Please note that since it's not a best practice to include 3rd party libraries with an
Angular library, the 3rd party libraries have to be installed manually as done with the
above command.
Usage
Add the SDK to the app's NgModule imports:
import { BloomreachExperienceNgSdkModule } from 'bloomreach-experience-ng-sdk';
@NgModule({
imports: [
BloomreachExperienceNgSdkModule
],
...
Then, add the following to an Angular component where you want BloomReach to (partly)
determine what to render:
- Import
ApiUrlsService
, ComponentMappingsService
, InitializeSdkService
, and RequestContextService
; - Add these services to the constructor of the component;
- Override default API Urls using
ApiUrlsService
; - Set component mappings through
ComponentMappingsService
; - Set current URL-path via
RequestContextService
; - Call
initialize()
method of InitializeSdkService
; - And add
<bre-render-cms-component></bre-render-cms-component>
to the component template.
See the next paragraph for a description and explanation of each of the services and the
components. There's also an API section below that includes more details.
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { ApiUrlsService, ComponentMappingsService, InitializeSdkService, RequestContextService } from 'bloomreach-experience-ng-sdk';
@Component({
template: `<bre-render-cms-component></bre-render-cms-component>`,
})
export class MyAppClass implements OnInit {
componentMappings = {
"MyCustomComponent": MyCustomComponent
};
apiUrls = {
live: {
},
preview: {
}
}
constructor(
private router: Router,
private apiUrlsService: ApiUrlsService,
private componentMappingsService: ComponentMappingsService,
private initializeSdkService: InitializeSdkService,
private requestContextService: RequestContextService
) {}
ngOnInit() {
this.apiUrlsService.setApiUrls(this.apiUrls);
this.componentMappingsService.setComponentMappings(this.componentMappings);
this.requestContextService.parseUrlPath(this.router.url);
this.initializeSdkService.initialize();
}
}
The component mappings tell the Angular app what Angular components to use for rendering
components from the Page Model API response, by mapping these to the hst:label of a CMS
catalog component. These mappings are set through the ComponentMappingsService
.
The RequestContextService
is used to pass the current URL so that it can fetch the Page
Model for the page that is active; and to detect whether preview is active so that
meta-data for CMS' Channel Manager functionality (e.g. in-context editing) is included in
the HTML, and consequently Channel Manager functionality is enabled.
InitializeSdkService
fetches the Page Model for the current URL that is set through
RequestContextService
. and it initializes the CMS' Channel Manager integration.
Finally, <bre-render-cms-component>
renders the components and any content referenced
from the components in the Page Model API response. This tag/component should be placed in
the Angular app at the exact location where you want the CMS components to be outputted.
Demo
A demo project is available at Github.
Please note that in order to use the Channel Manager Integration,
you need to have an Enterprise Maven account, which is only available toBloomReach's
Enterprise customers.
The demo project can be used in combination with the Angular example app. To build and
run the example app, do the following:
npm install
npm run start
The demo project is configured for React by default, so you will have to update the CORS
headers from http://localhost:3000 to http://localhost:4200 in
/hst:hst/hst:hosts/dev-localhost/localhost/hst:root[@hst:responseheaders].
Also, make sure to change the Channel Settings in the Channel Manager, and select Angular
from the front-end renderer dropdown menu.
Creating custom components
Custom components that are rendered through <bre-render-cms-component>
can be created
just as any other regular Angular component, with the requirement that it should implement
the BaseComponent
interface that is provided by the SDK.
Additionally, all components that are rendered through <bre-render-cms-component>
should
be added to the @NgModule()
's entryComponents
array, as the components are created
using the componentFactory. Otherwise you will get an error during component rendering.
Properties
The following properties are passed to the component, provided it's rendered by
<RenderCmsComponent>
:
configuration
- Object
component configuration. Contains the contributed models, raw
parameters and resolved parameters. Content included in the component's model is not
serialized as part of the component's configuration but in a separate content object, and
a JSON Pointer reference is used to link to the actual content object. This is done to
prevent content from being included multiple times in the API response when referenced
multiple times on a page.
Example
import { Component, Input, OnInit } from '@angular/core';
import { BaseComponent, getNestedObject } from 'bloomreach-experience-ng-sdk';
@Component({
selector: 'app-news-list',
templateUrl: `
<div class="row" *ngIf="items; else configureComponentBlock">
<div class="col-sm-12 news-list">
<app-news-item *ngFor="let item of items" [contentRef]="item"></app-news-item>
</div>
<nav class="blog-pagination">
<span class="btn btn-outline-primary disabled">Older</span>
<span class="btn btn-outline-secondary disabled">Newer</span>
</nav>
</div>
<ng-template #configureComponentBlock>Click to configure {{configuration.label}}</ng-template>`,
styleUrls: ['./news-list.component.css']
})
export class NewsListComponent implements BaseComponent, OnInit {
@Input() configuration: any;
items: any;
constructor() { }
ngOnInit() {
this.createList();
}
createList() {
this.items = getNestedObject(this.configuration, ['models', 'pageable', 'items']);
}
}
Content components
Components that reference a single content-item (e.g. the Banner component) can use a
superclass that provides a convenient method for retrieving the corresponding content-item
based on the reference found in the component's configuration.
To use the superclass, a content component has to extend SingleContentComponent
, which
is provided by the SDK. Just as a regular custom component, the component will need to
implement the BaseComponent
interface.
The SingleContentComponent
provides two methods: getContent()
and
getImageUrl(imageRef)
. See more details below.
Finally, content components can use the <bre-cms-edit-button>
component for rendering
the Manage Content
Button
in preview mode in the CMS.
Properties
content
- Object
raw content object that contains the content-item's fields and
field-values. Any references to other content-items (e.g. images) are serialized as JSON
Pointers.
Methods
getContent
- Object
retrieves the component's referenced content-item using the
JSONPointer that is found in the component's configuration. Stores the content-item
object in the content
property.getImageUrl(imageRef: string)
- String
generates fully qualified URL to an image
using the JSONPointer reference to the image.
Example
import { Component, OnInit } from '@angular/core';
import { BaseComponent, ImageUrlService, PageModelService, SingleContentComponent } from 'bloomreach-experience-ng-sdk';
@Component({
selector: 'app-banner',
templateUrl: `
<div class="jumbotron has-edit-button" *ngIf="content; else configureComponentBlock">
<bre-cms-edit-button *ngIf="content" [configuration]="content"></bre-cms-edit-button>
<h1>{{content.title}}</h1>
<figure>
<img src={{imageUrl}} alt={{content.title}}/>
</figure>
<p [innerHTML]="content.content.value"></p>
</div>
<ng-template #configureComponentBlock>Click to configure {{configuration.label}}</ng-template>`,
styleUrls: ['./banner.component.css']
})
export class BannerComponent extends SingleContentComponent implements BaseComponent, OnInit {
imageUrl: string;
constructor(imageUrlService: ImageUrlService, pageModelService: PageModelService) {
super(imageUrlService, pageModelService);
}
ngOnInit() {
super.ngOnInit();
this.getImage();
}
getImage() {
if (this.content && this.content.image) {
this.imageUrl = super.getImageUrl(this.content.image);
}
}
}
Static CMS components
Static CMS components are components that are defined by developers / administrators and
cannot be modified by users in the CMS. However, any content or site menus these
components reference can be changed by users in the CMS.
Since <RenderCmsComponent>
only renders container components (drag-and-drop components)
by default, you have to specify two additional properties in order to render a static CMS
component:
path
property to point to the relative path of the component;renderComponent
property to specify which Angular component to use for rendering the
component. See the example below.
import { Component } from '@angular/core';
import { MenuComponent } from '../cms-components/menu/menu.component';
@Component({
selector: 'app-static-component-example',
templateUrl: `<bre-render-cms-component [path]="'menu'" [renderComponent]="menuComponent"></bre-render-cms-component>`,
styleUrls: ['./static-component-example.css']
})
export class StaticComponentExample {
menuComponent = MenuComponent;
}
More component examples
For more detailed examples, see the components included in the demo application.
Building the SDK
You can build the SDK using the following command:
ng build --prod bloomreach-experience-ng-sdk
To test out builds locally, you can use the example application that you can build and run from the root:
npm install
npm run start
API
ApiUrlsService
Service for overriding the default API URLs, which are used for fetching the Page Model
API. Uses both a live and preview URL for the API URL.
Methods
setApiUrls(newApiUrls: ApiUrls = {})
- None
allows you to override the default URLs.
Typically you will only have to define scheme
, hostname
, port
, and contextPath
.
See ApiUrls
type below for format of the newApiUrls
parameter.
ApiUrls
type
live
:
scheme
: String
scheme (default: http)hostname
: String
hostname (default: localhost)port
: number
port number (default: 8080)contextPath
: String
site context-path (default: site)channelPath
: String
path to the used channel, if channel is accessed through a
subpathpreviewPrefix
: String
preview-prefix used by CMS (default: _cmsinternal)apiPath
: String
path to Page Model API as subpath (default: resourceapi)apiComponentRenderingUrlSuffix
: String
(default: _hn:type=component-rendering&_hn:ref=)
preview
: same as live (see above)
ComponentMappingsService
The component mapping maps CMS catalog components to Angular components, so that Angular
knows what components to use for rendering the components in the Page Model API response.
Methods
setComponentMappings(componentMappings: ComponentMappings)
- None
sets the component
mappings. Expects as input an object with the hst:label
of the CMS components as keys
and as value the Angular component used for rendering the component.
Example
import { Component, OnInit } from '@angular/core';
import { ComponentMappingsService } from 'bloomreach-experience-ng-sdk';
export class ComponentMappingExampleComponent implements OnInit {
constructor(private componentMappingsService: ComponentMappingsService) {}
ngOnInit() {
const componentMappings = {
'Banner': BannerComponent
}
this.componentMappingsService.setComponentMappings(this.componentMappings);
}
}
ImageUrlService
Generates URLs to images for any images that are served directly from BloomReach
Experience.
Methods
getImageUrl(imageRef)
- String
returns URL of image using imageRef
, the JSON
Pointer that references the image.getImageUrlByPath(imagePath: string, variant: string)
- String
returns URL of image
using its relative-path and the name of the image-variant.
InitializeSdkService
Service that handles initialization of the SDK, including:
Methods
initialize({initializePageModel = true, initializeRouterEvents = true}): Subscription | void
- initializes the SDK by fetching the Page Model and initializing the Channel manager integration. Returns router events subscription or void if initializeRouterEvents
is false
.
initializePageModel: boolean
- flag to fetch the Page Model on initialization;initializeRouterEvents: boolean
- flag to subscribe for router events.
PageModelService
Fetches the Page Model API and manages state.
Methods
getPageModel()
- Object
return the Page Model. Please note that this is a
synchronous call, so if this is called during initialization, the Page Model might not
have been set yet. Use getPageModelSubject()
in these cases.setPageModel(value: any)
- update the Page Model and push the value to the Page Model subject (see getPageModelSubject()
).getPageModelSubject()
Subject<Object>
return a subject of the Page Model that can be
subscribed to for asynchronous access to the Page Model.getContentViaReference(contentRef: string)
- Object
returns content item from Page
Model using the contentRef
JSON Pointer.
RequestContextService
The RequestContextService
is used to get the current URL for fetching the Page Model for
the current page; and to detect whether preview mode is active so that meta-data for
Channel Manager functionality (e.g. in-context editing) is included in the HTML, and
consequently Channel Manager functionality is enabled.
Methods
parseUrlPath(urlPath: string): void
- parses the current URL-path for detecting the
current URL and preview detection.parseRequest(request: Request): void
- parses the current request for detecting the
current URL and preview detection. See Request
type below for format of the request
parameter.isPreviewRequest(): boolean
- returns if preview mode is active/detected.getDebugging(): boolean
- returns if debugging mode is enabled or notsetDebugging(debugging: boolean): void
- sets debugging mode which enables detailed
logging on request parsing, Channel Manager integration, and component updates.
Request
type
<bre-render-cms-component>
Renders a CMS component and all of its children using the Page Model supplied by
PageModelService
. Will render the entire Page Model by default.
Properties
path
- String
path to a component (static CMS component), container or
container-item in the Page Model to render only that component and its children. If no
path is supplied, entire Page Model will be rendered.renderComponent
- Angular.Component
render a static CMS component using specified
Angular component. Only works in combination with path
property, which should specify
path to the static CMS component. Site menus that are rendered this way can leverage the
<bre-cms-edit-button>
component for rendering edit buttons in the CMS.
Example
import { Component } from '@angular/core';
import { MenuComponent } from '../cms-components/menu/menu.component';
@Component({
selector: 'app-static-component-example',
templateUrl: `<bre-render-cms-component [path]="'menu'" [renderComponent]="menuComponent"></bre-render-cms-component>`,
styleUrls: ['./static-component-example.css']
})
export class StaticComponentExample {
menuComponent = MenuComponent;
}
<bre-cms-edit-button>
Inserts meta-data for either a content-item or site menu for placing an edit button in
preview mode in the CMS.
Properties
configuration
- Object
configuration of the content-item or site-menu (not the
component configuration containing the content-item/site-menu), which has the _meta
object in its root.
Example
<bre-cms-edit-button *ngIf="menuConfiguration" [configuration]="menuConfiguration"></bre-cms-edit-button>
getNestedObject(nestedObject, pathArray)
Returns a nested object or value using a path array. Useful when you need to access deeply
nested objects/values without having to string null checks together.
Arguments
nestedObject
- Object
the object containing the nested object or value.pathArray
- Array
contains the path to the nested object as an array.
Return types
Object|null
returns the nested object if found, otherwise returns null.
FAQ / Troubleshooting
Nothing here yet :)
License
Apache 2.0