angularsession-storageangular-providersangular-standalone-components

Angular 17: mergeApplicationConfig in app.config.server.ts results in initialization of providers imported in app.config.ts


A standalone app generated with Angular 17 CLI imports the app.config.ts into the app.config.server.ts and merges the 2 configurations by default.

This results in all providers imported in app.config.ts to also be imported into app.config.server.ts and hence to be instantiated on the server.

If a provider needed by the client uses sessionStorage inside the constructor, this results in an error, because sessionStorage is not defined on the server. E.g. like this:

export class MyStoreItem extends StoreItem <Stuff> {
  constructor() {
    const storedStuff: any = sessionStorage.getItem('stuff');
  }
}

I have tried removing the import of app.config.ts from app.config.server.ts, as well as the call to mergeApplicationConfig(appConfig, serverConfig). This fixes the issue and the app seems to work fine.

My question is, is it necessary to merge the 2 configurations in app.config.server.ts, and if so why?

And secondly, if it is necessary, how do I fix this issue without checking inside the provider's constructor whether it is called from server or browser, since it should actually never be called by the server.

Thank you!


Solution

  • Is it necessary to merge the 2 configurations in app.config.server.ts, and if so why?

    Although few dependencies may vary (SessionStorage like you said) few are common for both (E.g: provideHttpClient(), provideRouter([])) It will be tedious to maintain duplicate code at two locations, hence we have mergeApplicationConfig(appConfig, serverConfig) to help you maintain the common dependencies better.

    if it is necessary, how do I fix this issue without checking inside the provider's constructor whether it is called from server or browser, since it should actually never be called by the server.

    The application will still work when you remove all of this, just that code will be duplicated in two places which you need to maintain, even if you remove the dependencies from app.config.server.ts there are still imports in the components which are shared by both the client and server code.

    So it's a must to have code blocks which might error out on the server to use isPlatformBrowser to stop execution in the server.

    Example of using isPlatformBrowser:

    import { isPlatformBrowser } from '@angular/common';
    import { PLATFORM_ID } from '@angular/core';
    import { Component, Inject } from '@angular/core';
    
    export class MyStoreItem extends StoreItem <Stuff> {
        isBrowser: boolean;
    
        constructor( @Inject(PLATFORM_ID) platFormId: Object) {
            this.isBrowser = isPlatformBrowser(platFormId);
            let storedStuff: any;
            // run code only on browser!
            if(this.isBrowser) {
                storedStuff = sessionStorage.getItem('stuff');
            }
        }
    }