Disclaimer: This blog post will be focused on step by step tutorial of how to deploy a Universal App using Hosting. For any explanations about Angular Universal and Server Side Rendering here are some helpful resources: Angular Firebase Angular Universal Server Side Rendering You can find the source code if you want to follow along. here Requirements (I am using v8.3.0 for this tutorial) node.js Part I: Build an angular app (browser and server versions) šØ 1. Install global dependencies We are going to use and in the command line to build and deploy your application. @angular/cli firebase-tools @angular/cli ā„ v1.3.0 firebase-tools v3.9.2 $ npm install --global @angular/cli firebase-tools 2. Create a new angularĀ project Using we are going to create a new app. In this case, I will name it . @angular/cli angular angular-universal-firebase $ ng new angular-universal-firebase$ cd angular-universal-firebase 3. Install @angular/platform-server To build and render your app, we need to install . universal @angular/platform-server $ npm install --save @angular/platform-server 4. A S configuration dd erver Side Rendering We are basically copying the default configuration and modifying it for the server side rendering (SSR) configuration. For the SSR configuration, we donāt need to add (since the code will run in a server instead of a browser) and (since this will be added when we reference the main when building the server HTML through . , we added which is a feature introduced in . This will allow us to build a SSR version of your app using . app polyfill node styles index.html @angular/platform-server Also note "platform": "server" @angular/cli#v1.3.0 @angular/cli // .angular-cli.json{...apps: [{ /* default config */ },**{"name": "ssr","root": "src","outDir": "functions/dist-ssr","assets": ["assets", "favicon.ico"],"index": "index.html","main": "main-ssr.ts","test": "test.ts","tsconfig": "tsconfig.app-ssr.json","prefix": "app","scripts": [],"environmentSource": "environments/environment.ts","environments": {"dev": "environments/environment.ts","prod": "environments/environment.prod.ts"},"platform": "server"} **],...} 5. Create necessary files for app serverĀ version Create a new module for the appās version. src/app/app.server.module.ts server // src/app/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: [ServerModule,AppModule],bootstrap: [AppComponent]}) export class AppServerModule { } Create entry point for the module. This is file we referenced in the server version of the app in . src/main-ssr.ts server main .angular-cli.json // src/main-ssr.ts export { AppServerModule } from './app/app.server.module'; Create the for the server version. Similar to the browser version except for which will reference the entry module for the server version that we just created. This is also referenced in configuration as . src/tsconfig.app-ssr.json tsconfig angularCompilerOptions.entryModule .angular-cli.json tsconfig // src/tsconfig.app-ssr.json {"extends": "../tsconfig.json","compilerOptions": {"outDir": "../out-tsc/app","baseUrl": "./","module": "commonjs","types": []},"exclude": ["test.ts","**/*.spec.ts"],"angularCompilerOptions": {"entryModule": "app/app.server.module#AppServerModule"}} 6. Include server transition in appās browserĀ module Since we are sending the server version of your app to the browser before the browser version, we need to add call when adding in of the browser module of the app. .withServerTransition() BrowserModule imports // src/app/app.module.ts import { BrowserModule } from '@angular/platform-browser';import { NgModule } from '@angular/core';import { AppComponent } from './app.component'; const APP_ID = 'angular-universal-firebase'; @NgModule({declarations: [AppComponent],imports: [ ],providers: [],bootstrap: [AppComponent]}) BrowserModule.withServerTransition({ appId: APP_ID }) export class AppModule { } Now we are ready to build the server and browser versions of the app! 7. Build browser and server versions of theĀ app Using the , we will build the two versions of the app.ā¢ : will build the browser version with configurations (i.e. minified html/js/css, aot, etc.)ā¢ : will build the server version. It will generate an file that we can use to render the app in . @angular/cli ng build -prod prod ng build -aot -app ssr ngFactory node $ ng build -prod$ ng build -aot -app ssr When both builds are finished, you should now have a folder in your directory and inside your directory. Hooray! š dist root dist-ssr functions Part II: Deploying with FirebaseĀ š [1] Before continuing, you should have had created a firebase project here . I named mine _angular-universal-firebase_ for this case. 1. Log in to ` ` in the commandĀ line firebase Log in to firebase in the command line with the same google account you used to create your firebase project in [1]. $ firebase login 2. Initialize Firebase in the `angular` project Initialize firebase configurations through the command line: $ firebase init Select and for features to set up Functions Hosting Firebase setup configuration Select the firebase project you created in [1]. (In my case, itās . angular-universal-firebase Accept all defaults in this stage; we will configure the rest in later steps. 3. Add package dependencies to `functions` Since we are using a node server through , We need to include dependencies in to render the server version of the app. firebase-functions angular functions/package.json Aside: Right now, I donāt know any way to mitigate this duplication of dependency declaration since as far as I know, you canāt access files outside the _functions_ directory in any _firebase-functions_ javascript files. But if you know a way, please let me know! // functions/package.json{"name": "functions","description": "Cloud Functions for Firebase","dependencies": { "firebase-admin": "~4.2.1","firebase-functions": "^0.5.7", },"private": true} "@angular/animations": "^4.3.5","@angular/common": "^4.3.5","@angular/compiler": "^4.3.5","@angular/core": "^4.3.5","@angular/forms": "^4.3.5","@angular/http": "^4.3.5","@angular/platform-browser": "^4.3.5","@angular/platform-server": "^4.3.5","express": "^4.15.4", "rxjs": "^5.4.3","zone.js": "^0.8.16" 4. Install packages in ` ` directory functions Install da dependencies! # in project root $ npm --prefix functions install or # in `functions` directory $ npm install 5. Copy ` ` folder to ` ` dist functions/dist Since you cannot access files outside of the directory in , we have to copy the directory inside the directory so we can access it in . functions firebase-functions dist functions firebase-functions dist should now exist in root folder AND functions folder 6. Create Firebase function to serve theĀ app Weāre going to use Firebase function type to send the response from an express server. There are a lot of things going on in this file but the most notable are: functions.https.onRequest Importing which was generated from . AppServerModuleNgFactory Part I: Step 7āāāserver version Creating an variable which is getting the file we generated from index index.html Part I: Step 7āāābrowser version. Using to generate an file that we send as a response with and parameters.Ā ā¢ parameter determines which route of the app is going to be rendered. Specifying this allows to build the of that route.ā¢ is the full document HTML of the page to render. In this case, it will be the browser version of the app. renderModuleFactory html url document url renderModuleFactory html document index.html // functions/index.js require('zone.js/dist/zone-node'); const functions = require('firebase-functions');const express = require('express');const path = require('path');const { enableProdMode } = require('@angular/core');const { renderModuleFactory } = require('@angular/platform-server'); const { AppServerModuleNgFactory } = require('./dist-ssr/main.bundle'); enableProdMode(); const index = require('fs').readFileSync(path.resolve(__dirname, './dist/index.html'), 'utf8').toString(); let app = express(); app.get('**', function( , ) {renderModuleFactory(AppServerModuleNgFactory, {url: req.path,document: index}).then( => res.status(200).send(html));}); req res html exports.ssr = functions.https.onRequest(app); 7. Configure FirebaseĀ hosting Now that we have built the function to render pages, we need to change the firebase hosting configuration to use this function. We also need to change the directory to use the directory to access your assets. public dist // firebase.json{ "hosting": { **"public": "dist", "rewrites": \[{ "source": "\*\*", "function": "ssr" }\]** } } 8. Delete ` ` from root directory dist/index.html This is so Firebase wonāt serve the file but rather run the function. : delete from the directory. . html ssr NOTE dist/index.html root DO NOT DELETE functions/dist/index.html $ rm dist/index.html 9. Deploy to Firebase šĀ š„ If all things went well, you should be able to deploy your app to Firebase: $ firebase deploy Thatās it!Ā š You can check out the source code . here I hope this tutorial was helpful in some way! I would love to hear any feedback or questions if you have any! ā¢ (Only one route handling) Other helpful resourcesĀ ā¢ Creating an Angular Universal app with the Angular CLI Angular Universal with Firebase Dynamic Hosting
Share Your Thoughts