From a9f1eac36750e8dbdad0fd9849e34792fab67bef Mon Sep 17 00:00:00 2001 From: Muhammad Ahsan Ayaz Date: Sun, 8 Dec 2024 06:58:42 +0500 Subject: [PATCH] chore(angular): add ssr --- angular.json | 2 +- package.json | 2 +- projects/demo/src/app/app.component.ts | 5 +- projects/demo/src/app/app.config.server.ts | 11 ++++ projects/demo/src/app/app.config.ts | 9 +++ projects/demo/src/app/app.module.ts | 18 ------ projects/demo/src/app/pipes/keys.pipe.ts | 5 +- projects/demo/src/app/server.ts | 32 ----------- projects/demo/src/main.server.ts | 7 +++ projects/demo/src/main.ts | 19 ++++++- projects/demo/src/server.ts | 65 ++++++++++++++++++++++ projects/demo/tsconfig.app.json | 3 +- 12 files changed, 117 insertions(+), 61 deletions(-) create mode 100644 projects/demo/src/app/app.config.server.ts create mode 100644 projects/demo/src/app/app.config.ts delete mode 100644 projects/demo/src/app/app.module.ts delete mode 100644 projects/demo/src/app/server.ts create mode 100644 projects/demo/src/main.server.ts create mode 100644 projects/demo/src/server.ts diff --git a/angular.json b/angular.json index 74ee38c..37e19eb 100644 --- a/angular.json +++ b/angular.json @@ -61,7 +61,7 @@ "server": "projects/demo/src/main.server.ts", "prerender": true, "ssr": { - "entry": "projects/demo/server.ts" + "entry": "projects/demo/src/server.ts" } }, "configurations": { diff --git a/package.json b/package.json index 87efb0d..e10cea2 100644 --- a/package.json +++ b/package.json @@ -128,4 +128,4 @@ "@angular/common": "$@angular/common" } } -} +} \ No newline at end of file diff --git a/projects/demo/src/app/app.component.ts b/projects/demo/src/app/app.component.ts index 037cb13..e86f2bc 100644 --- a/projects/demo/src/app/app.component.ts +++ b/projects/demo/src/app/app.component.ts @@ -1,11 +1,14 @@ import { Component, VERSION } from '@angular/core'; import { DeviceDetectorService } from 'ngx-device-detector'; +import { NgClass, NgIf, NgFor } from '@angular/common'; +import { KeysPipe } from './pipes/keys.pipe'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], - standalone: false + imports: [NgClass, NgIf, NgFor, KeysPipe], + standalone: true }) export class AppComponent { propsToShow = ['userAgent', 'os', 'browser', 'device', 'os_version', 'browser_version', 'deviceType', 'orientation']; diff --git a/projects/demo/src/app/app.config.server.ts b/projects/demo/src/app/app.config.server.ts new file mode 100644 index 0000000..b4d57c9 --- /dev/null +++ b/projects/demo/src/app/app.config.server.ts @@ -0,0 +1,11 @@ +import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; +import { provideServerRendering } from '@angular/platform-server'; +import { appConfig } from './app.config'; + +const serverConfig: ApplicationConfig = { + providers: [ + provideServerRendering() + ] +}; + +export const config = mergeApplicationConfig(appConfig, serverConfig); diff --git a/projects/demo/src/app/app.config.ts b/projects/demo/src/app/app.config.ts new file mode 100644 index 0000000..303e474 --- /dev/null +++ b/projects/demo/src/app/app.config.ts @@ -0,0 +1,9 @@ +import { + ApplicationConfig, +} from '@angular/core'; + +export const appConfig: ApplicationConfig = { + providers: [ + + ], +}; diff --git a/projects/demo/src/app/app.module.ts b/projects/demo/src/app/app.module.ts deleted file mode 100644 index a16d869..0000000 --- a/projects/demo/src/app/app.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { APP_ID, NgModule } from '@angular/core'; - -import { AppComponent } from './app.component'; - -import { KeysPipe } from './pipes/keys.pipe'; -import { CommonModule } from '@angular/common'; -import { NoopAnimationsModule } from '@angular/platform-browser/animations'; - -@NgModule({ - declarations: [AppComponent, KeysPipe], - imports: [NoopAnimationsModule, CommonModule], - providers: [{ - provide: APP_ID, - useValue: 'serverApp' - }], - bootstrap: [AppComponent], -}) -export class AppModule {} diff --git a/projects/demo/src/app/pipes/keys.pipe.ts b/projects/demo/src/app/pipes/keys.pipe.ts index 83400d1..7a83a9c 100644 --- a/projects/demo/src/app/pipes/keys.pipe.ts +++ b/projects/demo/src/app/pipes/keys.pipe.ts @@ -1,9 +1,6 @@ import { Pipe, PipeTransform } from '@angular/core'; -@Pipe({ - name: 'keys', - standalone: false -}) +@Pipe({ name: 'keys', standalone:true }) export class KeysPipe implements PipeTransform { transform(value, props: string[] = []): any { const keys = []; diff --git a/projects/demo/src/app/server.ts b/projects/demo/src/app/server.ts deleted file mode 100644 index e824f44..0000000 --- a/projects/demo/src/app/server.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { NgModule } from '@angular/core'; -import { ServerModule } from '@angular/platform-server'; - -import { AppModule } from './app.module'; -import { AppComponent } from './app.component'; -import { Inject, Injectable, Optional, PLATFORM_ID } from '@angular/core'; -import { REQUEST } from '../express.tokens'; -import { Request } from 'express'; -import { DeviceDetectorService } from 'ngx-device-detector'; -import { isPlatformServer } from '@angular/common'; - -@Injectable() -export class UniversalDeviceDetectorService extends DeviceDetectorService { - constructor(@Inject(PLATFORM_ID) platformId: any, @Optional() @Inject(REQUEST) request: Request) { - super(platformId); - if (isPlatformServer(platformId)) { - super.setDeviceInfo((request.headers['user-agent'] as string) || ''); - } - } -} - -@NgModule({ - imports: [AppModule, ServerModule], - bootstrap: [AppComponent], - providers: [ - { - provide: DeviceDetectorService, - useClass: UniversalDeviceDetectorService, - }, - ], -}) -export class AppServerModule {} diff --git a/projects/demo/src/main.server.ts b/projects/demo/src/main.server.ts new file mode 100644 index 0000000..4b9d4d1 --- /dev/null +++ b/projects/demo/src/main.server.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { config } from './app/app.config.server'; + +const bootstrap = () => bootstrapApplication(AppComponent, config); + +export default bootstrap; diff --git a/projects/demo/src/main.ts b/projects/demo/src/main.ts index 98deafb..163e5a1 100644 --- a/projects/demo/src/main.ts +++ b/projects/demo/src/main.ts @@ -1,9 +1,22 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { AppModule } from './app/app.module'; + +import { APP_ID, importProvidersFrom } from '@angular/core'; +import { provideNoopAnimations } from '@angular/platform-browser/animations'; +import { CommonModule } from '@angular/common'; +import { AppComponent } from './app/app.component'; +import { bootstrapApplication } from '@angular/platform-browser'; document.addEventListener('DOMContentLoaded', () => { - platformBrowserDynamic() - .bootstrapModule(AppModule) + bootstrapApplication(AppComponent, { + providers: [ + importProvidersFrom(CommonModule), + { + provide: APP_ID, + useValue: 'serverApp' + }, + provideNoopAnimations(), + ] +}) .catch(err => console.error(err)); }); diff --git a/projects/demo/src/server.ts b/projects/demo/src/server.ts new file mode 100644 index 0000000..f6a88ad --- /dev/null +++ b/projects/demo/src/server.ts @@ -0,0 +1,65 @@ +import { APP_BASE_HREF } from '@angular/common'; +import { CommonEngine, isMainModule } from '@angular/ssr/node'; +import express from 'express'; +import { dirname, join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import bootstrap from './main.server'; + +const serverDistFolder = dirname(fileURLToPath(import.meta.url)); +const browserDistFolder = resolve(serverDistFolder, '../browser'); +const indexHtml = join(serverDistFolder, 'index.server.html'); + +const app = express(); +const commonEngine = new CommonEngine(); + +/** + * Example Express Rest API endpoints can be defined here. + * Uncomment and define endpoints as necessary. + * + * Example: + * ```ts + * app.get('/api/**', (req, res) => { + * // Handle API request + * }); + * ``` + */ + +/** + * Serve static files from /browser + */ +app.get( + '**', + express.static(browserDistFolder, { + maxAge: '1y', + index: 'index.html' + }), +); + +/** + * Handle all other requests by rendering the Angular application. + */ +app.get('**', (req, res, next) => { + const { protocol, originalUrl, baseUrl, headers } = req; + + commonEngine + .render({ + bootstrap, + documentFilePath: indexHtml, + url: `${protocol}://${headers.host}${originalUrl}`, + publicPath: browserDistFolder, + providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }], + }) + .then((html) => res.send(html)) + .catch((err) => next(err)); +}); + +/** + * Start the server if this module is the main entry point. + * The server listens on the port defined by the `PORT` environment variable, or defaults to 4000. + */ +if (isMainModule(import.meta.url)) { + const port = process.env['PORT'] || 4000; + app.listen(port, () => { + console.log(`Node Express server listening on http://localhost:${port}`); + }); +} diff --git a/projects/demo/tsconfig.app.json b/projects/demo/tsconfig.app.json index e669e55..a74be25 100644 --- a/projects/demo/tsconfig.app.json +++ b/projects/demo/tsconfig.app.json @@ -11,7 +11,8 @@ "src/main.ts", "src/polyfills.ts", "src/demo.ts", - "server.ts" + "server.ts", + "src/server.ts" ], "include": ["src/**/*.d.ts"] }