Open In App

Event Management Web App using MEAN

Last Updated : 17 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

In this guide, we’ll walk through the step-by-step process of building a feature-rich Event Management Web App. We will make use of the MEAN stack, i.e. MongoDB, ExpressJS, Angular and NodeJS, to build this project.

Project Preview:

Event Managemenr app using MEAN Stack - Preview

Final Output of Event Management App

Prerequisites:

Approach to create Event Management Web App

  • Define the structure of an event using Mongoose schemas in a model file (e.g., `Event.js`).
  • Develop routes for handling Create, Read, Update, and Delete (CRUD) operations in a dedicated `eventRoutes.js` file.
  • Set up a MongoDB database and establish a connection in your Express application.
  • Create a server file (e.g., `server.js`) where Express is configured to listen on a specific port.
  • Design and implement a form inside our app component (`app.component.ts`) for adding new events.
  • Display all events using ngFor directive inside a card to display a list of events fetched from the server.
  • Style your components for an engaging user interface. We will be utilizing Tailwind CSS .

Steps to Create Application:

Step 1: Creating express app using the following command:

npm init -y

Step 2: Installing the required packages

npm install express mongoose body-parser cors

Folder Structure(backend):

backend-folder-structure

Backend Folder Structure

The updated dependencies in package.json file for backend will look like:

"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.19.2",
"mongoose": "^8.3.0"
}

Step 3: Below is the code for the backend.

JavaScript
// server.js

const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const cors = require("cors");
const eventRoutes = require("./routes/eventRoutes");

const app = express();
const PORT = process.env.PORT || 4000;

app.use(cors());
app.use(bodyParser.json());

MONGO_URI = "YOUR_MONGO_URI";

mongoose.connect(MONGO_URI).then(() => {
    console.log("Connected to MongoDB!");
});

app.use("/api", eventRoutes);

app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});
JavaScript
// routes/eventRoutes.js

const express = require("express");
const router = express.Router();
const Event = require("../models/Event");

