Skip to content

Commit

Permalink
chore(demo): setup ssr
Browse files Browse the repository at this point in the history
  • Loading branch information
tinesoft committed Jan 17, 2024
1 parent e7ffe41 commit b8edcbf
Show file tree
Hide file tree
Showing 12 changed files with 843 additions and 371 deletions.
65 changes: 63 additions & 2 deletions apps/demo/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"executor": "@angular-devkit/build-angular:browser",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/demo",
"outputPath": "dist/apps/demo/browser",
"index": "apps/demo/src/index.html",
"main": "apps/demo/src/main.ts",
"polyfills": ["zone.js"],
Expand Down Expand Up @@ -105,10 +105,71 @@
},
"deploy": {
"executor": "@nx-dotnet/nx-ghpages:deploy",
"dependsOn": ["predeploy"],
"options": {
"remote": "[email protected]:tinesoft/ngx-cookieconsent.git",
"directory": "dist/apps/demo"
"directory": "dist/apps/demo/browser"
}
},
"server": {
"dependsOn": ["build"],
"executor": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist/apps/demo/server",
"main": "apps/demo/server.ts",
"tsConfig": "apps/demo/tsconfig.server.json",
"inlineStyleLanguage": "scss"
},
"configurations": {
"production": {
"outputHashing": "media",
"fileReplacements": [
{
"replace": "apps/demo/src/environments/environment.ts",
"with": "apps/demo/src/environments/environment.prod.ts"
}
]
},
"development": {
"buildOptimizer": false,
"optimization": false,
"sourceMap": true,
"extractLicenses": false,
"vendorChunk": true
}
},
"defaultConfiguration": "production"
},
"serve-ssr": {
"executor": "@angular-devkit/build-angular:ssr-dev-server",
"configurations": {
"development": {
"browserTarget": "demo:build:development",
"serverTarget": "demo:server:development"
},
"production": {
"browserTarget": "demo:build:production",
"serverTarget": "demo:server:production"
}
},
"defaultConfiguration": "development"
},
"prerender": {
"executor": "@angular-devkit/build-angular:prerender",
"options": {
"routes": ["/"]
},
"configurations": {
"development": {
"browserTarget": "demo:build:development",
"serverTarget": "demo:server:development"
},
"production": {
"browserTarget": "demo:build:production",
"serverTarget": "demo:server:production"
}
},
"defaultConfiguration": "production"
}
},
"tags": []
Expand Down
72 changes: 72 additions & 0 deletions apps/demo/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import 'zone.js/node';

import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine } from '@angular/ssr';
import * as express from 'express';
import { existsSync } from 'node:fs';
import { join } from 'node:path';
import bootstrap from './src/main.server';

// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist/apps/demo/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html'))
? join(distFolder, 'index.original.html')
: join(distFolder, 'index.html');

const commonEngine = new CommonEngine();

server.set('view engine', 'html');
server.set('views', distFolder);

// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get(
'*.*',
express.static(distFolder, {
maxAge: '1y',
})
);

// All regular routes use the Angular engine
server.get('*', (req, res, next) => {
const { protocol, originalUrl, baseUrl, headers } = req;

commonEngine
.render({
bootstrap,
documentFilePath: indexHtml,
url: `${protocol}://${headers.host}${originalUrl}`,
publicPath: distFolder,
providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }],
})
.then((html) => res.send(html))
.catch((err) => next(err));
});

return server;
}

