Server-side rendering in ASP.NET Core Angular
By: Pieterjan De Clippel
Tags: Server Side Rendering, Angular8, Aspnetcore

There is an update on the story (angular 9): https://medium.com/@pieterjandeclippel/server-side-rendering-in-asp-net-core-angular-update-705c0ec01931

I’ve been trying to get Server-side rendering working in the Angular template for ASP.NET Core. I’m a developer, not a storyteller. So I’ll just jump right into it.

SSR is especially interesting for SEO purposes, or when visitors disable javascript, and you don’t want to limit the page for the user to the noscript tag.

Start by creating a new ASP.NET Core project. Choose the Angular template

You can already run the application. But when you look at the source code, you’ll see that there’s pretty much nothing on the page. The browser of the visitor renders the app. This is not good when SEO is important.

There is no content in the app-root tag

How do I add SSR

Go to the Solution Explorer and right-click the project file. Choose Edit project file from the context menu and set BuildServerSideRenderer to true:

Setting this flag will cause ASP.NET Core to trigger the ng build and ng build:ssr commands. When these commands succeed, a browser and server folder is generated in the ClientApp/dist folder.

Next, remove the ClientApp folder from the project and create a new project:

cd SpaPrerendering

ng new ClientApp --style=scss --routing

Add the express engine of Angular Universal to the project:



ng add cd ClientAppng add @nguniversal/express-engine --clientProject ClientApp

This command generates the AppServerModule, main.server, tsconfig.server.json and tsconfig.app.json in the project.

Install the aspnet-prerendering node package:

npm install --save aspnet-prerendering

Add the SPA prerendering middleware. I removed the unnecessary parts of the code:

To make the angular app behave the same way for ASPNETCORE_ENVIRONMENT=Development or Production:

Open the ClientApp/angular.json file

Locate the projects:ClientApp:architect:build:options:outputPath property

property Change the value from dist/browser to just dist

to just This will cause the app to behave the same regardless of whether ASPNETCORE_ENVIRONMENT is set to Development or Production

Select all 4 tsconfig files in the ClientApp folder, go to the properties window and modify the Build Action to Content.

Modify the following tsconfig files:

Add node to the types

Add node to the types and set module to commonjs

In case you’re using providers, modify the main.ts (ClientApp/src/main.ts):

Also modify the main.server.ts: (ClientApp/src/main.server.ts):

Now when you’d try to run the project, you’ll probably get the following error:

NodeInvocationException: Prerendering failed because of error: Error: Cannot find module SpaPrerendering\ClientApp\dist\server\main.js

This is because the app already starts to run before the Angular CLI server has finished building the app server-side. The ClientApp/dist/server folder simply doesn’t exist yet. Wait until the folder exists and run the project again.

And when we run again…

The final project is available on GitHub

Pass data from .NET to the Angular CLI during SSR

When you want to render a page that for example displays the information of a person, you’ll have to pass the person from ASP.NET to the angular app during the Server-side prerendering process.

Start by editing the Startup file:

Add the SupplyData command

Modify the main.ts. Provide some message value:

Modify the main.server.ts. Pass the value from the server to the provider:

Inject the message-provider in the AppComponent:

Now you can display the message on the AppComponent’s view:

You can pass any serializable object (like a person) to the SupplyData delegate, and use this object in the component’s view.

Result received from the server

Result after the browser loaded the Angular app

Errors and pitfalls

NodeInvocationException: Cannot destructure property `AppServerModule` of ‘undefined’ or ‘null’

Cause

Your tsconfig.server.json is incorrect: missing “compilerOptions:module” declaration

Your tsconfig.server.json is incorrect: missing “types” declaration

Possible solution

Open your tsconfig.server.json. Check if compilerOptions:module is set to commonjs. Check if compilerOptions:types is set to [ “node” ]:

NodeInvocationException: Prerendering failed because of error: Error: Cannot find module …

Cause:

You haven’t set the BuildServerRenderer flag in the project file

The server build hasn’t finished yet. Wait until the file ClientApp/dist/server/main.js exists

You misspelled the path to your server-main module

Solution:

Right click your project, choose Edit Project File and check if you have set <BuildServerSideRenderer>true</BuildServerSideRenderer>

and check if you have set <BuildServerSideRenderer>true</BuildServerSideRenderer> Check if the file exists and sorta contains following first line `(function(e, a) { for(var i in a) e[i] = a[i]; }(exports, (function(modules) {`. This is the file you need as BootModule.

NodeInvocationException: Prerendering failed because of error: TypeError: provideModuleMap is not a function

Cause:

You may have picked the wrong server.js file as BootModulePath.

Solution: