Extending the Demo - contentful/composable-storefront-integration-library GitHub Wiki
This guide will walk you through creating a new component with custom fields
First you need to create the component in Composable Storefront.
Create a model for the new component:
📁 src/app/model/cms.model.ts
import { CmsComponent } from '@spartacus/core';
import { CmsBannerComponentMedia } from '@spartacus/core/src/model/cms.model';
export interface CmsTextImageComponent extends CmsComponent {
text?: string;
image?: CmsBannerComponentMedia;
}
-
Create the Component File 📁
src/app/cms-components/content/text-image/text-image.component.ts
import { ChangeDetectionStrategy, Component } from '@angular/core'; import { CmsComponentData } from '@spartacus/storefront'; import { CmsTextImageComponent } from '../../../model/cms.model'; @Component({ selector: 'cx-text-image', templateUrl: './text-image.component.html', styleUrls: ['./text-image.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class TextImageComponent { constructor(public component: CmsComponentData<CmsTextImageComponent>) {} }
-
Create the Template File 📁
src/app/cms-components/content/text-image/text-image.component.html
<div *ngIf="component.data$ | async as data" class="container"> <h2>{{data.text}}</h2> <img *ngIf="data.image" src="{{data.image.url}}" alt="{{data.image.altText}}" /> </div>
-
Create the Stylesheet 📁
src/app/cms-components/content/text-image/text-image.component.scss
.container { display: flex; flex-direction: column; justify-content: center; align-items: center; }
-
Create a normalizer for the image Since Contentful structures images differently, we need a normalizer to transform the image data into the format used in Angular. 📁
src/app/cms-components/content/text-image/text-image-component-normalizer.ts
import { Injectable } from '@angular/core'; import { CmsComponent } from '@spartacus/core'; import { CmsBannerComponentMedia } from '@spartacus/core/src/model/cms.model'; import { Asset, Entry } from 'contentful'; import { ComponentSkeleton } from '../../../contentful/core/content-types'; import { isAsset } from '../../../contentful/core/type-guards'; import { CmsTextImageComponent } from '../../../model/cms.model'; @Injectable({ providedIn: 'root' }) export class TextImageComponentNormalizer { convert(source: Entry<ComponentSkeleton, undefined, string>, target: CmsTextImageComponent): CmsComponent { if (source.sys.contentType.sys.id === 'CmsTextImageComponent' && isAsset(source.fields?.['image'])) { target.image = this.normalizeMediaAsset(source.fields['image']); } return target; } private normalizeMediaAsset(media: Asset<undefined, string>): CmsBannerComponentMedia | undefined { return { altText: '', code: '', mime: media.fields.file?.contentType ?? '', url: media.fields.file?.url ?? '', }; } }
Create a module to register the new component and provide the normalizer in Spartacus.
📁 src/app/cms-components/content/text-image/text-image.module.ts
import { CommonModule, NgOptimizedImage } from '@angular/common';
import { NgModule } from '@angular/core';
import { CMS_COMPONENT_NORMALIZER, CmsConfig, provideDefaultConfig } from '@spartacus/core';
import { TextImageComponent } from './text-image.component';
import { TextImageComponentNormalizer } from './text-image-component-normalizer';
@NgModule({
imports: [CommonModule, NgOptimizedImage],
providers: [
provideDefaultConfig(<CmsConfig>{
cmsComponents: {
CmsTextImageComponent: {
component: TextImageComponent,
},
},
}),
{
provide: CMS_COMPONENT_NORMALIZER,
useExisting: TextImageComponentNormalizer,
multi: true,
},
],
declarations: [TextImageComponent],
exports: [TextImageComponent],
})
export class CmsTextImageModule {}
Finally, add the new component module to the Spartacus Feature Module:
📁 src/app/spartacus/spartacus-features.module.ts
🔗 More details: SAP Help - Creating a New Component
Now that we've created the component in Composable Storefront, the next step is to define the component template and content in Contentful.
-
Open Contentful, go to Content Models, and click Create Content Type.
-
Fill in the required details:
- Name (“Text-Image”)
- API Identifier (“CmsTextImageComponent”)
- Description
⚠️ The API Identifier must match the name (“CmsTextImageComponent”) used when registering the component module in Composable Storefront.
-
Add two fields:
-
Text
- Type: “Text” => “Short text, exact search”
- Name: “text”
- Field ID “text”
-
Image
- Type: “Media” => “One file”
- Name: “image”
- Field ID “image”
-
-
Click Create to save the new template.
Now, create a new component based on the template.
-
Create a new Text-Image content entry.
-
Add text and image values to be displayed in the component.
-
Click Publish to save and activate the component, including the image.
This guide will walk you through setting up a new page using a custom layout with new content slots.
To add a new page with a custom template in Composable Storefront, you need to configure both the layout and the routing. Once set up, you'll provide these configurations to the Composable Storefront application.
Create a custom layout configuration file:
📁 src/app/config/layout-config.ts
import { LayoutConfig } from '@spartacus/storefront';
export const layoutConfig: LayoutConfig = {
layoutSlots: {
DemoPageTemplate: {
slots: ['MainContent'],
},
},
};
🔗 More details: SAP Help - Page Layout
Create a custom routing configuration file:
📁 src/app/config/routing-config.ts
import { RoutingConfig } from '@spartacus/core';
export const routingConfig: RoutingConfig = {
routing: {
routes: {
demo: {
paths: ['demo'],
},
},
},
};
🔗 More details: SAP Help - Routing
Create a new configuration module:
📁 src/app/config/config.module.ts
import { NgModule } from '@angular/core';
import { provideConfig } from '@spartacus/core';
import { layoutConfig } from './layout-config';
import { routingConfig } from './routing-config';
@NgModule({
providers: [provideConfig(layoutConfig), provideConfig(routingConfig)],
})
export class DemoConfigModule {}
Finally, import the DemoConfigModule
in your AppModule
to apply the configurations.
Now that we've configured the layout and routing, the next step is to define the page template and content slots in Contentful.
- Open Contentful and go to your Content Models.
- Edit the CMSPage model.
- Add the new template:
- Find the Template field and edit it.
- Under Accept only specified values, enter the name of the new template.
- Click Confirm to save your changes.
- In the CMSPage model, create a new field of type Reference
- Name: “MainContent”
-
Field ID: “MainContent” (
⚠️ The Field ID must match the slot name from the layout configuration)
- Choose Many references, allowing multiple components to be added to the slot.
Use the FlexFields app from the Contentful Marketplace to manage slot selection.
- Navigate to Apps → Installed and configure the app.
- Add a new rule for the custom template:
- Hide all content slots except the ones that should be part of the template.
- Edit all other rules to exclude the newly created slots.
Now that the new page template is ready, we can create a content page based on it.
- Create a new CMSPage content entry.
- Select the Template from the dropdown. This will display the available slots.
- Fill in the required fields:
- Internal Name: A name to identify the component.
- Title: The headline displayed in the browser tab.
- Slug: Must match the route set in the routing configuration, which in this example is “demo”
-
Preview URL:
-
For static pages: Use a simple slug that matches the route.
- Example:
demo
→ Accessible athttps://localhost:4200/demo
- Example:
-
For dynamic pages: Use a specific example of the dynamic route.
- Example:
product/3755219/PSR%20960
→ Previews a product detail page for a specific product
- Example:
-
For static pages: Use a simple slug that matches the route.
- Add the previously created Text-Image component to the MainContent content slot.
- Publish the page.
- Start the development server.
- Navigate to your new route:
- Confirm that the components appear correctly on the page.