Injecting HTTP Request and Response

109 Views Asked by At

My angular app is running using server side rendering with Angular 17. Since location object is not available for redirecting, I am trying to inject request and response objects in to the app. I have implemented one redirect service which is given below.

import { Injectable, InjectionToken, Inject, PLATFORM_ID } from '@angular/core';  
import { Router } from '@angular/router';   
export const REQUEST = new InjectionToken<Request>('REQUEST');
export const RESPONSE = new InjectionToken<Response>('RESPONSE'); 
import { Response } from 'express';
import { isPlatformBrowser, isPlatformServer, DOCUMENT } from '@angular/common';
import { TransferState, makeStateKey } from '@angular/platform-browser';

@Injectable({
  providedIn: 'root'
})
export class RedirectService {
    constructor(
      @Inject(RESPONSE) private response: Response,
      @Inject(PLATFORM_ID) private platformId: Object,
      @Inject(DOCUMENT) private document: any,
      private transferState: TransferState
    ) {} 
    redirectToUrl(url: string): void {
      if (isPlatformBrowser(this.platformId)) {
        // Running in the browser
        window.location.href = url;
      } else {
        // Running on the server
        this.response.redirect(301, url);
      }
    }
    isSSR() {
      return isPlatformServer(this.platformId)
    }
    reload(): void {
      if (isPlatformBrowser(this.platformId)) {
        // Running in the browser
        window.location.reload();
      } else if (isPlatformServer(this.platformId)) {
        // Running on the server
  
        // Inject a script into the response that triggers a reload on the client side
        const script = `<script>document.location.reload();</script>`;
        const key = makeStateKey<string>('reloadScript');
        this.transferState.set(key, script);
  
        // Send a response with the script to trigger the reload
        this.response.send(this.document.toString());
      }
    }
    public redirect(url: string, router: Router) {  
      router.navigate(!Array.isArray(url)? [url]: url) 
    }  
}

But when run on browser without SSR it is showing the following error in browser console.

ERROR NullInjectorError: R3InjectorError(AppModule)[RedirectService -> InjectionToken RESPONSE -> InjectionToken RESPONSE]: 
  NullInjectorError: No provider for InjectionToken RESPONSE!

I tried some solutions which asks to inject the Response object from the module file. One such solution is given below.

import { APP_ID, NgZone } from '@angular/core';
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';
import { SERVER_PLATFORM_PROVIDERS, PlatformService, INITIAL_CONFIG, platformDynamicServer } from '@angular/platform-server';
import { DOCUMENT } from '@angular/common';
import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens';

// ...

providers: [
  // ...

  { provide: PLATFORM_ID, useValue: 'server' },
  { provide: APP_ID, useValue: 'my-app-id' },
  { provide: DOCUMENT, useFactory: (doc: any) => doc || {}, deps: [NgZone] },
  { provide: INITIAL_CONFIG, useFactory: (doc: any) => ({ document: doc }), deps: [DOCUMENT] },
  { provide: PlatformService, deps: [DOCUMENT] },
  { provide: REQUEST, useFactory: (req: any) => req, deps: [] },
  { provide: RESPONSE, useFactory: (res: any) => res, deps: [] },

  // ...
]

But since I am using Angular 17 and SSR, @nguniversal/express-engine/tokens is no longer available.

How can this be solved. How to inject request and response objects to the component via module.

1

There are 1 best solutions below

0
On

use nodemon and compile project

add this to "scripts" on package.json

"build:dev": "ng build --configuration development && node dist/{{YOUR_PROJECT}}/server/server.mjs",      
"dev": "nodemon --watch server.ts --watch src/ -e ts,html,scss  --exec \"npm run build:dev\"",