Skip to content

Commit

Permalink
Merge pull request #13911 from IgniteUI/angular-ssr
Browse files Browse the repository at this point in the history
test(ssr): extending the bundle-test project for SSR
  • Loading branch information
kdinev authored Feb 15, 2024
2 parents 4663691 + c2b57b3 commit b370e22
Show file tree
Hide file tree
Showing 18 changed files with 164 additions and 60 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:

strategy:
matrix:
node-version: [18.x, 20.5.x]
node-version: [18.x, 20.x]

steps:
- name: Checkout
Expand Down Expand Up @@ -56,7 +56,7 @@ jobs:
run: |
npm run build:i18n
npm run test:i18n:dist
- name: Bundle Tree-Shake Test
- name: Bundle Tree-Shake & SSR Test
run: npm run build:bundletest
- name: Publish to coveralls.io
if: github.repository == 'IgniteUI/igniteui-angular' && matrix.node-version == '18.x'
Expand Down
5 changes: 5 additions & 0 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,11 @@
"includePaths": [
"node_modules"
]
},
"server": "projects/bundle-test/src/main.server.ts",
"prerender": true,
"ssr": {
"entry": "projects/bundle-test/server.ts"
}
},
"configurations": {
Expand Down
56 changes: 56 additions & 0 deletions projects/bundle-test/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine } from '@angular/ssr';
import express from 'express';
import { fileURLToPath } from 'node:url';
import { dirname, join, resolve } 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 serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, '../browser');
const indexHtml = join(serverDistFolder, 'index.server.html');

const commonEngine = new CommonEngine();

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

// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(browserDistFolder, {
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: browserDistFolder,
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}`);
});
}

run();
2 changes: 1 addition & 1 deletion projects/bundle-test/src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { AppComponent } from './app.component';

describe('AppComponent', () => {
beforeEach(() => TestBed.configureTestingModule({
declarations: [AppComponent]
imports: [AppComponent]
}));

it('should create the app', () => {
Expand Down
9 changes: 6 additions & 3 deletions projects/bundle-test/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { Component } from '@angular/core';
import { ChipResourceStringsBG } from 'igniteui-angular-i18n';
import { RouterOutlet } from '@angular/router';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
standalone: true,
imports: [RouterOutlet]
})
export class AppComponent {
protected chipStrings = ChipResourceStringsBG;
Expand Down
11 changes: 11 additions & 0 deletions projects/bundle-test/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';

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

export const config = mergeApplicationConfig(appConfig, serverConfig);
9 changes: 9 additions & 0 deletions projects/bundle-test/src/app/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideClientHydration } from '@angular/platform-browser';

export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes), provideClientHydration()]
};
18 changes: 0 additions & 18 deletions projects/bundle-test/src/app/app.module.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { Routes } from '@angular/router';
import { ChipComponent } from './chip/chip.component';
import { ButtonGroupComponent } from './button-group/button-group.component';

const routes: Routes = [
export const routes: Routes = [
{ path: '', redirectTo: '/chip', pathMatch: 'full' },
{ path: 'chip', component: ChipComponent },
{ path: 'button-group', component: ButtonGroupComponent}
// { path: 'form', loadComponent: () => import('./form/form.component').then(m => m.FormComponent) },
// { path: 'stepper', loadComponent: () => import('./stepper/stepper.component').then(m => m.StepperComponent) },
// { path: 'grid', loadComponent: () => import('./grid/grid.component').then(m => m.GridComponent) }
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<igx-buttongroup>
<button type="button" igxButton="flat">Button 1</button>
<button type="button" igxButton="flat">Button 2</button>
<button type="button" igxButton="flat">Button 3</button>
</igx-buttongroup>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:host {
display: block;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { IGX_BUTTON_GROUP_DIRECTIVES } from 'igniteui-angular';

@Component({
selector: 'app-button-group',
standalone: true,
imports: [
IGX_BUTTON_GROUP_DIRECTIVES
],
templateUrl: './button-group.component.html',
styleUrl: './button-group.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ButtonGroupComponent { }
7 changes: 7 additions & 0 deletions projects/bundle-test/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;
9 changes: 4 additions & 5 deletions projects/bundle-test/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppComponent } from './app/app.component';
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';

import { AppModule } from './app/app.module';


platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
bootstrapApplication(AppComponent, appConfig).catch(err => console.error(err));
1 change: 1 addition & 0 deletions projects/bundle-test/src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ $app-palette: palette(

$include: (
igx-chip,
igx-buttongroup,
// igx-checkbox,
// igx-radio,
// igx-switch,
Expand Down
8 changes: 6 additions & 2 deletions projects/bundle-test/tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/app",
"types": [],
"types": [
"node"
],
"paths": {
"igniteui-angular": [
"dist/igniteui-angular"
Expand All @@ -17,7 +19,9 @@
}
},
"files": [
"src/main.ts"
"src/main.ts",
"src/main.server.ts",
"server.ts"
],
"include": [
"src/**/*.d.ts"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ export class IgxButtonGroupComponent extends DisplayDensityBase implements After

this.mutationObserver = this.setMutationsObserver();

this.mutationObserver.observe(this._el.nativeElement, this.observerConfig);
this.mutationObserver?.observe(this._el.nativeElement, this.observerConfig);
}

/**
Expand All @@ -483,7 +483,7 @@ export class IgxButtonGroupComponent extends DisplayDensityBase implements After
this.queryListNotifier$.next();
this.queryListNotifier$.complete();

this.mutationObserver.disconnect();
this.mutationObserver?.disconnect();
}

/**
Expand Down Expand Up @@ -513,28 +513,35 @@ export class IgxButtonGroupComponent extends DisplayDensityBase implements After
}
}

this.mutationObserver.observe(this._el.nativeElement, this.observerConfig);
this.mutationObserver?.observe(this._el.nativeElement, this.observerConfig);
}

private setMutationsObserver() {
return new MutationObserver((records, observer) => {
// Stop observing while handling changes
observer.disconnect();
if (typeof MutationObserver !== 'undefined') {
return new MutationObserver((records, observer) => {
// Stop observing while handling changes
observer.disconnect();

const updatedButtons = this.getUpdatedButtons(records);
const updatedButtons = this.getUpdatedButtons(records);

if (updatedButtons.length > 0) {
updatedButtons.forEach((button) => {
const index = this.buttons.map((b) => b.nativeElement).indexOf(button);
const args: IButtonGroupEventArgs = { owner: this, button: this.buttons[index], index };
if (updatedButtons.length > 0) {
updatedButtons.forEach((button) => {
const index = this.buttons.map((b) => b.nativeElement).indexOf(button);
const args: IButtonGroupEventArgs = { owner: this, button: this.buttons[index], index };

this.updateButtonSelectionState(index, args);
});
}
this.updateButtonSelectionState(index, args);
});
}

// Watch for changes again
observer.observe(this._el.nativeElement, this.observerConfig);
});
// Watch for changes again
observer.observe(this._el.nativeElement, this.observerConfig);

// Cleanup function
this._renderer.listen(this._el.nativeElement, 'DOMNodeRemoved', () => {
observer.disconnect();
});
});
}
}

private getUpdatedButtons(records: MutationRecord[]) {
Expand Down
9 changes: 6 additions & 3 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@
"baseUrl": "./",
"downlevelIteration": true,
"importHelpers": true,
"module": "es2020",
"module": "es2022",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true,
"target": "ES2022",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2019",
"es2022",
"dom"
],
"paths": {
Expand Down Expand Up @@ -45,6 +47,7 @@
"generateDeepReexports": true,
"strictTemplates": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
}
}

0 comments on commit b370e22

Please sign in to comment.