function run(): void {
const port = process.env['PORT'] || 4000;

// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = (mainModule && mainModule.filename) || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}

export default bootstrap;
30 changes: 11 additions & 19 deletions apps/demo/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import { Component, OnInit, OnDestroy, Inject, PLATFORM_ID } from '@angular/core';
import { Router, NavigationEnd, Event, RouterOutlet } from '@angular/router';
import { isPlatformBrowser } from '@angular/common';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { RouterOutlet } from '@angular/router';

import { NgcCookieConsentService, NgcInitializingEvent, NgcInitializationErrorEvent, NgcStatusChangeEvent, NgcNoCookieLawEvent } from 'ngx-cookieconsent';

import { filter } from 'rxjs/operators';
import { Subscription } from 'rxjs';
import { TranslateService } from '@ngx-translate/core';
import { HeaderComponent } from "./shared/header/header.component";
import { FooterComponent } from "./shared/footer/footer.component";
@Component({
selector: 'ngc-demo-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
standalone: true,
imports: [RouterOutlet, HeaderComponent, FooterComponent]
selector: 'ngc-demo-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
standalone: true,
imports: [RouterOutlet, HeaderComponent, FooterComponent]
})
export class AppComponent implements OnInit, OnDestroy{
export class AppComponent implements OnInit, OnDestroy {

//keep refs to subscriptions to be able to unsubscribe later
private popupOpenSubscription!: Subscription;
Expand All @@ -28,14 +26,8 @@ export class AppComponent implements OnInit, OnDestroy{
private revokeChoiceSubscription!: Subscription;
private noCookieLawSubscription!: Subscription;

constructor(private ccService: NgcCookieConsentService, private translateService:TranslateService, private router: Router, @Inject(PLATFORM_ID) private platformId: Object) {
this.router.events.pipe(
filter((event:Event) => event instanceof NavigationEnd)
).subscribe(event => {
if (isPlatformBrowser(this.platformId)) {
window.scroll(0, 0);
}
});
constructor(private ccService: NgcCookieConsentService, private translateService: TranslateService) {

}

ngOnInit() {
Expand All @@ -57,7 +49,7 @@ export class AppComponent implements OnInit, OnDestroy{
// the cookieconsent is initilializing... Not yet safe to call methods like `NgcCookieConsentService.hasAnswered()`
console.log(`initializing: ${JSON.stringify(event)}`);
});

this.initializedSubscription = this.ccService.initialized$.subscribe(
() => {
// the cookieconsent has been successfully initialized.
Expand Down
11 changes: 11 additions & 0 deletions apps/demo/src/app/app.config.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { appConfig } from './app.config';
import { provideHttpClient, withFetch } from '@angular/common/http';

const serverConfig: ApplicationConfig = {
providers: [provideServerRendering(), provideHttpClient(withFetch()),
],
};

export const config = mergeApplicationConfig(appConfig, serverConfig);
103 changes: 71 additions & 32 deletions apps/demo/src/app/app.config.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,73 @@

import { HttpClient, provideHttpClient } from '@angular/common/http';
import { ENVIRONMENT_INITIALIZER, SecurityContext } from '@angular/core';
import { ApplicationConfig } from '@angular/core';
import { ENVIRONMENT_INITIALIZER, SecurityContext, ApplicationConfig } from '@angular/core';

import { NgcCookieConsentConfig, provideNgcCookieConsent } from 'ngx-cookieconsent';
import {
NgcCookieConsentConfig,
provideNgcCookieConsent,
} from 'ngx-cookieconsent';

import { environment } from '../environments/environment';

import { provideRouter, withEnabledBlockingInitialNavigation } from '@angular/router';
import {
provideRouter,
withEnabledBlockingInitialNavigation,
withInMemoryScrolling,
} from '@angular/router';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { MARKED_OPTIONS, provideMarkdown } from 'ngx-markdown';
import { appRoutes } from './app.routes';
import { FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { IconDefinition, faBolt, faBook, faCheck, faClipboard, faFileText, faGear, faLanguage, faImage, faInfo, faLink, faMapMarker, faUndo, faVcard } from '@fortawesome/free-solid-svg-icons';
import {
IconDefinition,
faBolt,
faBook,
faCheck,
faClipboard,
faFileText,
faGear,
faLanguage,
faImage,
faInfo,
faLink,
faMapMarker,
faUndo,
faVcard,
} from '@fortawesome/free-solid-svg-icons';
import { faGithub } from '@fortawesome/free-brands-svg-icons';
import { provideClientHydration } from '@angular/platform-browser';

const iconsList: IconDefinition[] = [faUndo, faMapMarker, faLanguage, faLink, faGear, faFileText, faVcard, faImage, faCheck, faClipboard, faInfo, faBook, faBolt, faGithub];
const iconsList: IconDefinition[] = [
faUndo,
faMapMarker,
faLanguage,
faLink,
faGear,
faFileText,
faVcard,
faImage,
faCheck,
faClipboard,
faInfo,
faBook,
faBolt,
faGithub,
];

const cookieConfig: NgcCookieConsentConfig = {
cookie: {
domain: environment.cookieDomain // it is recommended to set your domain, for cookies to work properly
domain: environment.cookieDomain, // it is recommended to set your domain, for cookies to work properly
},
palette: {
popup: {
background: '#000'
background: '#000',
},
button: {
background: '#f1d600'
}
background: '#f1d600',
},
},
theme: 'edgeless',
type: 'opt-out'
type: 'opt-out',
};

// AoT requires an exported function for factories
Expand All @@ -41,40 +77,43 @@ export function HttpLoaderFactory(httpClient: HttpClient) {

export const appConfig: ApplicationConfig = {
providers: [
provideRouter(appRoutes, withEnabledBlockingInitialNavigation()),
provideClientHydration(),
provideRouter(appRoutes,
withEnabledBlockingInitialNavigation(),
withInMemoryScrolling({
scrollPositionRestoration: 'top'
})),
provideHttpClient(),
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
deps: [HttpClient],
},
}).providers,
provideNgcCookieConsent(cookieConfig),
provideMarkdown(
{
markedOptions: {
provide: MARKED_OPTIONS,
useValue: {
gfm: true,
breaks: false,
pedantic: false,
smartLists: true,
smartypants: false,
},
provideMarkdown({
markedOptions: {
provide: MARKED_OPTIONS,
useValue: {
gfm: true,
breaks: false,
pedantic: false,
smartLists: true,
smartypants: false,
},
sanitize: SecurityContext.NONE
}
),
},
sanitize: SecurityContext.NONE,
}),
{
provide: ENVIRONMENT_INITIALIZER,
useFactory: (iconLibrary: FaIconLibrary) => async() => {
useFactory: (iconLibrary: FaIconLibrary) => async () => {
// Add any icons needed here:
iconLibrary.addIcons(...iconsList);
},
// When using a factory provider you need to explicitly specify its dependencies.
deps: [FaIconLibrary],
multi: true,
},
]
}
],
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ import { NO_ERRORS_SCHEMA } from '@angular/core';
import { appConfig } from '../../app.config';
import { PlaygroundComponent } from './playground.component';


describe('PlaygroundComponent', () => {
let component: PlaygroundComponent;
let fixture: ComponentFixture<PlaygroundComponent>;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule(Object.assign({}, appConfig, {
schemas: [NO_ERRORS_SCHEMA]
schemas: [NO_ERRORS_SCHEMA],
}))
.compileComponents();
}));
Expand Down
7 changes: 7 additions & 0 deletions apps/demo/src/main.server.ts
Original file line number Diff line number Diff line change
@@ -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;
Loading

0 comments on commit b8edcbf

Please sign in to comment.