In this post I will show how to use Angular to render server side templates.

A key win of the Angular architecture is that the framework is not tied to a particular runtime platform.

While browsers still remain the most typical platform for Angular applications, it’s easy to execute Angular code on other runtimes. One popular option is server side rendering.

In this post I will show how to use Angular Universal to render pure server side templates in Node/Express.

The typical use case for Angular Universal is to combine server and client side rendering to improve SEO and load performance of JavaScript applications.

In my application I am less interested in the client side part.

Instead I will focus on the server side part since I want to use Angular to replace some of my existing Mustache templates.

The feature I want to convert is my Angular article dashboard.

Express Integration

Since all my existing server templates use Mustache, I had to find a way to allow Angular and Mustache to coexist in the same application.

The solution I ended up with was to create a sub application in Express where I configured Angular as the view engine. In the main app the view engine is still Mustache.

Below is my sub Express application:

import * as express from 'express'; import {enableProdMode} from '@angular/core'; let app: any = express(); import * as path from 'path'; const VIEW_FOLDER = path.join(process.cwd(), 'angular-server'); import 'zone.js/dist/zone-node'; import 'reflect-metadata'; import {ngExpressEngine} from '@nguniversal/express-engine'; import {AppServerModuleNgFactory} from './app.server.module.ngfactory'; enableProdMode(); app.engine('html', ngExpressEngine({ bootstrap: AppServerModuleNgFactory, })); app.get("/", function(req, res){ res.render(path.join(VIEW_FOLDER, 'index.html'), {req}); }); export const APP = app;

Back in the main application I can now import the sub app and register it like so:

const ANGULAR = require('./angular-server/server'); app.use('/angular-2-articles', ANGULAR.APP);

Now, any request to /angular-2-articles will be routed to the sub application.

Not only is this approach great for supporting multiple view engines, it also makes my application more modular.

Angular Integration

From here on out we’re pretty much just dealing with standard Angular code.

I have included the relevant Angular code below:

app.server.module.ts
import {NgModule} from '@angular/core'; import {ServerModule} from '@angular/platform-server'; import {AppModule} from './app.module'; import {AppComponent} from './app.component'; @NgModule({ imports: [ AppModule, ServerModule ], bootstrap: [ AppComponent ], }) export class AppServerModule {}
app.module.ts
import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {BrowserModule} from '@angular/platform-browser'; import {AppComponent} from './app.component'; @NgModule({ imports: [BrowserModule.withServerTransition({appId: 'angular-samples'}), CommonModule], declarations: [AppComponent] }) export class AppModule {}
app.component.ts
import {Component} from '@angular/core'; import {CHAPTERS} from './chapters'; import 'rxjs/add/operator/map'; import {HttpClient} from '@angular/common/http'; @Component({ templateUrl: './app.component.html', selector: 'app-component' }) export class AppComponent { chapters; constructor(private http: HttpClient) {} ngOnInit() { this.chapters = this.http .get('http://www.some-url') .map(articles => [...articles.angular, ...articles.javascript]) .map(articles => { return CHAPTERS.map(chapter => { const article = articles.find(a => a.friendlyUrl === chapter.key); chapter.intro = article.intro; return chapter; }); }); } }
app.component.html
<div class="row"> <div *ngFor="let chapter of chapters | async" class="card col-sm-4"> <h3>{{chapter.title}}</h3> <div><strong>Author:</strong> <a href="/bio">Torgeir Helgevold</a></div> <p>{{chapter.intro}}</p> <div class="card-footer"> <div> <a href="/viewarticle/{{chapter.key}}">Read Full Article</a> </div> <div *ngIf="chapter.liveDemo"> <div><a href="http://www.syntaxsuccess.com/angular-2-samples{{chapter.liveDemo}}">Live Demo</a></div> </div> </div> </div> </div>

You may have noticed that for server side applications we import BrowserModule.withServerTransition, but otherwise most of the code is familiar Angular code.

What I really like about this is that we can use most standard Angular functionality – even the async pipe works.

It’s also very easy to implement complex template logic since you have full access to Angular template directives like ngIf and ngFor.

You do however have to stay away from browser specific api functions (e.g. DOM functions).

Demo

I was very happy to see how easy it was to convert my existing Mustache solution to Angular server templates. You can see the final version here.

Going forward I might even convert more of my templates.