node.jsangularangular-ssr

Server rendering with Angular SSR never returns any html


I am trying to get Angular SSR to work on my web app. Everything seems to work just that the html rendered on my server seems to never be served.

Here is my server.ts:

import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine } from '@angular/ssr';
import express from 'express';
import { fileURLToPath } from 'node:url';
import { dirname, join, resolve } from 'node:path';
import bootstrap from './src/main.server';
import { TransferState } from '@angular/core';

export function app(): express.Express {
  const server = express();
  const serverDistFolder = dirname(fileURLToPath(import.meta.url));
  const browserDistFolder = resolve(serverDistFolder, '../browser');
  const indexHtml = join(serverDistFolder, 'index.server.html');
  const distFolder = join(process.cwd(), 'dist/appeningsweb/browser');

  const commonEngine = new CommonEngine();

  server.set('view engine', 'html');
  server.set('views', browserDistFolder);

   // Serve static files from /browser
   server.use('/assets', express.static(join(distFolder, 'assets'), {
    maxAge: '1y'
  }));

  // Serve static files from /browser
  server.use(express.static(distFolder, {
    maxAge: '1y',
    index: false
  }));
  
  // All regular routes use the Angular engine
  server.get('*', (req, res, next) => {
    const { protocol, originalUrl, baseUrl, headers } = req;


    console.log("Original url");
    console.log(originalUrl);

    commonEngine
    .render({
      bootstrap,
      documentFilePath: indexHtml,
      url: `${protocol}://${headers.host}${originalUrl}`,
      publicPath: browserDistFolder,
      providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }, { provide: TransferState, useValue: {} }],
    })
    .then((html) => res.send(html))
    .catch((err) => next(err));

  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 23 listening on http://localhost:${port}`);
  });
}

run();

And here is my view that i want to be rendered with SSR.

import { Component, Inject, OnInit, PLATFORM_ID, TransferState, makeStateKey } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { DataService } from '../../services/data.service';
import { catchError } from 'rxjs';
import { Meta, Title } from '@angular/platform-browser';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';

const EVENT_DATA_KEY = makeStateKey<any>('eventData');

@Component({
  selector: 'event-details',
  templateUrl: './event-details.component.html',
  styleUrls: ['./event-details.component.scss']
})
export class EventDetailsComponent implements OnInit {
  eventData: any = undefined;

  constructor(
    private dataService: DataService,
    private route: ActivatedRoute,
    private meta: Meta,
    private titleService: Title,
    @Inject(PLATFORM_ID) private platformId: any,
    private transferState: TransferState
  ) {}

  ngOnInit(): void {
    this.route.queryParams.subscribe((params: any) => {
        if(isPlatformServer(this.platformId)) {
            console.log("Is server rendered aqui");
            const token = params['token'];

            if (!token) {
              return;
            }
    
            this.dataService.get(`/api/events/webappeventdetails?token=${token}`).pipe(
              catchError((err) => {
                throw err;
              })
            ).subscribe((res: any) => {
              console.log("Event data");
              this.eventData = res;
              this.updateMetaTags(res);
              this.transferState.set(EVENT_DATA_KEY, res);
            });
        }
        if(isPlatformBrowser(this.platformId)) {
            console.log("Is browser rendered aqui");
            console.log(EVENT_DATA_KEY);
            this.eventData = this.transferState.get<any>(EVENT_DATA_KEY, null);
            if(this.eventData){
                console.log("settings eventdata");
                this.updateMetaTags(this.eventData);
            }            
        }       
      });
  }

  updateMetaTags(eventData: any): void {
    this.titleService.setTitle(eventData.title);
    this.meta.updateTag({ name: 'description', content: this.stripHtml(eventData.description.substring(0, 400) + '...') });
    this.meta.updateTag({ property: 'og:title', content: eventData.title });
    this.meta.updateTag({ property: 'og:description', content: this.stripHtml(eventData.description.substring(0, 400) + '...') });
    this.meta.updateTag({ property: 'og:image', content: eventData.picture });
    this.meta.updateTag({ property: 'og:url', content: eventData.url });
    this.meta.updateTag({ property: 'og:type', content: "website" });
  }

  stripHtml(html: string): string {
    return html.replace(/<[^>]*>/g, '');
  }
}

I can see in my chrome console that the logs for when it runs the browser code is working, it writes out this "Is browser rendered aqui". I'm expecting to see the server logs in my visual studio console terminal but this "Is server rendered aqui" message is never rendered. I can, however, see the logs in my terminal from my server.ts file. I can see the full request there and everything seems normal there. What am i missing here?

Thanks

I've tried a lot of different loggins etc etc


Solution

  • Currently (v18), the server.ts is not used in dev mode (when using the Vite devserver).

    This is currently being tracked by this issue. Also expect some changes following this RFC.