Address Book using MEAN
It is important to have an efficient way to manage contacts for personal and professional life. Building an address book application can be a rewarding project, allowing you to learn the ins and outs of full-stack web development while creating a useful tool. In this article, we’ll explore how to build an address book application using the MEAN stack, comprising MongoDB, Express.js, Angular, and Node.js.
Project Preview:
Prerequisites:
Approach to Create Address Book using MEAN Stack
Backend:
- Set up a new node js project
- Set up the server using express with CORS as the middleware in file server.js
- Create the app instance using const app = express()
- Create controllers folder which will define the API methods
- Create models folder to create the database schema for address-book and user
- Create router folder and mention all the routes related to user login, register, creating, retrieving, updating and deleting the address in an address book
- Set up local Mongo DB database
- Set up the connection to local Mongo DB in server.js file
- Create collections within the database to store and retrieve the data from database
- Two collections are created – address-book, user
- Implement the core logic of creating, retrieving, updating and deleting the address
- Test the API endpoints using postman
Frontend:
- Create a new angular project
- Create components folder and seperate components for seperate routes
- Create HTML, CSS and ts files for all the components
- Create service to establish communication between frontend and backend routes
- Create various routes in app.routes.ts folder
- Test the frontend application in browser at https://localhost:3000
Steps to Create Project
Step 1: Create the main folder for complete project
mkdir address-book
cd address-book
Step 2: Initialize the node.js project
npm init -y
Step 3: Install the required dependencies
npm install express mongoose jsonwebtoken bcryptjs nodemon cors body-parser
Folder Structure (Backend):
The updated dependencies in package.json file of backend will look like:
"dependencies": {
"@auth0/angular-jwt": "^5.2.0",
"bcryptjs": "^2.4.3",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.3.0",
"nodemon": "^3.1.0"
}
Example: Create the required files as seen on the project structure and add the following codes.
// controller/addressController.js
const Address = require("../model/Address");
const jwt = require("jsonwebtoken");
const secretKey = "jwtSecret";
exports.getAllAddress = async (req, res) => {
let userId;
try {
jwt.verify(
req.headers["authorization"].substring(7),
secretKey,
(error, decodedToken) => {
if (error) {
res.status(401).json({
success: false,
message: error.message,
});
} else {
userId = decodedToken.user.id;
}
}
);
const addresses = await Address.find();
res.status(200).json(addresses);
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
exports.getAddressById = async (req, res) => {
try {
const id = req.params.id;
const address = await Address.findById(id);
res.status(200).json(address);
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
exports.createAddress = async (req, res) => {
try {
let address = {};
const {
firstname,
lastname,
email,
phone,
addressline,
state,
pincode,
country,
dob,
} = req.body;
if (
req.headers["authorization"] &&
req.headers["authorization"].startsWith("Bearer ")
) {
jwt.verify(
req.headers["authorization"].substring(7),
secretKey,
(error, decodedToken) => {
if (error) {
res.status(401).json({
success: false,
message: error.message,
});
} else {
address = new Address({
user: decodedToken.user.id,
firstname,
lastname,
email,
phone,
addressline,
state,
pincode,
country,
dob,
});
}
}
);
await address.save();
res.status(200).json({
success: true,
address: address,
});
}
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
exports.updateAddress = async (req, res) => {
try {
const { id } = req.params;
const {
firstname,
lastname,
email,
phone,
addressline,
state,
pincode,
country,
dob,
} = req.body;
const address = await Address.findByIdAndUpdate(
id,
{
firstname,
lastname,
email,
phone,
addressline,
state,
pincode,
country,
dob,
},
{ new: true }
);
res.status(201).json({
success: true,
address: address,
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
exports.deleteAddress = async (req, res) => {
try {
const { id } = req.params;
await Address.findByIdAndDelete(id);
res.status(200).json({
success: true,
message: "Address Deleted Successfully",
});
} catch (error) {
res.status(500).json({
success: false,
message: "Error while deleting address",
});
}
};
// controller/authController.js
const User = require("../model/User");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
exports.register = async (req, res) => {
try {
const { username, email, password } = req.body;
let user = await User.findOne({ email });
if (user) {
return res.status(400).json({
message: "User Already Exist",
success: false,
});
}
user = new User({
username: username,
email: email,
password: password,
});
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
await user.save();
const token = generateJwtToken(user.id);
res.status(201).json({
success: true,
token: token,
message: "User registered successfully",
});
} catch (error) {
res.status(500).json({
message: "Server error! New user registration failed",
success: false,
});
}
};
exports.login = async (req, res) => {
try {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user) {
return res.status(400).json({
message: "Invalid credentials",
success: false,
});
}
const isMatched = await bcrypt.compare(password, user.password);
if (!isMatched) {
return res.status(400).json({
message: "Invalid credentials",
success: false,
});
}
const token = generateJwtToken(user.id);
res.status(200).json({
success: true,
message: "User logged in successfully",
token: token,
});
} catch (error) {
return res.status(500).json({
success: false,
message: "Internal Server Error, Login unsuccessful",
});
}
};
function generateJwtToken(userID) {
const payload = {
user: {
id: userID,
},
};
return jwt.sign(payload, "jwtSecret", { expiresIn: 3600 });
}
exports.getUserDetailsFronUserId = async (req, res) => {
try {
const { id } = req.params;
const user = await User.findById(id);
return res.status(200).json(user);
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
// model/Address.js
const mongoose = require('mongoose');
const addressSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.ObjectId,
ref: "User",
required: true
},
firstname: {
type: String,
required: true
},
lastname: {
type: String,
required: true
},
email: {
type: String,
required: true
},
phone: {
type: String,
required: true
},
addressline: {
type: String,
required: true
},
state: {
type: String,
required: true
},
pincode: {
type: String,
required: true
},
country: {
type: String,
required: true
},
dob: {
type: Date,
required: true
}
});
module.exports = mongoose.model('Address', addressSchema);
// model/User.js
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true
},
email: {
type: String,
required: true,
uniques: true
},
password: {
type: String,
required: true
}
});
module.exports = mongoose.model('User', userSchema);
// route/addressRoutes.js
const express = require("express");
const router = express.Router();
const addressController = require("../controller/addressController");
router.get("/getAllAddress", addressController.getAllAddress);
router.get("/getAddressById/:id", addressController.getAddressById);
router.post("/createAddress", addressController.createAddress);
router.put("/updateAddress/:id", addressController.updateAddress);
router.delete("/deleteAddress/:id", addressController.deleteAddress);
module.exports = router;
//route/authRoutes.js
const express = require("express");
const router = express.Router();
const authController = require("../controller/authController");
router.post("/register", authController.register);
router.post("/login", authController.login);
router.get("/:id", authController.getUserDetailsFronUserId);
module.exports = router;
// server.js
const express = require("express");
const cors = require("cors");
const mongoose = require("mongoose");
const authRoutes = require("../backend/route/authRoutes");
const addressRoutes = require("../backend/route/addressRoutes");
const app = express();
app.use(cors());
app.use(express.json());
mongoose
.connect("mongodb://localhost:27017/address-book", {
family: 4,
})
.then(() => console.log("Mongo DB connected"))
.catch((err) => console.log(err));
app.use("/api/auth", authRoutes);
app.use("/api/address", addressRoutes);
const PORT = 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
Step 4: To start the backend run the following command.
nodemon server.js
Step 5: Install the angular CLI
npm install -g @angular/cli
Step 6: Create a new angular project
ng new frontend
Step 7: Create components for different functionalities in angular
Syntax - ng generate component <component-name>
ng generate component user
ng generate component address-create
ng generate component address
Step 8: Create services for communication between frontend and backend
Syntax - ng generate service <service-name>
ng generate service auth
ng generate service address
ng generate service shared
Folder 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.
<!-- address.compoennt.html -->
<div class="container">
<a class="link-button" (click)="showAddFormFunction()">Create New Address</a>
<h2>List of Address</h2>
<div
*ngIf="displayedAddressList.length > 0; else noAddress"
class="table-container"
>
<table class="address-table">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email ID</th>
<th>Phone</th>
<th>Address Line</th>
<th>State</th>
<th>PIN Code</th>
<th>Country</th>
<th>Date of Birth</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let address of displayedAddressList">
<td>{{ address.firstname }}</td>
<td>{{ address.lastname }}</td>
<td>{{ address.email }}</td>
<td>{{ address.phone }}</td>
<td>{{ address.addressline }}</td>
<td>{{ address.state }}</td>
<td>{{ address.pincode }}</td>
<td>{{ address.country }}</td>
<td>{{ address.dob }}</td>
<td>
<button class="btn" (click)="getAddressById(address._id)">
Get Adress
</button>
<button
class="btn"
(click)="populateUpdateForm(address)"
*ngIf="address.user === getUserId()"
>
Update
</button>
<button
class="btn delete-btn"
(click)="confirmDelete(address._id)"
*ngIf="address.user === getUserId()"
>
Delete
</button>
</td>
</tr>
</tbody>
</table>
</div>
<ng-template #noAddress>
<div class="no-address-container">
<p>No Address Available</p>
</div>
</ng-template>
<div *ngIf="showUpdateForm" class="update-form-container">
<button class="close-button" (click)="cancelUpdate()">X</button>
<h2>Update Address</h2>
<form class="update-form" (ngSubmit)="updateAddress(addressUpdated._id)">
<div>
<label for="updateFirstName">First Name:</label>
<input
type="text"
id="updateFirstName"
name="updateFirstName"
[(ngModel)]="addressUpdated.firstname"
required
/>
</div>
<div>
<label for="updateLastName">Last Name:</label>
<input
type="text"
id="updateLastName"
name="updateLastName"
[(ngModel)]="addressUpdated.lastname"
required
/>
</div>
<div>
<label for="updateEmail">Email ID:</label>
<input
type="email"
id="updateEmail"
name="updateEmail"
[(ngModel)]="addressUpdated.email"
required
/>
</div>
<div>
<label for="updatePhone">Phone Number:</label>
<input
type="number"
id="updatePhone"
name="updatePhone"
[(ngModel)]="addressUpdated.phone"
required
/>
</div>
<div>
<label for="updateAddressLine">Address Line:</label>
<input
type="text"
id="updateAddressLine"
name="updateAddressLine"
[(ngModel)]="addressUpdated.addressline"
required
/>
</div>
<div>
<label for="updateState">Address Line:</label>
<input
type="text"
id="updateState"
name="updateState"
[(ngModel)]="addressUpdated.state"
required
/>
</div>
<div>
<label for="updatePincode">Address Line:</label>
<input
type="number"
id="updatePincode"
name="updatePincode"
[(ngModel)]="addressUpdated.pincode"
required
/>
</div>
<div>
<label for="updateCountry">Address Line:</label>
<input
type="text"
id="updateCountry"
name="updateCountry"
[(ngModel)]="addressUpdated.country"
required
/>
</div>
<div>
<label for="updateDOB">Date Of Birth:</label>
<input
type="date"
id="updateDOB"
name="updateDOB"
[(ngModel)]="addressUpdated.dob"
required
/>
</div>
<div>
<button type="submit">Update Address</button>
<button type="button" (click)="cancelUpdate()">Cancel</button>
</div>
</form>
</div>
<div
*ngIf="!showUpdateForm && addressById._id"
class="view-address-container"
>
<button class="close-button" (click)="closeView()">X</button>
<h2>View Address</h2>
<form class="view-address-form">
<div>
<label for="viewFirstName">First Name:</label>
<input
type="text"
id="viewFirstName"
name="viewFirstName"
[(ngModel)]="addressById.firstname"
required
/>
</div>
<div>
<label for="viewLastName">Last Name:</label>
<input
type="text"
id="viewLastName"
name="viewLastName"
[(ngModel)]="addressById.lastname"
required
/>
</div>
<div>
<label for="viewEmail">Email ID:</label>
<input
type="email"
id="viewEmail"
name="viewEmail"
[(ngModel)]="addressById.email"
required
/>
</div>
<div>
<label for="viewPhone">Phone Number:</label>
<input
type="number"
id="viewPhone"
name="viewPhone"
[(ngModel)]="addressById.phone"
required
/>
</div>
<div>
<label for="viewAddressLine">Address Line:</label>
<input
type="text"
id="viewAddressLine"
name="viewAddressLine"
[(ngModel)]="addressById.addressline"
required
/>
</div>
<div>
<label for="viewState">State:</label>
<input
type="text"
id="viewState"
name="viewState"
[(ngModel)]="addressById.state"
required
/>
</div>
<div>
<label for="viewPincode">PIN Code:</label>
<input
type="text"
id="viewPincode"
name="viewPincode"
[(ngModel)]="addressById.pincode"
required
/>
</div>
<div>
<label for="viewCountry">Country:</label>
<input
type="text"
id="viewCountry"
name="viewCountry"
[(ngModel)]="addressById.country"
required
/>
</div>
<div>
<label for="viewDOB">Date Of Birth:</label>
<input
type="date"
id="viewDOB"
name="viewDOB"
[(ngModel)]="addressById.dob"
required
/>
</div>
<div>
<a href="/getAllAddress" class="link-button" (click)="closeView()"
>Get All Address</a
>
</div>
</form>
</div>
</div>
<!-- address-create.compoentn.html -->
<div class="container">
<div class="create-form-container">
<h2>Create New Address</h2>
<form class="create-form" (ngSubmit)="createAddress()">
<div>
<label for="firstName">First Name:</label>
<input
type="text"
id="firstName"
name="firstName"
[(ngModel)]="addressCreated.firstname"
required
/>
</div>
<div>
<label for="lastName">Last Name:</label>
<input
type="text"
id="lastName"
name="lastName"
[(ngModel)]="addressCreated.lastname"
required
/>
</div>
<div>
<label for="email">Email:</label>
<input
type="email"
id="email"
name="email"
[(ngModel)]="addressCreated.email"
required
/>
</div>
<div>
<label for="phone">Phone:</label>
<input
type="number"
id="phone"
name="phone"
[(ngModel)]="addressCreated.phone"
required
/>
</div>
<div>
<label for="addressLine">Address Line:</label>
<input
type="text"
id="addressLine"
name="addressLine"
[(ngModel)]="addressCreated.addressline"
required
/>
</div>
<div>
<label for="state">State:</label>
<input
type="text"
id="state"
name="state"
[(ngModel)]="addressCreated.state"
required
/>
</div>
<div>
<label for="pinCode">PIN Code:</label>
<input
type="number"
id="pinCode"
name="pinCode"
[(ngModel)]="addressCreated.pincode"
required
/>
</div>
<div>
<label for="country">Country:</label>
<input
type="text"
id="country"
name="country"
[(ngModel)]="addressCreated.country"
required
/>
</div>
<div>
<label for="dob">Date Of Birth:</label>
<input
type="date"
id="dob"
name="dob"
[(ngModel)]="addressCreated.dob"
required
/>
</div>
<div>
<button type="submit">Add Address</button>
<button type="button" (click)="resetForm()">Clear</button>
<a href="/getAllAddress" class="link-button">Get All Address</a>
</div>
</form>
</div>
</div>
<!-- user.component.hmtl -->
<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">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>
<!-- app.component.html -->
<main class="main">
<div class="content">
<div class="left-side">
<h1>{{ title }}</h1>
<div>
<ul>
<li><a (click)="login()" *ngIf="!isLoggedIn">Login</a></li>
<li><a (click)="register()" *ngIf="!isLoggedIn">Register</a></li>
<li><a (click)="logout()" *ngIf="isLoggedIn">Logout</a></li>
</ul>
</div>
</div>
</div>
</main>
<router-outlet> </router-outlet>
/* app.component.css */
.main {
display: flex;
flex-direction: column;
align-items: center;
background-color: darkslategrey;
color: white;
}
.content {
width: 100%;
max-width: 1200px;
padding: 20px;
}
.left-side {
display: flex;
justify-content: space-between;
align-items: center;
}
.left-side h1 {
margin: 0;
margin-left: 3%;
}
.left-side ul {
list-style-type: none;
padding: 0;
margin: 0;
}
.left-side li {
display: inline;
margin-right: 20px;
}
.left-side li a {
text-decoration: none;
color: white;
font-weight: bold;
font-size: 1.5rem;
}
.left-side li a:hover {
color: lightgray;
}
a {
cursor: pointer;
}
/* address.component.css */
.container {
max-width: 100%;
margin: 0 auto;
padding: 20px;
}
.address-table {
width: 100%;
border-collapse: collapse;
}
.address-table th,
.address-table td {
padding: 10px;
border: 1px solid #ccc;
text-align: center;
}
h2 {
text-align: center;
}
.address-table th {
background-color: #f0f0f0;
}
.update-form-container,
.view-address-container {
margin-top: 20px;
padding: 20px;
border: 1px solid #ccc;
}
.update-form,
.view-address-form {
display: flex;
flex-direction: column;
}
.update-form input,
.view-address-form input {
margin-bottom: 10px;
}
.btn {
padding: 8px 16px;
margin-right: 10px;
margin-bottom: 1.5vmax;
cursor: pointer;
background-color: #0056b3;
color: #fff;
border: none;
border-radius: 4px;
}
.delete-btn {
background-color: #dc3545;
}
.link-button {
display: inline-block;
padding: 8px 16px;
background-color: #1b599a;
color: #fff;
text-decoration: none;
border-radius: 4px;
cursor: pointer;
}
.link-button-clear {
display: inline-block;
padding: 8px 16px;
background-color: #dc3545;
color: #fff;
text-decoration: none;
border-radius: 4px;
cursor: pointer;
border: none;
}
.no-address-container {
margin-top: 20px;
padding: 10px;
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
border-radius: 4px;
}
.no-address-container p {
text-align: center;
margin: 0;
}
/* Update Activity */
.update-form-container {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 90%;
background-color: rgba(255, 255, 255, 1);
padding: 3vmax;
border-radius: 5px;
overflow-y: auto;
}
.update-form-container h2 {
text-align: center;
font-size: 2rem;
}
.update-form-container .close-btn {
position: absolute;
top: 15px;
right: 5px;
font-size: 18px;
color: #555;
cursor: pointer;
}
.update-form-container .close-btn:hover {
color: #333;
}
.update-form label {
display: block;
margin-bottom: 5px;
}
.update-form input[type="text"],
.update-form input[type="number"],
.update-form input[type="date"],
.update-form input[type="email"] {
width: calc(100% - 12px);
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
.update-form button[type="submit"],
.update-form button[type="button"] {
width: 10%;
padding: 10px;
background-color: green;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
margin-right: 10px;
}
.update-form button[type="button"] {
background-color: #dc3545;
}
.view-address-container {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 90%;
background-color: rgba(255, 255, 255, 1);
padding: 3vmax;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.view-address-container h2 {
text-align: center;
font-size: 2rem;
}
.view-address-form label {
display: block;
margin-bottom: 5px;
}
.view-address-form input[type="text"],
.view-address-form input[type="number"],
.view-address-form input[type="date"],
.view-address-form input[type="email"] {
width: calc(100% - 12px);
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
.view-address-form button[type="submit"],
.view-address-form a {
width: 10%;
padding: 10px;
background-color: #0056b3;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
text-align: center;
display: inline-block;
text-decoration: none;
}
.close-button {
position: absolute;
top: 20px;
right: 10px;
padding: 5px 10px;
background-color: #ccc;
border: none;
border-radius: 5px;
cursor: pointer;
}
.close-button:hover {
background-color: #aaa;
}
/* address-create.component.css */
.container {
max-width: 100%;
margin: 0 auto;
padding: 20px;
}
.create-form-container {
width: 90%;
background-color: rgba(255, 255, 255, 1);
border-radius: 5px;
margin: 10px auto;
}
.create-form-container h2 {
text-align: center;
font-size: 2rem;
}
.create-form-container {
color: #333;
}
.create-form label {
display: block;
margin-bottom: 5px;
}
.create-form input[type="text"],
.create-form input[type="number"],
.create-form input[type="date"],
.create-form input[type="email"] {
width: calc(100% - 12px);
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
.create-form button[type="submit"],
.create-form button[type="button"] {
width: auto;
padding: 10px 20px;
background-color: green;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
margin-right: 10px;
}
.create-form button[type="button"] {
background-color: #dc3545;
}
.create-form a {
width: 10%;
padding: 10px;
background-color: #0056b3;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
text-align: center;
display: inline-block;
text-decoration: none;
}
/* user.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;
margin-top: 10px;
}
.success-message {
color: green;
background-color: rgb(186, 218, 186);
padding: 10px;
border: 1px solid green;
border-radius: 5px;
margin-bottom: 10px;
margin-top: 10px;
}
// Address.component.ts
import { Component, OnInit } from "@angular/core";
import { AddressService } from "../address.service";
import { AuthService } from "../auth.service";
import { Router } from "@angular/router";
import { SharedService } from "../shared.service";
import { FormsModule } from "@angular/forms";
import { CommonModule } from "@angular/common";
@Component({
selector: "app-address",
standalone: true,
imports: [FormsModule, CommonModule],
templateUrl: "./address.component.html",
styleUrl: "./address.component.css",
})
export class AddressComponent implements OnInit {
isLoggedIn: boolean = false;
addressList: any[] = [];
displayedAddressList: any[] = [];
addressById: any = {};
addressUpdated: any = {};
showUpdateForm: boolean = false;
errorMessage: string = "";
constructor(
private addressService: AddressService,
private userService: AuthService,
private router: Router,
private sharedService: SharedService
) { }
ngOnInit(): void {
if (typeof localStorage !== "undefined" && localStorage.getItem("token")) {
this.getAllAddresses();
}
}
ifLoggedIn(): boolean {
this.userService.isAuthenticated().subscribe((isAuthenticated: boolean) => {
this.isLoggedIn = isAuthenticated;
});
return this.isLoggedIn;
}
getUserId(): string | null {
if (typeof localStorage !== "undefined") {
const token = localStorage.getItem("token");
if (token) {
const tokenPayload = JSON.parse(atob(token.split(".")[1]));
console.log(tokenPayload);
return tokenPayload.user.id;
}
}
return null;
}
getAllAddresses(): void {
if (typeof localStorage !== "undefined") {
const token = localStorage.getItem("token");
if (token) {
this.addressService
.getAllAddress(token)
.subscribe((addressList: any) => {
this.addressList = addressList;
this.displayedAddressList = [...this.addressList];
});
}
}
}
getAddressById(id: string): void {
if (typeof localStorage !== "undefined") {
const token = localStorage.getItem("token");
if (token) {
this.showUpdateForm = false;
this.addressService
.getAddressById(id, token)
.subscribe((addressById: any) => {
this.addressById = addressById;
this.addressById.dob = this.addressById.dob.toString().slice(0, 10);
});
}
}
}
closeView(): void {
this.addressById = {};
}
showAddFormFunction(): void {
this.router.navigate(["/createAddress"]);
}
populateUpdateForm(address: any) {
this.addressUpdated = { ...address };
this.addressUpdated.dob = this.addressUpdated.dob.toString().slice(0, 10);
this.showUpdateForm = true;
}
updateAddress(id: string): any {
if (typeof localStorage !== "undefined") {
const token = localStorage.getItem("token");
if (token) {
this.addressService.updateAddress(this.addressUpdated, token).subscribe(
(addressUpdated: any) => {
const index = this.displayedAddressList.findIndex(
(p) => p._id === id
);
if (index !== -1) {
this.addressList[index] = addressUpdated;
this.displayedAddressList[index] = addressUpdated;
this.getAllAddresses();
this.showUpdateForm = false;
this.router.navigate(["/getAllAddress"]);
}
this.cancelUpdate();
},
(error) => {
this.errorMessage = "Error Updating Activity";
}
);
}
}
return this.addressUpdated;
}
cancelUpdate(): void {
this.showUpdateForm = false;
this.addressUpdated = {};
this.addressById = {};
}
confirmDelete(addressId: string): void {
const confirmDelete = window.confirm(
"Are you sure you want to delete this address?"
);
if (confirmDelete) {
this.deleteAddress(addressId);
}
}
deleteAddress(id: string): void {
if (typeof localStorage !== "undefined") {
const token = localStorage.getItem("token");
if (token) {
this.addressService.deleteAddress(id, token).subscribe(
() => {
this.addressList = this.addressList.filter(
(address: any) => address._id !== id
);
this.displayedAddressList = [...this.addressList];
},
(error) => {
this.errorMessage = "Error Deleting Address";
}
);
}
}
}
}
// Address-create.compoennt.ts
import { Component } from "@angular/core";
import { AddressService } from "../address.service";
import { AuthService } from "../auth.service";
import { Router } from "@angular/router";
import { SharedService } from "../shared.service";
import { FormsModule } from "@angular/forms";
import { CommonModule } from "@angular/common";
@Component({
selector: "app-address-create",
standalone: true,
imports: [FormsModule, CommonModule],
templateUrl: "./address-create.component.html",
styleUrl: "./address-create.component.css",
})
export class AddressCreateComponent {
isLoggedIn: boolean = false;
addressCreated: any = {};
showAddForm: boolean = false;
errorMessage: string = "";
constructor(
private addressService: AddressService,
private userService: AuthService,
private router: Router,
private sharedService: SharedService
) { }
ngOnInit(): void { }
createAddress(): void {
if (typeof localStorage !== "undefined") {
const token = localStorage.getItem("token");
if (token) {
this.addressService
.createAddress(this.addressCreated, token)
.subscribe((addressCreated) => {
this.addressCreated = addressCreated;
this.resetForm();
this.router.navigate(["/getAllAddress"]);
});
}
}
}
resetForm(): void {
this.addressCreated = {};
}
}
// User.component.ts
import { Component, OnInit } from "@angular/core";
import { AuthService } from "../auth.service";
import { Router } from "@angular/router";
import { SharedService } from "../shared.service";
import { FormsModule } from "@angular/forms";
import { CommonModule } from "@angular/common";
@Component({
selector: "app-user",
standalone: true,
imports: [FormsModule, CommonModule],
templateUrl: "./user.component.html",
styleUrl: "./user.component.css",
})
export class UserComponent implements OnInit {
username!: string;
email!: string;
password!: string;
credentials: any = {};
successMessage: string = "";
errorMessage: string = "";
loginActive: boolean = true;
registerActive: boolean = false;
constructor(
private userService: AuthService,
private router: Router,
private sharedService: SharedService
) { }
ngOnInit(): void {
this.sharedService.loginEvent.subscribe(() => {
this.loginActive = true;
this.registerActive = false;
this.username = "";
this.email = "";
this.password = "";
this.successMessage = "";
this.errorMessage = "";
});
this.sharedService.registerEvent.subscribe(() => {
this.registerActive = true;
this.loginActive = false;
this.username = "";
this.email = "";
this.password = "";
this.successMessage = "";
this.errorMessage = "";
});
}
login(): void {
const credentials = {
email: this.email,
password: this.password,
};
this.userService.login(credentials).subscribe(
(response: any) => {
const token = response.token;
localStorage.setItem("token", token);
this.userService.setAuthenticationStatus(true);
this.userService.emitLoggedInEvent();
this.loginActive = false;
this.registerActive = false;
this.router.navigate(["/getAllAddress"]);
this.successMessage = "User logged in successfully.";
this.errorMessage = "";
},
(error: any) => {
console.error("Error logging in:", error);
this.errorMessage =
"Login unsuccessfull ! Please reload or try in incognito tab";
this.successMessage = "";
}
);
}
register(): void {
const userData = {
username: this.username,
email: this.email,
password: this.password,
};
this.userService.register(userData).subscribe(
(response: any) => {
this.successMessage = response.message;
this.errorMessage = "";
this.loginActive = true;
this.registerActive = false;
},
(error: any) => {
this.errorMessage = "User not registered successfully";
this.successMessage = "";
}
);
}
}
// Address.service.ts
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs";
@Injectable({
providedIn: "root",
})
export class AddressService {
private baseUrl = "http://localhost:5000/api/address";
constructor(private httpClient: HttpClient) { }
getAllAddress(token: string): Observable<any> {
const headers = new HttpHeaders({
Authorization: `Bearer ${token}`,
});
return this.httpClient.get<any>(`${this.baseUrl}/getAllAddress`, {
headers,
});
}
getAddressById(id: string, token: string): Observable<any> {
const headers = new HttpHeaders({
Authorization: `Bearer ${token}`,
});
return this.httpClient.get<any>(`${this.baseUrl}/getAddressById/${id}`, {
headers,
});
}
createAddress(address: any, token: string): Observable<any> {
const headers = new HttpHeaders({
Authorization: `Bearer ${token}`,
});
return this.httpClient.post<any>(`${this.baseUrl}/createAddress`, address, {
headers,
});
}
updateAddress(address: any, token: string): Observable<any> {
const headers = new HttpHeaders({
Authorization: `Bearer ${token}`,
});
return this.httpClient.put<any>(
`${this.baseUrl}/updateAddress/${address._id}`,
address,
{ headers }
);
}
deleteAddress(id: string, token: string): Observable<void> {
const headers = new HttpHeaders({
Authorization: `Bearer ${token}`,
});
return this.httpClient.delete<void>(`${this.baseUrl}/deleteAddress/${id}`, {
headers,
});
}
}
// Auth.service.ts
import { HttpClient } from "@angular/common/http";
import { EventEmitter, Injectable } from "@angular/core";
import { BehaviorSubject, 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);
}
private isAuthenticatedSubject = new BehaviorSubject<boolean>(false);
isAuthenticated(): Observable<boolean> {
return this.isAuthenticatedSubject.asObservable();
}
setAuthenticationStatus(isAuthenticated: boolean): void {
this.isAuthenticatedSubject.next(isAuthenticated);
}
loggedInEvent: EventEmitter<any> = new EventEmitter();
emitLoggedInEvent() {
this.loggedInEvent.emit();
}
}
// 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();
}
}
// app.component.ts
import { Component } from "@angular/core";
import { Router, RouterOutlet } from "@angular/router";
import { SharedService } from "./shared.service";
import { AuthService } from "./auth.service";
import { FormsModule } from "@angular/forms";
import { CommonModule } from "@angular/common";
@Component({
selector: "app-root",
standalone: true,
imports: [RouterOutlet, FormsModule, CommonModule],
templateUrl: "./app.component.html",
styleUrl: "./app.component.css",
})
export class AppComponent {
title = "w3wiki Address Book";
isLoggedIn: boolean = false;
constructor(
private router: Router,
private userService: AuthService,
private sharedService: SharedService
) { }
ngOnInit(): void {
this.userService.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.userService.setAuthenticationStatus(false);
this.isLoggedIn = false;
localStorage.removeItem("token");
this.router.navigate(["/"]);
}
}
// app.routes.ts
import { Routes } from "@angular/router";
import { AddressComponent } from "./address/address.component";
import { AddressCreateComponent } from "./address-create/address-create.component";
import { UserComponent } from "./user/user.component";
export const routes: Routes = [
{ path: "", component: UserComponent },
{ path: "getAllAddress", component: AddressComponent },
{ path: "getAddressById/:id", component: AddressComponent },
{ path: "createAddress", component: AddressCreateComponent },
{ path: "updateAddress/:id", component: AddressComponent },
{ path: "deleteAddress/:id", component: AddressComponent },
{ path: "**", redirectTo: "/" },
];
// 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 { AddressComponent } from './address/address.component';
import { AddressCreateComponent } from './address-create/address-create.component';
import { UserComponent } from './user/user.component';
@NgModule({
declarations: [
AppComponent,
UserComponent,
AddressComponent,
AddressCreateComponent,
],
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot(routes),
],
exports: [RouterModule],
providers: [{ provide: JWT_OPTIONS, useValue: JWT_OPTIONS }, JwtHelperService],
bootstrap: [AppComponent]
})
export class AppModule { }
To start the angular application run the following command.
ng serve
Output: