Skip to content

Commit 47ba251

Browse files
Add server-side prerendering for Angular 2 template
1 parent 6d2e51b commit 47ba251

13 files changed

Lines changed: 50 additions & 134 deletions

File tree

src/Microsoft.AspNet.SpaServices/Content/Node/prerenderer.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ function loadViaTypeScript(module, filename) {
3131
// First perform a minimal transpilation from TS code to ES2015. This is very fast (doesn't involve type checking)
3232
// and is unlikely to need any special compiler options
3333
var src = fs.readFileSync(filename, 'utf8');
34-
var compilerOptions = { jsx: ts.JsxEmit.Preserve, module: ts.ModuleKind.ES2015, target: ts.ScriptTarget.ES6 };
34+
var compilerOptions = { jsx: ts.JsxEmit.Preserve, module: ts.ModuleKind.ES2015, target: ts.ScriptTarget.ES6, emitDecoratorMetadata: true };
3535
var es6Code = ts.transpile(src, compilerOptions, 'test.tsx', /* diagnostics */ []);
3636

3737
// Second, process the ES2015 via Babel. We have to do this (instead of going directly from TS to ES5) because
@@ -116,6 +116,7 @@ function renderToString(callback, bootModulePath, bootModuleExport, absoluteRequ
116116
var params = {
117117
location: url.parse(requestPathAndQuery),
118118
url: requestPathAndQuery,
119+
absoluteUrl: absoluteRequestUrl,
119120
domainTasks: domainTaskCompletionPromise
120121
};
121122

File renamed without changes.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import 'angular2-universal-preview/dist/server/universal-polyfill.js';
2+
import * as ngCore from 'angular2/core';
3+
import * as ngRouter from 'angular2/router';
4+
import * as ngUniversal from 'angular2-universal-preview';
5+
import { BASE_URL } from 'angular2-universal-preview/dist/server/src/http/node_http';
6+
import * as ngUniversalRender from 'angular2-universal-preview/dist/server/src/render';
7+
8+
// TODO: Make this ugly code go away, e.g., by somehow loading via Webpack
9+
function loadAsString(module, filename) {
10+
module.exports = require('fs').readFileSync(filename, 'utf8');
11+
}
12+
(require as any).extensions['.html'] = loadAsString;
13+
(require as any).extensions['.css'] = loadAsString;
14+
let App: any = require('./components/app/app').App;
15+
16+
export default function (params: any): Promise<{ html: string }> {
17+
return new Promise<{ html: string, globals: { [key: string]: any } }>((resolve, reject) => {
18+
const serverBindings = [
19+
ngRouter.ROUTER_BINDINGS,
20+
ngUniversal.HTTP_PROVIDERS,
21+
ngCore.provide(BASE_URL, { useValue: params.absoluteUrl }),
22+
ngCore.provide(ngUniversal.REQUEST_URL, { useValue: params.url }),
23+
ngCore.provide(ngRouter.APP_BASE_HREF, { useValue: '/' }),
24+
ngUniversal.SERVER_LOCATION_PROVIDERS
25+
];
26+
27+
ngUniversalRender.renderToString(App, serverBindings).then(
28+
html => resolve({ html, globals: {} }),
29+
reject // Also propagate any errors back into the host application
30+
);
31+
});
32+
}

templates/Angular2Spa/ClientApp/components/fetch-data/fetch-data.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as ng from 'angular2/core';
2-
import fetch from 'isomorphic-fetch';
2+
import { Http } from 'angular2/http';
33

44
@ng.Component({
55
selector: 'fetch-data'
@@ -10,12 +10,10 @@ import fetch from 'isomorphic-fetch';
1010
export class FetchData {
1111
public forecasts: WeatherForecast[];
1212

13-
constructor() {
14-
fetch('/api/SampleData/WeatherForecasts')
15-
.then(response => response.json())
16-
.then((data: WeatherForecast[]) => {
17-
this.forecasts = data;
18-
});
13+
constructor(http: Http) {
14+
http.get('/api/SampleData/WeatherForecasts').subscribe(result => {
15+
this.forecasts = result.json();
16+
});
1917
}
2018
}
2119

templates/Angular2Spa/ClientApp/components/home/home.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ <h1>Hello, world!</h1>
99
<p>To help you get started, we've also set up:</p>
1010
<ul>
1111
<li><strong>Client-side navigation</strong>. For example, click <em>Counter</em> then <em>Back</em> to return here.</li>
12+
<li><strong>Server-side prerendering</strong>. For faster initial loading and improved SEO, your Angular 2 app is prerendered on the server. The resulting HTML is then transferred to the browser where a client-side copy of the app takes over.</li>
1213
<li><strong>Webpack dev middleware</strong>. In development mode, there's no need to run the <code>webpack</code> build tool. Your client-side resources are dynamically built on demand. Updates are available as soon as you modify any file.</li>
1314
<li><strong>Hot module replacement</strong>. In development mode, you don't even need to reload the page after making most changes. Within seconds of saving changes to files, your Angular 2 app will be rebuilt and a new instance injected is into the page.</li>
1415
<li><strong>Efficient production builds</strong>. In production mode, development-time features are disabled, and the <code>webpack</code> build tool produces minified static CSS and JavaScript files.</li>

templates/Angular2Spa/Views/Home/Index.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
ViewData["Title"] = "Home Page";
33
}
44

5-
<app>Loading...</app>
5+
<app asp-prerender-module="ClientApp/boot-server">Loading...</app>
66

77
@section scripts {
88
<script src="~/dist/main.js" asp-append-version="true"></script>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
@using WebApplicationBasic
22
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
3+
@addTagHelper "*, Microsoft.AspNet.SpaServices"

templates/Angular2Spa/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,15 @@
2525
},
2626
"dependencies": {
2727
"angular2": "^2.0.0-beta.7",
28+
"angular2-universal-preview": "^0.55.4",
2829
"babel-core": "^6.5.2",
30+
"css": "^2.2.1",
31+
"domain-task": "^1.0.0",
2932
"es6-shim": "^0.33.13",
3033
"es7-reflect-metadata": "^1.5.5",
3134
"isomorphic-fetch": "^2.2.1",
35+
"parse5": "^1.5.1",
36+
"preboot": "^1.1.3",
3237
"reflect-metadata": "^0.1.2",
3338
"rxjs": "^5.0.0-beta.2",
3439
"zone.js": "^0.5.15"

templates/Angular2Spa/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"moduleResolution": "node",
44
"target": "es6",
55
"sourceMap": true,
6-
"experimentalDecorators": true
6+
"experimentalDecorators": true,
7+
"emitDecoratorMetadata": true
78
},
89
"exclude": [
910
"node_modules"

templates/Angular2Spa/tsd.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77
"installed": {
88
"requirejs/require.d.ts": {
99
"commit": "dade4414712ce84e3c63393f1aae407e9e7e6af7"
10-
},
11-
"isomorphic-fetch/isomorphic-fetch.d.ts": {
12-
"commit": "4d2d0653003a4d1df4d7c68054cce4f4ace58bdf"
1310
}
1411
}
1512
}

0 commit comments

Comments
 (0)