Search code examples
angularserver-side-renderingangular-universalprerender

Provide token with Angular pre-render


I am looking for a way to provide tokens from the server-side when prerendering the application using the prerender script.

The issue I am facing is linked to this issue

Providing the tokens for the app does not work. I am giving the values as empty strings as the providers are expecting strings which are: (req.headers['user-agent'] and a string URL of the request)

 export function app(): express.Express {
      const server = express();
      const distFolder = join(process.cwd(), 'dist/web/browser');
      const indexHtml = existsSync(join(distFolder, 'index.original.html'))
        ? 'index.original.html'
        : 'index';
    
      // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
      server.engine(
        'html',
        ngExpressEngine({
          bootstrap: AppServerModule,
          providers: [
            {
              provide: UNIVERSAL_USER_AGENT,
              useValue: '',
            },
            {
              provide: UNIVERSAL_LOCATION,
              useValue: '',
            },
          ],
        })
      );
    
      server.set('view engine', 'html');
      server.set('views', distFolder);
    
      // Example Express Rest API endpoints
      // server.get('/api/**', (req, res) => { });
      // Serve static files from /browser
      server.get(
        '*.*',
        express.static(distFolder, {
          maxAge: '1y',
        })
      );
    
      // All regular routes use the Universal engine
      server.get('*', (req, res) => {
        res.render(indexHtml, {
          req,
          providers: [
            { provide: APP_BASE_HREF, useValue: req.baseUrl },
            provideLocation(req),
            provideUserAgent(req),
          ],
        });
      });
    
      return server;
    }
    
    function run(): void {
      const port = process.env['PORT'] || 4000;
    
      // Start up the Node server
      const server = app();
      server.listen(port, () => {
        console.log(`Node Express server listening on http://localhost:${port}`);
      });
    }

I have also provided the tokens in the server module but still, it does not pick them up.

@NgModule({
  imports: [AppModule, ServerModule, UniversalModule, FlexLayoutServerModule],
  bootstrap: [AppComponent],
  providers: [
    {
      provide: UNIVERSAL_USER_AGENT,
      useValue: '',
    },
    {
      provide: UNIVERSAL_LOCATION,
      useValue: '',
    },
  ],
})
export class AppServerModule {}

Is there a way to achieve this? I am still confused by dependency injection. The normal SSR works because I am able to provide the tokens using factories since they depend on the req object. But with the pretender, I do not have req object so I cannot use the factories. I'd appreciate any suggestions and ideas


Solution

  • I have used Scully, the app pre-renders, my issue is solved.

    To get started with Scully, you can check out this link.

    How I got it working was to install Scully using the Angular CLI. The add schematic will do some changes to your project for basic configuration, I think it adds also the scripts I have noted below.

    ng add @scullyio/init
    

    I then added scripts in my package.json file like below

    "scully": "npx scully --",
    "scully:serve": "npx scully serve --"
    

    After installing Scully, I just needed to build my app using the Angular build script: ng build and npm run scully. This pre-rendered my application without any issues.

    There was a limitation I have faced for dynamic routes where I had a Angular route config like /some-path/232 and the 232 is dynamic and unknown at build time, I have circumvented this limitation by changing that route path to /some-path and using queryParams to pass the data I needed for the route/component. I hope that helps.