Project Structure (Frontend)
The updated dependencies in package.json file of frontend will look like:
"dependencies": {
"@angular/animations": "^17.2.0",
"@angular/common": "^17.2.0",
"@angular/compiler": "^17.2.0",
"@angular/core": "^17.2.0",
"@angular/forms": "^17.2.0",
"@angular/platform-browser": "^17.2.0",
"@angular/platform-browser-dynamic": "^17.2.0",
"@angular/platform-server": "^17.2.0",
"@angular/router": "^17.2.0",
"@angular/ssr": "^17.2.3",
"@auth0/angular-jwt": "^5.2.0",
"express": "^4.18.2",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
}
Example: Create the required files as seen in project structure and add the following codes.
<!-- auth.component.html -->
<div class="error-message" *ngIf="errorMessage">
{{ errorMessage }}
</div>
<div class="success-message" *ngIf="successMessage">
{{ successMessage }}
</div>
<div class="container" *ngIf="loginActive">
<h2>Login</h2>
<form (ngSubmit)="login()">
<div class="form-group">
<label for="email">Email:</label>
<input type="email" class="form-control" id="email" name="email" [(ngModel)]="email" required>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" class="form-control" id="password" name="password" [(ngModel)]="password" required>
</div>
<button type="submit" class="btn btn-primary" href="getAllPosts">Login</button>
</form>
</div>
<div class="container" *ngIf="registerActive">
<h2>Register</h2>
<form (submit)="register()">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" class="form-control" [(ngModel)]="username" name="username" required />
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" class="form-control" [(ngModel)]="email" name="email" required />
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" class="form-control" [(ngModel)]="password" name="password" required />
</div>
<button type="submit" class="btn btn-primary">Register</button>
</form>
</div>
<!-- sidebar.component.html -->
<ul class="nav">
<li><a (click)="loadContent('summarizedText')">My Summarized Texts</a></li>
<li><a (click)="loadContent('summarizeHere')">Summarize Here</a></li>
</ul>
<!-- summarizer.component.html -->
<div *ngIf="summarizedTextList && summarizedTextList.length > 0; else noSummarizedTexts">
<div class="summary" *ngFor="let text of summarizedTextList; let i = index">
<div class="summary-content">
{{text.summarizedText}}
</div>
<button (click)="deleteSummarizedText(text._id)">Delete</button>
</div>
</div>
<ng-template #noSummarizedTexts>
<div class="no-summary-container">
<p>No Summarized Text available</p>
</div>
</ng-template>
<!-- add-summarizer.component.html -->
<div class="container">
<div class="input-section">
<label for="inputText">Enter Text to summarize</label>
<textarea class="input-text" name="inputText" id="inputText" [(ngModel)]="inputText" rows="10"
cols="50"></textarea>
<label for="length">Enter Summary Length</label>
<input class="summary-length" type="number" [(ngModel)]="summaryLength" placeholder="Enter summary length"
min="1" />
<button class="summarize-button" (click)="summarize()">Summarize</button>
</div>
<div class="summarized-text-section" *ngIf="summarizedText">
<h3>Summarized Text</h3>
<p>{{ summarizedText }}</p>
</div>
</div>
<!-- app.component.html -->
<nav class="navbar">
<div class="navbar-title">{{ title }}</div>
<ul class="navbar-menu">
<li><a href="#" (click)="login()" *ngIf="!isLoggedIn">Login</a></li>
<li><a href="#" (click)="register()" *ngIf="!isLoggedIn">Register</a></li>
<li><a href="#" (click)="logout()" *ngIf="isLoggedIn">Logout</a></li>
</ul>
</nav>
<div class="container">
<app-sidebar (contentLoad)="loadContent($event)"></app-sidebar>
<div class="content">
<router-outlet></router-outlet>
</div>
</div>
/* app.component.css */
.navbar {
background-color: #333;
color: #fff;
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5vmax 3vmax;
}
.navbar-title {
font-size: 1.7rem;
}
.navbar-menu {
list-style-type: none;
padding: 0;
margin: 0;
}
.navbar-menu li {
display: inline;
margin-right: 2vmax;
font-size: 1.3rem;
}
.navbar-menu li:last-child {
margin-right: 0;
}
.navbar-menu li a {
color: #fff;
text-decoration: none;
}
.navbar-menu li a:hover {
text-decoration: underline;
}
.container {
display: flex;
height: 100%;
min-height: 87vh;
margin-top: 0.4vmax;
}
.content {
flex: 1;
padding: 20px;
}
app-sidebar {
width: 25%;
background-color: #333;
padding: 20px;
color: white;
font-size: 1.4rem;
}
/* summarizer.component.css */
.summary {
border: 1px solid #ccc;
border-radius: 5px;
padding: 1vmax;
margin-bottom: 2vmax;
}
.summary-content {
margin-bottom: 1vmax;
}
button {
background-color: crimson;
padding: 0.4vmax;
color: white;
border: none;
border-radius: 4px;
}
.no-summary-container {
margin-top: 20px;
padding: 10px;
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
border-radius: 4px;
}
.no-summary-container p {
text-align: center;
margin: 0;
}
/* auth.component.css */
.container {
width: 50%;
margin: 2rem auto;
padding: 1.5vmax;
padding-right: 2.5vmax;
border: 1px solid #ccc;
border-radius: 5px;
}
h2 {
text-align: center;
margin-bottom: 20px;
font-size: 2rem;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="text"],
input[type="email"],
input[type="password"] {
width: 97%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
button[type="submit"] {
width: 20%;
padding: 1.1vmax;
background-color: #0056b3;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
font-weight: bold;
font-size: 1rem;
align-self: center;
margin-top: 1vmax;
}
.container {
width: 50%;
margin: 2rem auto;
padding: 1.5vmax;
padding-right: 3.5vmax;
border: 1px solid #ccc;
border-radius: 5px;
}
h2 {
text-align: center;
margin-bottom: 20px;
font-size: 2rem;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="email"],
input[type="password"] {
width: 99%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
button[type="submit"] {
width: 20%;
padding: 1.1vmax;
background-color: #0056b3;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
font-weight: bold;
font-size: 1rem;
align-self: center;
margin-top: 1vmax;
}
.error-message {
color: #FF0000;
background-color: #FFEFEF;
padding: 10px;
border: 1px solid #FF0000;
border-radius: 5px;
margin-bottom: 10px;
}
.success-message {
color: green;
background-color: rgb(185, 231, 185);
padding: 10px;
border: 1px solid green;
border-radius: 5px;
margin-bottom: 10px;
}
/* add-summarizer.component.css */
.container {
max-width: 70%;
margin: 0 auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
background-color: #fff;
}
.input-section {
margin-bottom: 20px;
}
.input-section label {
display: block;
font-weight: bold;
margin-bottom: 5px;
margin-top: 1vmax;
}
.input-section textarea {
width: 98%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
resize: vertical;
text-align: justify;
}
.input-section input[type="number"] {
width: 98%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
.input-section button {
padding: 10px 20px;
background-color: #0056b3;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
margin-top: 3vmax;
width: 30%;
font-weight: 700;
font-size: 1rem;
}
.summarized-text-section {
margin-top: 20px;
}
.summarized-text-section h3 {
font-size: 24px;
margin-bottom: 10px;
}
.summarized-text-section p {
font-size: 16px;
line-height: 1.6;
text-align: justify;
}
/* sidebar.component.css */
ul {
border: 1px solid lightgray;
margin-top: 0%;
border-radius: 10px;
}
li {
list-style-type: none;
margin: 3vmax;
cursor: pointer;
box-sizing: border-box;
}
li:hover {
color: lightgray;
}
a {
color: lightgray;
text-decoration: none;
}
a:hover {
color: white;
text-decoration: none;
}
// auth.component.ts
import { Component } from '@angular/core';
import { AuthService } from '../../service/auth.service';
import { SharedService } from '../../service/shared.service';
import { Router } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-auth',
standalone: true,
imports: [FormsModule, CommonModule],
templateUrl: './auth.component.html',
styleUrl: './auth.component.css'
})
export class AuthComponent {
username!: string;
email!: string;
password!: string;
credentials: any = {};
successMessage: string = '';
errorMessage: string = '';
loginActive: boolean = true;
registerActive: boolean = false;
constructor(private authService: AuthService,
private router: Router,
private sharedService: SharedService) { }
ngOnInit(): void {
this.sharedService.loginEvent.subscribe(() => {
this.errorMessage = "";
this.successMessage = "";
this.loginActive = true;
this.registerActive = false;
});
this.sharedService.registerEvent.subscribe(() => {
this.errorMessage = "";
this.successMessage = "";
this.registerActive = true;
this.loginActive = false;
});
}
login(): void {
const credentials = {
email: this.email,
password: this.password
};
this.authService.login(credentials).subscribe(
(response: any) => {
const token = response.token;
localStorage.setItem("token", token);
this.authService.emitLoggedInEvent();
this.loginActive = false;
this.registerActive = false;
this.email = "";
this.password = "";
this.successMessage = response.message;
this.router.navigate(["/summarized-text"]);
},
(error: any) => {
console.error('Error logging in:', error);
this.errorMessage = "Login unsuccessfull ! Please reload or try in incognito tab";
}
);
this.errorMessage = "";
this.successMessage = "";
this.email = "";
this.password = "";
}
register(): void {
const userData = {
username: this.username,
email: this.email,
password: this.password
};
this.authService.register(userData).subscribe(
(response: any) => {
this.successMessage = response.message;
this.loginActive = true;
this.registerActive = false;
this.username = '';
this.email = '';
this.password = '';
},
(error: any) => {
console.error(error);
this.errorMessage = "User not registered successfully";
}
);
this.username = "";
this.email = "";
this.password = "";
}
}
// sidebar.component.ts
import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-sidebar',
standalone: true,
imports: [],
templateUrl: './sidebar.component.html',
styleUrl: './sidebar.component.css'
})
export class SidebarComponent {
isLoggedIn: boolean = true;
constructor() { }
ngOnInit(): void {
if (typeof localStorage !== 'undefined') {
const token = localStorage.getItem('token');
if (token) {
this.isLoggedIn = true;
}
}
}
@Output() contentLoad = new EventEmitter<string>();
loadContent(page: any) {
console.log("Event emit");
this.contentLoad.emit(page);
}
}
// summarizer.component.ts
import { Component } from '@angular/core';
import { SummarizerService } from '../../service/summarizer.service';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { AuthService } from '../../service/auth.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-summarizer',
standalone: true,
imports: [FormsModule, CommonModule],
templateUrl: './summarizer.component.html',
styleUrl: './summarizer.component.css'
})
export class SummarizerComponent {
inputText: string = '';
summarizedText: string = '';
userId: any = "";
summarizedTextList: any[] = [];
constructor(
private summarizerService: SummarizerService,
private router: Router) { }
ngOnInit(): void {
this.userId = this.getUserId();
if (this.userId) {
this.getSummarizedText(this.userId);
}
}
getUserId(): string | null {
if (typeof localStorage !== 'undefined') {
const token = localStorage.getItem('token');
if (token) {
const tokenPayload = JSON.parse(atob(token.split('.')[1]));
return tokenPayload.user.id;
}
}
return null;
}
getSummarizedText(userId: string) {
this.summarizerService.getSummarizedText(userId)
.subscribe((response: any) => {
this.summarizedTextList = response.summarizedTexts;
}, (error: any) => {
console.error('Error occurred:', error);
});
}
async deleteSummarizedText(textId: string) {
await this.summarizerService.deleteSummarizedText(textId)
.subscribe((response: any) => {
console.log("Summary Text Deleted Successfully");
this.getSummarizedText(this.userId);
}, (error: any) => {
console.log('Error occured', error);
});
}
}
// add-summarizer.component.ts
import { Component } from '@angular/core';
import { SummarizerService } from '../../service/summarizer.service';
import { AuthService } from '../../service/auth.service';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-add-summarizer',
standalone: true,
imports: [FormsModule, CommonModule],
templateUrl: './add-summarizer.component.html',
styleUrl: './add-summarizer.component.css'
})
export class AddSummarizerComponent {
inputText: string = '';
summarizedText: string = '';
userId: any = "";
summaryLength: number = 1;
constructor(private summarizerService: SummarizerService,
private authService: AuthService) { }
ngOnInit(): void {
}
getUserId(): string | null {
if (typeof localStorage !== 'undefined') {
const token = localStorage.getItem('token');
if (token) {
const tokenPayload = JSON.parse(atob(token.split('.')[1]));
return tokenPayload.user.id;
}
}
return null;
}
summarize() {
if (this.getUserId() && this.getUserId() !== "") {
this.userId = this.getUserId();
}
this.summarizerService.summarizeText(this.inputText,
this.userId, this.summaryLength)
.subscribe((response: any) => {
this.summarizedText = response.summary;
}, (error: any) => {
console.error('Error occurred:', error);
});
}
}
// app.component.ts
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Router, RouterOutlet } from '@angular/router';
import { AuthService } from '../app/service/auth.service';
import { SharedService } from './service/shared.service';
import { SidebarComponent } from './component/sidebar/sidebar.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, FormsModule, CommonModule, SidebarComponent],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
title = 'w3wiki Summarizer';
isLoggedIn: boolean = false;
constructor(private router: Router,
private authService: AuthService,
private sharedService: SharedService) { }
ngOnInit(): void {
this.authService.loggedInEvent.subscribe((data: any) => {
this.isLoggedIn = true;
});
if (typeof localStorage !== 'undefined' &&
localStorage.getItem('token')) {
this.isLoggedIn = true;
}
}
login(): void {
this.sharedService.triggerLoginEvent();
this.router.navigate(["/"]);
}
register(): void {
this.sharedService.triggerRegisterEvent();
this.router.navigate(["/"]);
}
logout(): void {
this.isLoggedIn = false;
localStorage.removeItem('token');
this.router.navigate(["/"]);
}
loadContent(page: any) {
if (page === "summarizedText") {
this.router.navigate(["/getSummarizedText"]);
}
else if (page === "summarizeHere") {
this.router.navigate(["/summarized-text"]);
}
}
}
// app.routes.ts
import {
Routes
} from '@angular/router';
import {
AuthComponent
} from './component/auth/auth.component';
import {
SummarizerComponent
} from './component/summarizer/summarizer.component';
import {
AddSummarizerComponent
} from './component/add-summarizer/add-summarizer.component';
export const routes: Routes = [
{ path: "", component: AuthComponent },
{
path: "summarized-text",
component: AddSummarizerComponent
},
{
path: "getSummarizedText",
component: SummarizerComponent
}
];
// app.module.ts
import {
InjectionToken,
NgModule
} from '@angular/core';
import {
BrowserModule
} from '@angular/platform-browser';
import {
FormsModule
} from '@angular/forms';
import {
AppComponent
} from './app.component';
import {
JwtHelperService,
JWT_OPTIONS
} from '@auth0/angular-jwt';
import {
RouterModule
} from '@angular/router';
import {
routes
} from './app.routes';
import {
AuthComponent
} from './component/auth/auth.component';
import {
SummarizerComponent
} from './component/summarizer/summarizer.component';
import {
SidebarComponent
} from './component/sidebar/sidebar.component';
import {
AddSummarizerComponent
} from './component/add-summarizer/add-summarizer.component';
@NgModule({
declarations: [
AppComponent,
AuthComponent,
SummarizerComponent,
SidebarComponent,
AddSummarizerComponent
],
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot(routes),
],
exports: [RouterModule],
providers: [{
provide: JWT_OPTIONS,
useValue: JWT_OPTIONS
}, JwtHelperService],
bootstrap: [AppComponent]
})
export class AppModule { }
// shared.service.ts
import {
EventEmitter,
Injectable
} from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class SharedService {
loginEvent: EventEmitter<void> = new EventEmitter<void>();
registerEvent: EventEmitter<void> = new EventEmitter<void>();
constructor() { }
triggerLoginEvent(): void {
this.loginEvent.emit();
}
triggerRegisterEvent(): void {
this.registerEvent.emit();
}
}
// auth.service.ts
import { HttpClient } from '@angular/common/http';
import { EventEmitter, Injectable } from '@angular/core';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private baseUrl = 'http://localhost:5000/api/auth';
constructor(private httpClient: HttpClient) { }
register(userData: any): Observable<any> {
return this.httpClient.post(`${this.baseUrl}/register`,
userData);
};
login(credentials: any): Observable<any> {
return this.httpClient.post(`${this.baseUrl}/login`,
credentials);
};
loggedInEvent: EventEmitter<any> = new EventEmitter();
emitLoggedInEvent() {
this.loggedInEvent.emit();
}
}
// summarizer.service.ts
import {
HttpClient,
HttpHeaders
} from '@angular/common/http';
import {
Injectable
} from '@angular/core';
import {
Observable
} from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class SummarizerService {
private baseUrl = "http://localhost:5000/api/summarize";
constructor(private http: HttpClient) { }
summarizeText(inputText: string,
userId: string,
summaryLength: number): Observable<any> {
return this.http.post<any>(`${this.baseUrl}/summarized-text`, {
text: inputText,
userId: userId,
summaryLength: summaryLength
});
}
getSummarizedText(userId: string): Observable<any> {
return this.http.get<any>(`${this.baseUrl}/summarized-text/${userId}`);
}
deleteSummarizedText(textId: string): Observable<void> {
return this.http.delete<void>(`${this.baseUrl}/deleteText/${textId}`);
}
}
Output:
Summarizer Website using MEAN Stack
This article aims to develop a project using MEAN stack which will work as a summarizer website. MEAN stack technologies include MongoDB, AngularJS, NodeJS, and Express. It will give you a thorough understanding of how to create a MEAN stack application end to end from backend to frontend including the user authentication feature.
Output Preview: Let us have a look at how the final output will look like.
Contact Us