router.get("/getAll", async (req, res) => {
    try {
        const events = await Event.find();
        res.json(events);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

router.get("/get/:id", async (req, res) => {
    try {
        const event = await Event.findById(req.params.id);
        res.json(event);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

router.post("/add", async (req, res) => {
    const event = new Event({
        title: req.body.title,
        location: req.body.location,
        date: req.body.date,
        remind: req.body.remind,
    });
    try {
        const newEvent = await event.save();
        console.log(newEvent);
        res.status(201).json(newEvent);
    } catch (error) {
        res.status(400).json({ message: error.message });
    }
});

router.delete("/delete/:id", async (req, res) => {
    try {
        await Event.findByIdAndDelete(req.params.id);
        res.json({ message: "Event deleted" });
    } catch (error) {
        console.error("Error deleting event:", error);
        res.status(500).json({ message: error.message });
    }
});

router.put("/update/:id", async (req, res) => {
    const eventId = req.params.id;
    const { title, date, location, remind } = req.body;

    try {
        const event = await Event.findById(eventId);
        if (!event) {
            return res.status(404).json({ message: "Event not found" });
        }

        event.title = title;
        event.date = date;
        event.location = location;
        event.remind = remind;

        await event.save();
        console.log(event);
        res.json(event);
    } catch (error) {
        console.error("Error updating event:", error);
        res.status(500).json({ message: "Internal Server Error" });
    }
});

module.exports = router;
JavaScript
// models/Event.js

const mongoose = require("mongoose");

const eventSchema = new mongoose.Schema({
    title: { type: String, required: true },
    date: { type: Date, required: true },
    location: { type: String, required: true },
    remind: { type: Boolean, default: false },
});
const Event = mongoose.model("Event", eventSchema);

module.exports = Event;

Step 4: Start the backend server

node server.js

Step 5: Create Angular Project using the following command.

ng new frontend

NOTE: While creating the project, choose ‘Sass (SCSS)’ when asked ‘Which stylesheet format would you like to use?’.

Step 6: Switch to the project directory:

cd frontend

Step 7: Installing the required packages:

npm install tailwindcss @tailwindcss/forms ngx-toastr

Folder Structure(frontend):

frontend-folder-structure

Frontend Folder Structure

Dependencies:

"dependencies": {
"@angular/animations": "^17.3.0",
"@angular/common": "^17.3.0",
"@angular/compiler": "^17.3.0",
"@angular/core": "^17.3.0",
"@angular/forms": "^17.3.0",
"@angular/platform-browser": "^17.3.0",
"@angular/platform-browser-dynamic": "^17.3.0",
"@angular/router": "^17.3.0",
"@tailwindcss/forms": "^0.5.7",
"ngx-toastr": "^18.0.0",
"rxjs": "~7.8.0",
"tailwindcss": "^3.4.3",
"zone.js": "~0.14.3"
}

Step 8: Create the Tailwind Configuration file (`tailwind.config.js`) using below command:

npx tailwindcss init

Step 9: Create a Angular service using below command.

ng generate service api

Step 10: Add the following codes in the required files.

HTML
<!-- src/app/app.component.html -->

<div class="flex items-center justify-center">
    <div class="w-full max-w-xl">
        <h2 class="text-center text-3xl 
        font-semibold leading-7 text-green-600 mt-2">
            Event Management Web App
        </h2>
        <div class="rounded overflow-hidden shadow-lg mt-5">
            <form class="px-6 py-4 bg-gray-100">
                <div class="space-y-12">
                    <div class="border-b border-gray-900/10 pb-4">
                        <div class="grid grid-cols-1 gap-x-6
                         gap-y-4 sm:grid-cols-6">
                            <div class="sm:col-span-6">
                                <label for="title"
                                    class="block text-sm 
                                    font-medium leading-6 text-gray-900
                                     required">Title</label>
                                <div class="mt-2">
                                    <input type="text" [(ngModel)]="title"
                                     name="title" id="title" autocomplete="title"
                                        class="block w-full rounded-md border-0
                                         py-1.5 text-gray-900 shadow-sm ring-1 
                                         ring-inset ring-gray-300 placeholder:text-gray-400
                                          focus:ring-2 focus:ring-inset 
                                          focus:ring-indigo-600 sm:text-sm sm:leading-6"
                                        placeholder="Event Title" required />
                                </div>
                            </div>

                            <div class="sm:col-span-3">
                                <label for="location"
                                    class="block text-sm font-medium leading-6
                                     text-gray-900 required">Location</label>
                                <div class="mt-2">
                                    <select [(ngModel)]="location" id="location"
                                     name="location" autocomplete="location"
                                        class="block w-full rounded-md border-0 
                                        py-1.5 text-gray-900 shadow-sm ring-1
                                         ring-inset ring-gray-300 focus:ring-2 
                                         focus:ring-inset focus:ring-indigo-600 
                                         sm:max-w-xs sm:text-sm sm:leading-6"
                                        required>
                                        <option>Select</option>
                                        <option>India</option>
                                        <option>United States</option>
                                        <option>Australia</option>
                                        <option>Japan</option>
                                        <option>United Kingdom</option>
                                        <option>France</option>
                                    </select>
                                </div>
                            </div>
                            <div class="sm:col-span-3">
                                <label for="date"
                                    class="block text-sm font-medium leading-6
                                     text-gray-900 required">Date</label>
                                <div class="mt-2">
                                    <input [(ngModel)]="date" id="date" 
                                    name="date" type="date"
                                        class="block w-full rounded-md
                                         border-0 py-1.5 text-gray-900 shadow-sm
                                          ring-1 ring-inset ring-gray-300 focus:ring-2
                                           focus:ring-inset focus:ring-indigo-600
                                            sm:max-w-xs sm:text-sm sm:leading-6"
                                        required />
                                </div>
                            </div>
                            <div class="sm:col-span-6">
                                <div class="relative flex gap-x-3">
                                    <div class="flex h-6 items-center">
                                        <input [(ngModel)]="remind" id="remind"
                                         name="remind" type="checkbox"
                                            class="h-4 w-4 rounded border-gray-300
                                             text-indigo-600 focus:ring-indigo-600" />
                                    </div>
                                    <div class="text-sm leading-6">
                                        <label for="remind" class="font-medium
                                         text-gray-900">Remind Me</label>
                                        <p class="text-gray-500">
                                            Get notified about this event an hour before.
                                        </p>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="mt-3 flex items-center justify-end gap-x-6">
                    <button type="button" class="text-sm font-semibold
                     leading-6 text-gray-900" (click)="clear()">
                        Clear
                    </button>
                    <button *ngIf="isEditing" type="submit"
                        class="rounded-md bg-indigo-600 px-3 py-2 
                        text-sm font-semibold text-white shadow-sm
                         hover:bg-indigo-500 focus-visible:outline 
                         focus-visible:outline-2 focus-visible:outline-offset-2
                          focus-visible:outline-indigo-600"
                        (click)="onUpdateClick()">
                        Update
                    </button>
                    <button *ngIf="!isEditing" type="submit"
                        class="rounded-md bg-indigo-600 px-3 py-2 text-sm
                         font-semibold text-white shadow-sm 
                         hover:bg-indigo-500 focus-visible:outline
                          focus-visible:outline-2 
                          focus-visible:outline-offset-2
                           focus-visible:outline-indigo-600"
                        (click)="onAddClick()">
                        Add
                    </button>
                </div>
            </form>
        </div>
    </div>
</div>
<div class="m-5">
    <h2 class="text-center text-3xl font-semibold leading-7
     text-green-600 mt-2 mb-5">
        List Of Upcoming Events
    </h2>
    <div class="grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-4">
        <div *ngFor="let event of eventsArray">
            <div class="max-w-xs rounded overflow-hidden 
            shadow-lg bg-gray-100">
                <div class="px-6 py-4">
                    <div class="font-bold text-xl mb-2">{{ event.title }}</div>
                    <div class="flex items-start justify-between">
                        <div class="text-gray-700 text-sm">
                            {{ event.date | date : "fullDate" }}
                            <p></p>
                        </div>
                        <div *ngIf="event.remind">
                            <svg viewBox="0 0 24 24" width="20"
                             height="20" xmlns="http://www.w3.org/2000/svg">
                                <path
                                    d="m5.705 3.71-1.41-1.42C1 
                                    5.563 1 7.935 1 11h1l1-.063C3 8.009 3
                                     6.396 5.705 3.71zm13.999-1.42-1.408 1.42C21
                                      6.396 21 8.009 21 11l2-.063c0-3.002 
                                      0-5.374-3.296-8.647zM12 22a2.98 2.98 0 0 0 
                                      2.818-2H9.182A2.98 2.98 0 0 0 12 
                                      22zm7-7.414V10c0-3.217-2.185-5.927-5.145-6.742C13.562
                                       2.52 12.846 2 12 2s-1.562.52-1.855 1.258C7.184
                                        4.073 5 6.783 5 10v4.586l-1.707 1.707A.996.996
                                         0 0 0 3 17v1a1 1 0 0 0 1 1h16a1 
                                         1 0 0 0 1-1v-1a.996.996 0 0 0-.293-.707L19 14.586z" />
                            </svg>
                        </div>
                    </div>
                </div>
                <div class="px-6 mb-2">
                    <span
                        class="inline-block bg-gray-200 rounded-full
                         px-3 py-1 text-xs font-semibold
                          text-gray-700 mr-2 mb-2">{{
                        event.location }}</span>
                </div>
                <div class="flex items-center 
                justify-center gap-x-3 my-2">
                    <button type="button"
                        class="rounded-md bg-yellow-500
                         px-5 py-2 text-sm font-semibold 
                         text-white shadow-sm hover:bg-yellow-400 
                         focus-visible:outline focus-visible:outline-2 
                         focus-visible:outline-offset-2 focus-visible:outline-yellow-600"
                        (click)="onEditEvent(event._id)">
                        Edit
                    </button>
                    <button type="button"
                        class="rounded-md bg-red-600 px-3 py-2 text-sm
                         font-semibold text-white shadow-sm hover:bg-red-500
                          focus-visible:outline focus-visible:outline-2 
                          focus-visible:outline-offset-2 
                          focus-visible:outline-red-600"
                        (click)="onDeleteEvent(event._id)">
                        Delete
                    </button>
                </div>
            </div>
        </div>
    </div>
</div>
CSS
/* styles.scss */

@tailwind base;
@tailwind components;
@tailwind utilities;

@import "ngx-toastr/toastr";
CSS
/* src/app/app.component.scss */

.required:after {
   content: " *";
   color: red;
}
JavaScript
// tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
    content: ["./src/**/*.{html,js}"],
    theme: {
        extend: {},
    },
    plugins: [require('@tailwindcss/forms')],
};
JavaScript
// app.config.ts

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideHttpClient } from '@angular/common/http';
import { provideAnimations } from '@angular/platform-browser/animations';
import { provideToastr } from 'ngx-toastr';

export const appConfig: ApplicationConfig = {
    providers: [
        provideRouter(routes),
        provideHttpClient(),
        provideAnimations(),
        provideToastr(),
    ],
};
JavaScript
// api.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
    providedIn: 'root',
})
export class ApiService {
    constructor(private http: HttpClient) { }

