Automatic Multi-Site Configuration
Every site that is defined in the CMS has its own context, which includes a base site ID, language properties, and currency properties. The context also defines how these attributes are persisted in the URL. You can allow composable storefront to automatically determine the context based on the URL patterns of your sites, as defined in the CMS. You can enable this automatic context configuration by simply not defining the context.baseSite property in spartacus-configuration.module.ts.
Before the application is initialized, composable storefront gets a list of the base sites from the back end, compares the current URL with the URL patterns of the sites that are defined in the CMS, and then identifies the current base site, along with its languages, currencies, and URL encoding attributes.
Mitigating the Initial Back End Call
The initial call to the back end for base sites can be slow, which affects the user experience. To address this, you can choose to cache the context using server-side rendering (SSR) or progressive web application (PWA) techniques.
Caching the Site Context with Server-Side Rendering
The site can be identified during server-side rendering, and the context can be transferred to the browser using the Angular TransferState mechanism. To avoid making calls for base sites on the server side with every page request, the pages can be cached using a reverse proxy.
To allow the site to be identified on the server side, you need to provide the current request URL to composable storefront. You can do this by using the composable storefront decorator of the ngExpressEngine, which provides the SERVER_REQUEST_URL injection token under the hood. You can configure this in main.server.ts, as follows:
import { ngExpressEngine as engine } from '@nguniversal/express-engine';
import { NgExpressEngineDecorator } from '@spartacus/core';
export const ngExpressEngine = NgExpressEngineDecorator.get(engine);
Caching the Back End Response with Base Sites in PWA
When using PWA, the back end response that provides the base sites can be cached by the Angular service worker, by adding a configuration to the dataGroups array in your service worker configuration. The following is an example from ngsw-config.json:
{
// ...
"dataGroups": [
// ...
{
"name": "basesites",
"urls": [
"*/rest/v2/basesites?fields=baseSites\\(uid,defaultLanguage\\(isocode\\),urlEncodingAttributes,urlPatterns,stores\\(currencies\\(isocode\\),defaultCurrency\\(isocode\\),languages\\(isocode\\),defaultLanguage\\(isocode\\)\\)*"
],
"cacheConfig": {
"maxSize": 1,
"maxAge": "1d", // Set to 1 day. Customize this value according to your needs.
"strategy": "performance"
}
}
]
}
In the example above, the maxAge shows a value of one day. You can customize this value according to your needs.
Note that you need to escape any parentheses with double backslashes \\, as shown in the urls configuration above. Also note that if you customize the endpoint URL in your application, you need to reflect this in the urls configuration.
For more information, see the official Angular service worker configuration
documentation.
Important Notes
Base Sites and Storefronts
When the base site is encoded in the URL parameters, composable storefront refers to the baseSite parameter, while the CMS refers to this parameter as a storefront. You should continue to use the storefront parameter name in the CMS, because composable storefront implicitly maps storefront to baseSite. Other parameters, such as language and currency, are not affected.
Writing URL Patterns in Java
For historical reasons, regular expressions with URL patterns that are defined in the CMS are written in Java. However, these regular expressions are evaluated on the front end using JavaScript. You should continue to use Java regex in the CMS, and they will be implicitly converted to JavaScript in composable storefront. For example, for case-insensitivity, modifiers such as (?i) are mapped to /i.
For more information, see this comparison of regular expression engines in Wikipedia
, and also look at the implementation of the JavaRegExpConverter service in composable storefront.
Disabling a Base Site
The back end endpoint returns a list of all base sites, without any information about whether the site is active or not, regardless of the options defined in the CMS, such as active, activeFrom, or activeTo. To disable a base site, you must remove the URL patterns for that base site.
As an alternative, low-level workaround, you can set restrictions for calls to the database in the back end to filter only active sites.
Local Development
When composable storefront identifies the site from the URL, it is often the case that the URL is not localhost:4200 (which is the default URL for ng serve), but something more complex, such as electronics.localhost:4200. In this case, consider the following:
-
the URL patterns defined in the CMS need to take into account that the port :4200 is also a part of the URL
-
your operating system needs to know the IP of the custom domains (such as 127.0.0.1 electronics.localhost), which you must add to your hosts file
-
the local Webpack dev server needs to allow for domains other than localhost, so you need to pass either the host flag or the disable-host-check flag to the ng serve command. Otherwise the Webpack dev server will display Invalid Host Header in the browser. The following are examples:
-
ng serve --host electronics.localhost
-
ng serve --disable-host-check
-
Adding a Custom Context
The site context that is persisted in the URL might not be just a simple ISO code of the currency or language. For example, it could be a formatted language that uses uppercase letters, or underscores instead of dashes. In this case, a custom SiteContext<T> service can be implemented. It needs to be registered in ContextServiceMap and reflected in the config of context and context.urlParameters.
The following sections describe how to implement a custom context service. In the example that follows, the ISO language code is formatted in uppercase, but you can use this technique to implement any custom site context that you wish.
Creating a Service for a Custom Context
The following is an example of creating a service for a custom context where the ISO language code is formatted in uppercase:
export function languageToCustom(lang: string): string {
return lang && lang.toUpperCase();
}
export function custom2Language(country: string): string {
return country && country.toLowerCase();
}
@Injectable({ providedIn: 'root' })
export class CustomContextService implements SiteContext<string> {
constructor(protected langService: LanguageService) {}
getActive(): Observable<string> {
return this.langService.getActive().pipe(map(languageToCustom));
}
setActive(country: string): void {
this.langService.setActive(custom2Language(country));
}
getAll(): Observable<string[]> {
return this.langService
.getAll()
.pipe(
map(languages => languages.map(lang => languageToCustom(lang.isocode)))
);
}
}
Configuring the Custom Context
You need to fill the composable storefront configuration of context.custom with all possible valid values of the custom context. How you do this depends on whether you are using Automatic Multi-Site Configuration or Static Multi-Site Configuration. Each of these scenarios is described in the following sections.
Automatic Context Configuration
If you are using automatic site configuration, can set up the composable storefront configuration of context.custom as follows:
Procedure
Static Context Configuration
If you are using static context configuration, you need to populate the context.custom with all possible valid values of the custom context. The following is an example from spartacus-configuration.module.ts:
providers: [
provideConfig({
context: { custom: ['EN', 'DE', 'JA', 'ZH'] }
})
]
Updating the Context Services Mapping
After configuring your custom context, you need to provide the ContextServiceMap that contains the custom context service. You can provide this in your app module, as shown in the following example:
export function serviceMapFactory() {
return {
[LANGUAGE_CONTEXT_ID]: LanguageService,
[CURRENCY_CONTEXT_ID]: CurrencyService,
[BASE_SITE_CONTEXT_ID]: BaseSiteService,
custom: CustomContextService,
};
}
@NgModule({
/* ... */
providers: [
{
provide: ContextServiceMap,
useFactory: serviceMapFactory,
},
// version >= 3.2
{
provide: SiteContextConfigInitializer,
useClass: CustomSiteContextConfigInitializer,
},
// version < 3.2
{
provide: OccConfigLoaderService,
useClass: CustomOccConfigLoaderService,
},
],
/*...*/
You should now be able to see your URL with an uppercase ISO language code (for example, www.site.com/EN), while still being able to use standard lowercase languages in your application.
Theme Configuration
You can add a theme to your SiteContextConfig, either automatically or statically.
Automatic Theme Configuration
You can set the theme by using the automatic site configuration, as described in the following procedure.
Procedure
Static Theme Configuration
You can statically configuring your theme, as shown in the following example:
providers: [
provideConfig({
context: { theme: ['your-theme-value'] }
})
]