1

I'm working on an SSR project and trying to get the users request headers sent to the node server to parse the accepted language header param inside an angular resolver.

What Im trying to do is redirect the users root route to a preferred language setting, either accepted-lang headers or the default lang.

When Im trying to inject the Request instance, its always null/undefined

The DefaultLang Resolver:

@Injectable({ providedIn: 'root' })
export class DefaultlanguageResolver implements Resolve<boolean> {
  translocoService = inject(TranslocoService);
  navigationService = inject(NavigationService);
  request = inject(Request, { optional: true });
  appConfig = inject(APP_CONFIG);

  resolve(
    _route: ActivatedRouteSnapshot,
    _state: RouterStateSnapshot
  ): boolean | Observable<boolean> | Promise<boolean> {

    // this.request is always null/undefined

    const headerLang = this.request?.headers
      ? this.request.headers.get('accept-language')
      : null;

    if (
      headerLang &&
      this.appConfig.availableLangs.find((lang) => lang === headerLang)
    ) {
      this.translocoService.setActiveLang(headerLang);
    } else {
      this.translocoService.setActiveLang(
        this.translocoService.getDefaultLang()
      );
    }

    this.navigationService.navigateToRoot();
    return true;
  }
}

My routes:

export const AppRoutes: Routes = [
  {
    path: '',
    pathMatch: 'full',
    resolve: [DefaultlanguageResolver],
    // Required component property
    component: LayoutComponent,
  },
  {
    path: ':lang',
    resolve: [LanguageResolver],
    component: LayoutComponent,
    children: [...]
  }
]
Don
  • 366
  • 1
  • 10

1 Answers1

0

TLDR;


import {isPlatformServer} from "@angular/common";
import {Request} from "express";
import {REQUEST} from "@nguniversal/express-engine/tokens";

// ...

constructor(
    @Inject(PLATFORM_ID) platformId: any,
    @Optional() @Inject(REQUEST) request: Request | null) {
    if (isPlatformServer(platformId)) {
        request?.header('Accept-Language');
        // request.acceptsLanguages() // Good utility for language preferences
    }
}

SEASON 01

Angular being design to be platform agnostic (okay, to some extent), you either render Angular pages using:

A quick thinkering of these wordings and the Angular architecture reveals that Angular still need environment specifics to come around rendering, and deals with differences between browsers but also servers. Hence the need for:

See for yourself in the source code how on the server, @nguniversal/express-engine provides us with token/value for REQUEST and RESPONSE...

function getReqResProviders(req: Request, res?: Response): StaticProvider[] {
  const providers: StaticProvider[] = [
    {
      provide: REQUEST,
      useValue: req,
    },
  ];
  if (res) {
    providers.push({
      provide: RESPONSE,
      useValue: res,
    });
  }

  return providers;
}

...during options configuration of the Express view engine.

renderOptions.providers = [...(renderOptions.providers ?? []), getReqResProviders(req, res)];

NOTE: And, because you're likely rendering your app in other environments, consider the Optional() injection modifier, and the type signature, and the condition, to gracefully handle everything.

Here is the documentation about the Optional modifier, for further readings on it.

Salathiel Genese
  • 1,639
  • 2
  • 21
  • 37
  • Ok. But this will only work if the angular app is hosted directly through `start:ssr` and not through eg. ASP.NET Core – Pieterjan Jun 11 '23 at 19:26
  • Well, (1) the OP wasn't ASP-oriented, (2) I run `dev:ssr` on local and NodeJS/Docker in production, (3) I am not a reference when it comes to ASP, (4) ASP support for for Angular SSR seems deprecated - https://www.npmjs.com/package/@nguniversal/aspnetcore-engine , (5) ASP/SSR intergation seems to be a wrap up that invokes NodeJS under the hood as mentioned here - https://trilon.io/blog/angular-universal-server-side-rendering-deep-dive#aspnet-core-angular-universal-integration - and demonstrated in the `Dockerfile` of the repository a few lines below that last reference. – Salathiel Genese Jun 11 '23 at 23:41