    getAllEvents(): Observable<any> {
        return this.http.get<any>('http://localhost:4000/api/getAll');
    }

    getEvent(id: number, data: any): Observable<any> {
        return this.http.post<any>('http://localhost:4000/api/get/' + id, data);
    }

    addEvent(data: any): Observable<any> {
        return this.http.post<any>('http://localhost:4000/api/add', data);
    }

    updateEvent(id: string, data: any): Observable<any> {
        return this.http.put<any>('http://localhost:4000/api/update/' + id, data);
    }

    deleteEvent(id: string): Observable<any> {
        return this.http.delete<any>('http://localhost:4000/api/delete/' + id);
    }
}
JavaScript
// src/app/app.component.ts

import { Component, OnInit } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { ApiService } from './api.service';
import { ToastrService } from 'ngx-toastr';

@Component({
    selector: 'app-root',
    standalone: true,
    imports: [RouterOutlet, FormsModule, CommonModule],
    templateUrl: './app.component.html',
    styleUrl: './app.component.css',
})
export class AppComponent implements OnInit {
    title: string = '';
    date: string = '';
    location: string = 'Select';
    remind: boolean = false;
    isEditing: boolean = false;
    eventId: string = '';
    eventsArray: any[] = [];

    constructor(private apiService: 
        ApiService, private toastr: ToastrService) { }

