Use Coveo Search in SAP Commerce Cloud Composable Storefront

SAP Commerce Cloud composable storefront (previously SAP Spartacus) is built upon Angular, which Coveo Atomic supports natively.

This article explains how to include Coveo Atomic into the Angular components of an SAP Commerce Cloud composable storefront.

Important

This article shows how to replace the default components of the SAP Commerce Cloud composable storefront. As the default components already exist in the back end, you don’t need to do anything in the Backoffice Administration Cockpit.

You need to update the front end only.

Adjust the app to use Coveo

To use Coveo-powered search in your project, perform the following steps.

  1. In the root of your Angular application, open the tsconfig.json file and update the compilerOptions object with the following:

    "compilerOptions": {
      // ...
      "allowSyntheticDefaultImports": true
    },
  2. Install the Atomic Angular package:

    npm i @coveo/atomic-angular
  3. Move the Atomic static assets to the src directory of your app and update the angular.json file accordingly.

  4. To add basic styling, include the Default Coveo Theme.

  5. To be able to use the Atomic Angular components, open the src/app/app.module.ts file and import the AtomicAngular module.

Update the main component

  1. To initialize the search interface, update the src/app/app.component.ts file:

    import { AfterViewInit, Component } from '@angular/core';
    import { loadFieldActions } from '@coveo/atomic-angular'
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.scss']
    })
    
    export class AppComponent implements AfterViewInit {
      ngAfterViewInit(): void {
        const searchInterface = document.querySelector('atomic-search-interface');
        searchInterface
          ?.initialize({
            accessToken: 'xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', 1
            organizationId: 'orgnameplaceholder',
          })
          .then(() => {
            const engine = searchInterface.engine;
    
            if (engine) {
              const fieldActions = loadFieldActions(engine);
              engine.dispatch(fieldActions.registerFieldsToInclude([ 2
                'ec_brand',
                'ec_category',
                'ec_image',
                'ec_price'
              ]));
              searchInterface.executeFirstSearch();
            }
          });
      }
    }
    1 Specifies credentials for the target Coveo organization. See Authenticate via search token and Use API key authentication with the Search API.
    2 Specifies what custom fields to use in the UI.
  2. By default, the src/app/app.component.html contains only one component:

    <cx-storefront></cx-storefront>

    This component is the entry point of the SAP Commerce Cloud composable storefront. To add a search interface to the storefront, you need to wrap it inside the atomic-search-interface component. Also, you need to add an Angular template for every Atomic component you want to implement in your project. For example, you can add a atomic-search-box component:

    <atomic-search-interface>
      <cx-storefront></cx-storefront>
    </atomic-search-interface>
    
    <ng-template cxOutletRef="SearchBoxComponent"> 1
      <app-coveo-search-box></app-coveo-search-box>
    </ng-template>
1 cxOutletRef is the name of the default component you want to replace. To find out the name of the default component, you need to:
  1. Open the storefront in your browser.

  2. On the Network tab, find the request to the cms/pages? endpoint.

  3. In the preview, find the content slot that contains the component you want to replace.

For example, the default search box component is called SearchBoxComponent:

The preview of the request to the `cms/pages?` endpoint with the `contentSlots` section expanded
Important

You can also replace a default component by updating the storefront’s global configuration. See provideConfig in the SAP Commerce Cloud documentation.

See the example of this method below, in the next section.

Build a search box module

Now that you imported and initialized Coveo Atomic, you can use Atomic components to build a UI.

  1. Create a coveo-search module:

    ng g module coveo-search
  2. Open the created src/app/coveo-search/coveo-search.module.ts file and import AtomicAngularModule:

    // other imports
    import { AtomicAngularModule } from '@coveo/atomic-angular';
    
    @NgModule({
      declarations: [],
      imports: [CommonModule, AtomicAngularModule]
    })
    export class CoveoSearchModule {}
  3. Next, within your new module, create a coveo-search-box component:

    ng g component coveo-search/coveo-search-box

    This imports the CoveoSearchBoxComponent component and adds it into your module’s declarations section. You may also want to specify the component in the exports section, so that you can use it in other modules in the future.

    Example
    // other imports
    import { CoveoSearchBoxComponent } from './coveo-search-box/coveo-search-box.component';
    
    @NgModule({
      declarations: [CoveoSearchBoxComponent],
      imports: [CommonModule, AtomicAngularModule],
      exports: [CoveoSearchBoxComponent]
    })
    Important
    Another way to replace components

    If you decided not to update src/app/app.component.html to replace the default components, you can do the replacement via provideConfig.

    For example, to replace SearchBoxComponent, open the src/app/coveo-search/coveo-search.module.ts file and add the providers section to the @NgModule decorator:

    @NgModule({
      declarations: [CoveoSearchBoxComponent],
      imports: [CommonModule, SearchBoxModule, AtomicAngularModule],
      exports: [CoveoSearchBoxComponent],
      providers: [
        provideConfig({
          cmsComponents: {
            SearchBoxComponent: {
              component: CoveoSearchBoxComponent,
            },
          },
        }),
      ],
    })
  4. Update the src/app/coveo-search-box/coveo-search-box.component.html template:

    <div class="custom-search">
      <atomic-layout-section section="search">
        <atomic-search-box>
          <atomic-search-box-recent-queries></atomic-search-box-recent-queries>
          <atomic-search-box-query-suggestions></atomic-search-box-query-suggestions>
        </atomic-search-box>
      </atomic-layout-section>
    </div>

Build a search results module

The default search results component is called SearchResultsListComponent.

The preview of the request to the `cms/pages?` endpoint with the `SearchResultsListComponent` component shown

To replace it with an Atomic component, perform the similar steps as for the search box component above.

  1. Update the src/app/app.component.html file to replace that component:

    // ...
    // previously added components
    
    <ng-template cxOutletRef="SearchResultsListComponent">
      <app-coveo-search-list></app-coveo-search-list>
    </ng-template>
  2. Create a coveo-search-result module and the coveo-search-list component within it.

  3. Update the src/app/coveo-search-list/coveo-search-list.component.html template file:

    <div class="custom-search-results">
      <atomic-layout-section section="main">
        <atomic-layout-section section="status">
          <atomic-refine-toggle></atomic-refine-toggle>
          <atomic-query-summary></atomic-query-summary>
          <atomic-sort-dropdown>
            <atomic-sort-expression label="relevance" expression="relevancy"></atomic-sort-expression>
            <!-- The custom field dispatched in app.component.ts -->
            <atomic-sort-expression label="Price (high to low)" expression="ec_price descending"></atomic-sort-expression>
            <!-- The custom field dispatched in app.component.ts -->
            <atomic-sort-expression label="Price (low to high)" expression="ec_price ascending"></atomic-sort-expression>
          </atomic-sort-dropdown>
        </atomic-layout-section>
        <atomic-layout-section section="results">
          <atomic-result-list
            display="grid"
            image-size="large"
          >
            <atomic-result-template>
              <template>
                <atomic-result-text field="title"></atomic-result-text>
                <atomic-result-section-visual>
                  <!-- The custom field dispatched in app.component.ts -->
                  <atomic-result-image field="ec_image"></atomic-result-image>
                </atomic-result-section-visual>
                <atomic-result-section-excerpt>
                  <atomic-result-text field="excerpt"></atomic-result-text>
                </atomic-result-section-excerpt>
              </template>
            </atomic-result-template>
          </atomic-result-list>
        </atomic-layout-section>
        <atomic-layout-section section="pagination">
          <atomic-load-more-results></atomic-load-more-results>
        </atomic-layout-section>
      </atomic-layout-section>
    </div>

Build a facet module

The default component for facets and filters is called ProductRefinementComponent.

The preview of the request to the `cms/pages?` endpoint with the `ProductRefinementComponent` component shown

To replace it with an Atomic component, perform the similar steps as shown in the previous section.

  1. Update the src/app/app.component.html file to replace that component:

    // ...
    // previously added components
    
    <ng-template cxOutletRef="ProductRefinementComponent">
      <app-coveo-search-facet></app-coveo-search-facet>
    </ng-template>
  2. Create a coveo-search-result-refinement module and the coveo-search-facet component within it.

  3. Update the src/app/coveo-search-list/coveo-search-facet.component.html template file:

    <div class="custom-search-facets">
      <atomic-layout-section section="facets">
        <atomic-facet-manager>
          <!-- The custom field dispatched in app.component.ts -->
          <atomic-category-facet
            field="ec_category"
            label="Category"
          ></atomic-category-facet>
          <!-- The custom field dispatched in app.component.ts -->
          <atomic-facet
            field="ec_brand"
            label="Brand"
          ></atomic-facet>
          <!-- The custom field dispatched in app.component.ts -->
          <atomic-numeric-facet
            field="ec_price"
            label="Price"
          ></atomic-numeric-facet>
        </atomic-facet-manager>
      </atomic-layout-section>
    </div>

Test the application

  1. To launch the application locally, run the yarn start command. The application will open in the browser, at the http://localhost:4200 address by default.

  2. You should now have an interface powered by Coveo! Feel free to modify and experiment with it. You can add or remove components in the markup, type in requests in the search bar, use facets on the left side, or click the results.

Important

To see search results, make sure that your Coveo organization already has data indexed from an SAP Commerce Cloud instance.

Sample implementation

To see a more advanced implementation, examine the Coveo Barca demo.