Web Client Application
Bootstrap the frontend application
In this section, you will use Angular 8 to create the frontend application project.
If you do not have Angular CLI installed on your machine, open a terminal window and run npm install -g @angular/cli@8. This will install the version 8 of Angular.
To create the frontend application
- In a terminal, navigate to the same root directory as the backend application directory.
- Run
ng new events-uiand accept the defaults. This creates the frontend project directory named events-ui and installs Angular8 dependencies. - Open the project directory in your favorite frontend IDE.
- Create or edit the following folders and files in the project directory:
- /src/app/model directory - to create
- /src/environments directory - to edit
- /src/app/component directory - to create
- /src/app/app.module.ts file - to edit
Model
To implement the model
- Create a directory named model
- In the model directory create the event.ts file which defines the event received on the web-socket.
- Write the following code for the event class:
export class Event {
tenant: string;
eventTime: string;
}Environment
The environments directory is created by default at the ng new command. It contains 2 files:
- environment.ts - the default configuration file with the default environment settings
- environment.prod.ts - the production specific configuration file
To edit the environment.ts file
- Open environment.ts and copy the following code in it:
export const environment = {
production: false,
apiEndpoint: '<%WEBSOCKET-URL%>'
};- Replace
<%WEBSOCKET-URL%>with the public web socket endpoint of the events-application server app. For examplehttp://localhost:8080/socket, if the app is running on localhost.
Component
To implement the component directory
- Create a component directory named component
- By default, Angular8 creates component files. Their names start with app.component. Move the following types of component files to the previously created directory:
- app.component.ts - which stores the service that initiates web-socket and receives events on it
- app.component.html - which stores the display of the component
- app.component.css - which stores the style for the component
- Write the following code for the app.component.ts:
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.css'],
providers: [],
})
export class EventComponent implements OnInit {
event: Event;
_eventSubscription: ISubscription;
serverUrl = environment.apiEndpoint;
stompClient: any;
connect() {
const socket = new SockJS(this.serverUrl);
this.stompClient = Stomp.over(socket);
const _this = this;
this.stompClient.connect({}, function (frame: any) {
console.log('Connected: ' + frame);
_this.stompClient.subscribe('/clock/event', function (data: any) {
_this.event = JSON.parse(data.body);
});
});
}
constructor() { }
ngOnInit() {
this.event = new Event();
this.event.tenant = 'N/A';
this.event.eventTime = 'N/A';
this.connect();
}
ngOnDestroy() {
this._eventSubscription.unsubscribe();
}
}- Import the required packages for app.component.ts above the class declaration.
import { Component, OnInit } from '@angular/core';
import { Event } from '../model/event';
import { ISubscription } from 'rxjs/Subscription';
import * as Stomp from 'stompjs';
import * as SockJS from 'sockjs-client';
import { environment } from '../../environments/environment';- Write the following code for the app.component.html:
<div class="event-container">
<mat-form-field>
<input matInput [disabled]="true" placeholder="Tenant" value="{{ event.tenant }}">
</mat-form-field>
<mat-form-field>
<input matInput [disabled]="true" placeholder="EventTime" value="{{ event.eventTime }}">
</mat-form-field>
</div>- Write the following code for the app.component.css:
.event-container {
display: flex;
flex-direction: column;
}
.event-container > * {
width: 100%;
}Application Module
The module file stores the links to other modules and the imports for the frontend application.
To edit the app.module.ts file
- Open the typescript file named app.module.ts
- Write the following code for the app.module.ts
@NgModule({
exports: [
A11yModule,
CdkStepperModule,
CdkTableModule,
CdkTreeModule,
DragDropModule,
MatAutocompleteModule,
MatBadgeModule,
MatBottomSheetModule,
MatButtonModule,
MatButtonToggleModule,
MatCardModule,
MatCheckboxModule,
MatChipsModule,
MatStepperModule,
MatDatepickerModule,
MatDialogModule,
MatDividerModule,
MatExpansionModule,
MatGridListModule,
MatIconModule,
MatInputModule,
MatListModule,
MatMenuModule,
MatNativeDateModule,
MatPaginatorModule,
MatProgressBarModule,
MatProgressSpinnerModule,
MatRadioModule,
MatRippleModule,
MatSelectModule,
MatSidenavModule,
MatSliderModule,
MatSlideToggleModule,
MatSnackBarModule,
MatSortModule,
MatTableModule,
MatTabsModule,
MatToolbarModule,
MatTooltipModule,
MatTreeModule,
PortalModule,
ScrollingModule,
]
})
export class DemoMaterialModule {}- Import all the required libraries and modules:
import {NgModule} from '@angular/core';
import {A11yModule} from '@angular/cdk/a11y';
import {DragDropModule} from '@angular/cdk/drag-drop';
import {PortalModule} from '@angular/cdk/portal';
import {ScrollingModule} from '@angular/cdk/scrolling';
import {CdkStepperModule} from '@angular/cdk/stepper';
import {CdkTableModule} from '@angular/cdk/table';
import {CdkTreeModule} from '@angular/cdk/tree';
import {MatAutocompleteModule} from '@angular/material/autocomplete';
import {MatBadgeModule} from '@angular/material/badge';
import {MatBottomSheetModule} from '@angular/material/bottom-sheet';
import {MatButtonModule} from '@angular/material/button';
import {MatButtonToggleModule} from '@angular/material/button-toggle';
import {MatCardModule} from '@angular/material/card';
import {MatCheckboxModule} from '@angular/material/checkbox';
import {MatChipsModule} from '@angular/material/chips';
import {MatStepperModule} from '@angular/material/stepper';
import {MatDatepickerModule} from '@angular/material/datepicker';
import {MatDialogModule} from '@angular/material/dialog';
import {MatDividerModule} from '@angular/material/divider';
import {MatExpansionModule} from '@angular/material/expansion';
import {MatGridListModule} from '@angular/material/grid-list';
import {MatIconModule} from '@angular/material/icon';
import {MatInputModule} from '@angular/material/input';
import {MatListModule} from '@angular/material/list';
import {MatMenuModule} from '@angular/material/menu';
import {MatNativeDateModule, MatRippleModule} from '@angular/material/core';
import {MatPaginatorModule} from '@angular/material/paginator';
import {MatProgressBarModule} from '@angular/material/progress-bar';
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
import {MatRadioModule} from '@angular/material/radio';
import {MatSelectModule} from '@angular/material/select';
import {MatSidenavModule} from '@angular/material/sidenav';
import {MatSliderModule} from '@angular/material/slider';
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
import {MatSnackBarModule} from '@angular/material/snack-bar';
import {MatSortModule} from '@angular/material/sort';
import {MatTableModule} from '@angular/material/table';
import {MatTabsModule} from '@angular/material/tabs';
import {MatToolbarModule} from '@angular/material/toolbar';
import {MatTooltipModule} from '@angular/material/tooltip';
import {MatTreeModule} from '@angular/material/tree';Edit Other Angular Default Files
For the frontend application to work, you need to edit some of the default angular files, created by the ng new name-of-the-app command.
Find the files that need editing in the src/ directory:
- main.ts - the main entry point for your app. Compiles the application with the JIT compiler and bootstraps the application’s root module AppModule to run in the browser.
import './polyfills';
import {HttpClientModule} from '@angular/common/http';
import {NgModule} from '@angular/core';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {MatNativeDateModule} from '@angular/material/core';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {DemoMaterialModule} from './app/app.module';
import {EventComponent} from './app/component/app.component';
@NgModule({
imports: [
BrowserModule,
NoopAnimationsModule,
FormsModule,
HttpClientModule,
DemoMaterialModule,
MatNativeDateModule,
ReactiveFormsModule,
EventComponent
],
entryComponents: [EventComponent],
bootstrap: [EventComponent],
})
export class AppModule {}
platformBrowserDynamic().bootstrapModule(AppModule);- polyfills.ts - the polyfill scripts that make the application compatible with different browsers
import 'zone.js/dist/zone';
import 'hammerjs';
(window as any).global = window;- styles.css - the global style file
@use '@angular/material/prebuilt-themes/deeppurple-amber.css';
body {
font-family: Roboto, Arial, sans-serif;
margin: 0;
}
.basic-container {
padding: 30px;
}
.version-info {
font-size: 8pt;
float: right;
}- index.html - the main HTML page that is served when you access the default port.
<!doctype html>
<html>
<head>
<title>Event Notifications</title>
</head>
<body>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons&display=block" rel="stylesheet">
<div class="mat-app-background basic-container">
<app-root></app-root>
</div>
</body>
</html>- angular.json - CLI configuration defaults for all projects in the workspace, including configuration options for build, serve, and test tools that the CLI uses
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"events-ui": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/events-ui",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": false,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"buildTarget": "events-ui:build"
},
"configurations": {
"production": {
"buildTarget": "events-ui:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "events-ui:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "events-ui:serve"
},
"configurations": {
"production": {
"devServerTarget": "events-ui:serve:production"
}
}
}
}
}},
"defaultProject": "events-ui"
}This UI application was built with Angular 8.2.9 version. Using a greater version may lead to conflicts for the imports of dependencies and modules. To resolve the conflicts, modify the package.json file as follows and run
npm installafter saving the file.
- package.json - the npm package dependencies that are available to all projects in the workspace
{
"name": "events-ui",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^8.2.9",
"@angular/cdk": "~8.2.2",
"@angular/common": "^8.2.9",
"@angular/compiler": "^8.2.9",
"@angular/core": "^8.2.9",
"@angular/forms": "^8.2.9",
"@angular/material": "~8.2.2",
"@angular/material-moment-adapter": "~8.2.2",
"@angular/platform-browser": "^8.2.9",
"@angular/platform-browser-dynamic": "^8.2.9",
"@angular/router": "^8.2.9",
"@types/jquery": "^3.3.31",
"@types/sockjs-client": "^1.1.1",
"@types/stompjs": "^2.3.4",
"angular-in-memory-web-api": "~0.9.0",
"core-js": "^2.6.9",
"hammerjs": "^2.0.8",
"jquery": "^3.4.1",
"moment": "^2.24.0",
"net": "^1.0.2",
"rxjs": "^6.5.3",
"rxjs-compat": "^6.5.3",
"sockjs-client": "^1.4.0",
"stompjs": "^2.3.3",
"tslib": "^1.10.0",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.803.23",
"@angular/cli": "^8.3.15",
"@angular/compiler-cli": "^8.2.9",
"@angular/language-service": "^8.2.9",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"codelyzer": "^5.0.0",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "^3.5.3"
}
}This is how your frontend project should look like at the end:
Events App Frontend Project