    ngOnInit(): void {
        this.apiService.getAllEvents().subscribe((res) => {
            this.eventsArray = res;
        });
    }

    onAddClick(): void {
        const data = {
            title: this.title,
            date: this.date,
            location: this.location,
            remind: this.remind,
        };
        this.apiService.addEvent(data).subscribe((res) => {
            this.eventsArray.push(res);
            this.clear();
            this.toastr.success('New event added successfully', 'Success!');
        });
    }

    onEditEvent(id: string): void {
        const index = this.eventsArray
        .findIndex((ev) => ev._id === id);
        const event = this.eventsArray[index];
        this.title = event['title'];
        this.location = event['location'];
        this.date = new Date(event['date'])
        .toISOString().slice(0, 10);
        this.remind = event['remind'];
        this.isEditing = true;
        this.eventId = id;
    }

    clear(): void {
        this.title = '';
        this.date = '';
        this.location = 'Select';
        this.remind = false;
    }

    onUpdateClick(): void {
        const data = {
            title: this.title,
            date: this.date,
            location: this.location,
            remind: this.remind,
        };
        this.apiService.updateEvent(this.eventId, data).subscribe((res) => {
            this.eventsArray = this.eventsArray.filter(
                (ev) => ev._id !== this.eventId
            );
            this.eventsArray.push(res);
            this.eventId = '';
            this.isEditing = false;
            this.toastr.success('Event updated successfully', 'Success!');
            this.clear();
        });
    }

    onDeleteEvent(id: string): void {
        this.apiService.deleteEvent(id).subscribe((res) => {
            this.eventsArray = this.eventsArray.filter((ev) => ev._id !== id);
            this.toastr.success('Event deleted successfully!', 'Success!');
        });
    }
}

Step 11: Start the frontend angular application

ng serve --open

Output:

Event Management Web App using MEAN

Event Management Web App using MEAN


MongoDB Database storing the events data:

Event  Management database in MongoDB

MongoDB Results



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads