In todays's tutorial, we are going to implement Full Calendar with Node.js & MongoDB. To implement API, we use light weight Node.js web application framework Express and we use MongoDB to store our Events data.

Prerequisites

  • Node.js
  • To Continue with this tutorial, you should have installed Node.js in your pc. If you are still not installed Node.js in your machine you can download it from here.
  • Mongo DB
  • To Continue with this tutorial, you should have installed Mongo DB in your pc. If you are still not installed Mongo in your machine you can download it from here.
  • Postman
  • To test our API endpoints, you need a API Testing application like Postman. You can download Postman from here.

    Setting up the Project

    First you need to create a directory for our project. If you are going to use terminal to create directory, run below command on your termianl.
    1
    mkdir angulr-fullcalendar

    Then navigate to your project directory by using below command in your terminal

    1
    cd angulr-fullcalendar

    Then we need to create directory for our server application. After creating that, you need to navigate to that server directory where our API going to implement. Now we need to initialize a new package.json:

    1
    npm init

    Next, we need to install to required dependencies:

    1
    npm install --save express body-parser express mongoose nodemon cors

    At this point, we have been initialized our project and all the dependencies have been installed. Usually we start a project with creating our entry file such like server.js. Create a file called server.js in your root directory and then add below code in it:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import express from 'express';
    import bodyParser from 'body-parser';
    import mongoose from 'mongoose';
    import cors from 'cors';

    import eventRoutes from './routes/events.js';

    app.use(bodyParser.json({ limit: "30mb", extended: true }));
    app.use(bodyParser.urlencoded({ limit: "30mb", extended: true }));
    app.use(cors());

    const CONNECTION_URL = '<your-database-string>';

    const PORT = process.env.PORT || 5000;

    mongoose.connect(CONNECTION_URL,
    { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => app.listen(PORT, () =>
    console.log(`Server Running on Port: http://localhost:${PORT}`)))
    .catch((error) => console.log(`${error} did not connect`));

    mongoose.set('useFindAndModify', false);

    We have created our server.js file but we still missing our routes paths which will be added at the end. Now, we can run our server.js file by running npm strat command in your server directory terminal.

    Note: You need to change your `package.json` file

    1
    2
    3
    "scripts": {
    "start": "nodemon server.js"
    },

    When you run above npm start command, the server will start and it will show Server Running on Port: http://localhost:5000 in your terminal. Now, we need to proceed with our Database section.

    Setting up the Database

    Create a directory call models in your project's root directory. Then, inside that model directory create a file called Event.js where our event schema going to be stored and place below code:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import mongoose from 'mongoose';

    const eventSchema = mongoose.Schema({
    title: {
    type: String,
    required: true,
    },
    date: {
    type: String,
    required: true,
    }
    });

    const Event = mongoose.model('Event', eventSchema);

    export default Event;

    Setting up the Controllers

    Now, we need to create our controllers which need to fetch our all events, create new event and delete event. So, before moving to coding part you need to create a directory call controllers in your project's root directory. Then, inside that controllers directory create a file called event.js and place below code:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    import express from 'express';
    import mongoose from 'mongoose';

    import Event from '../models/Event.js';

    const router = express.Router();

    /* Fetch all events */
    export const getEvents = async (req, res) => {
    try {
    const events = await Event.find();

    res.status(200).json(events);
    } catch (error) {
    res.status(404).json({ message: error.message });
    }
    }

    /* Create new event */
    export const createEvent = async (req, res) => {
    const { title, date } = req.body;

    const newEvent = new Event({ title, date })

    try {
    await newEvent.save();
    res.status(201).json(
    {
    type: "success",
    message: "Event has been added successfully"
    }
    );
    } catch (error) {
    res.status(409).json({ message: error.message });
    }
    }

    /* Delete singile event */
    export const deleteEvent = async (req, res) => {
    const { id } = req.params;

    if (!mongoose.Types.ObjectId.isValid(id)) return res.status(404).send(`No event with id: ${id}`);

    await Event.findByIdAndRemove(id);

    res.json({ message: "Event deleted successfully." });
    }

    export default router;

    Setting up the Routes

    We have already finished with our Models and Controllers. So, now we need to map our funtion in our Controller with the routes. To do it, create a directory call routes in your project's root directory. Then, inside that routes directory create a file called events.js and place below code:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import express from 'express';

    import { getEvents, createEvent, deleteEvent } from '../controllers/events.js';

    const router = express.Router();

    router.get('/get_events', getEvents);
    router.post('/add_events', createEvent);
    router.delete('/delete_event/:id', deleteEvent);

    export default router;

    Finish API part

    To finsih with our API development part, we need to add more new lines to our `server.js` file:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    ...
    import cors from 'cors';

    import eventRoutes from './routes/events.js';

    const app = express();
    ...

    ...
    app.use(cors());

    app.use('/events', eventRoutes);

    const CONNECTION_URL = '<your-database-string>';
    ...

    Now we have finsihed our API development and our final server.js file looks like below:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    import express from 'express';
    import bodyParser from 'body-parser';
    import mongoose from 'mongoose';
    import cors from 'cors';

    import eventRoutes from './routes/events.js';

    const app = express();

    app.use(bodyParser.json({ limit: "30mb", extended: true }));
    app.use(bodyParser.urlencoded({ limit: "30mb", extended: true }));
    app.use(cors());

    app.use('/events', eventRoutes);

    const CONNECTION_URL = '<your-database-string>';

    const PORT = process.env.PORT || 5000;

    mongoose.connect(CONNECTION_URL,
    { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => app.listen(PORT, () =>
    console.log(`Server Running on Port: http://localhost:${PORT}`)))
    .catch((error) => console.log(`${error} did not connect`));

    mongoose.set('useFindAndModify', false);

    If you like to check it before intergrading with Angular application, you can use Postman.

    Testing with Postman

    * Get Events

    Method: GET
    URL: http://localhost:5000/events/get_events

    • Create Event

    Method: POST
    URL: http://localhost:5000/events/add_events
    Body
    x-www-form-urlencoded

    Key Value
    title This test event
    date 2020-01-15
    • Delete Event

    Method: DELETE
    URL: http://localhost:5000/events/delete_event/{id}

    Creating Angular application

    To create our client side application run below command in your `angular-fullcalendar` root directory:

    1
    ng new client

    This command will create a fresh angular project for us.

    Install dependencies

    We need two additional dependencies for this project. You can install them by running below command in your client folder's terminal:

    1
    npm i --save @fullcalendar/angular @fullcalendar/daygrid @fullcalendar/interaction @sweetalert2

    Create API service

    Now, we need to create `api service` file, which holds our base url path of our server. Create a folder called services in our app and then run below command in your terminal:

    1
    ng new service services/api-service

    After creating that file, paste below code in it:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    import { Injectable } from '@angular/core';
    import { HttpHeaders, HttpClient } from '@angular/common/http';
    import { Router } from '@angular/router';
    import { map, catchError } from 'rxjs/operators';
    import { throwError } from 'rxjs';

    @Injectable({
    providedIn: 'root'
    })
    export class ApiServiceService {

    baseUrl = 'http://localhost:5000/events/';
    headers = new HttpHeaders().set('Content-Type', 'application/json');
    currentUser = {};
    window: any;

    constructor(
    private http: HttpClient,
    public router: Router
    ) { }

    // Add Event to Calender//
    addEvent(event) {
    return this.http.post(this.baseUrl + 'add_events', event);
    }

    // Get All Events //
    getAllEvents() {
    return this.http.get(this.baseUrl + 'get_events').
    pipe(
    map((data: any) => {
    console.log(data);
    return data;
    }), catchError(error => {
    return throwError('Something went wrong');
    })
    );
    }

    // Delete Single Event//
    deleteSingleEvent(id) {
    return this.http.delete(this.baseUrl + 'delete_event/' + id).
    pipe(
    map((data: any) => {
    return data;
    }), catchError(error => {
    return throwError('Something went wrong');
    })
    );
    }
    }

    Creating Interface for Events

    In app directory, create a folder named data and create a file named events.ts in it paste below code:

    1
    2
    3
    4
    5
    export class Events {
    title: string;
    date: string;
    _id: string;
    }

    Create Calendar component

    We need to create two new components in our Angular application. Create a directory called pages in app directory and then run below command in terminal:
    1
    2
    3
    ng new c pages/calendar

    ng new c pages/calendar/add-event

    This command will create two new componets which hold our calendar and add event form. Now open calendar.component.ts and paste below command:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    import { Component, OnInit } from '@angular/core';
    import { CalendarOptions } from '@fullcalendar/angular';
    import { HttpClient } from '@angular/common/http';
    import { ApiServiceService } from '../../services/api-service.service';
    import Swal from 'sweetalert2';
    import { Events } from 'src/app/data/events';

    @Component({
    selector: 'app-calendar',
    templateUrl: './calendar.component.html',
    styleUrls: ['./calendar.component.css']
    })

    export class CalendarComponent implements OnInit {

    calendarOptions: CalendarOptions;
    error: any;
    events: Events;
    constructor(
    public http: HttpClient,
    private apiService: ApiServiceService
    ) {}

    handleDateClick(arg) {

    }

    onSelectx(event) {

    }

    ngOnInit() {
    this.getAllEvents();
    }

    deleteEvent(id) {
    this.apiService.deleteSingleEvent(id).subscribe((data: any) => {});
    }

    getAllEvents() {
    this.apiService.getAllEvents().subscribe((data: any) => {
    const self = this;
    this.calendarOptions = {
    initialView: 'dayGridMonth',
    selectable: false,
    editable: false,
    // dateClick: this.handleDateClick.bind(this),
    select: this.handleDateClick.bind(this),
    events: data,
    eventClick(evetData) {
    // tslint:disable-next-line:variable-name
    const event_id = evetData.event._def.extendedProps._id;
    Swal.fire({
    title: 'Are you sure?',
    text: 'You won\'t be able to revert this!',
    icon: 'warning',
    showCancelButton: true,
    confirmButtonColor: '#3085d6',
    cancelButtonColor: '#d33',
    confirmButtonText: 'Yes, delete it!',
    timer: 30000,
    }).then((result) => {
    if (result.value) {
    self.deleteEvent(event_id);
    Swal.fire(
    'Deleted!',
    'Your file has been deleted.',
    'success'
    );
    self.getAllEvents();
    }

    }).catch(() => {
    Swal.fire('Failed!', 'There was something went wrong.');
    });
    }
    };
    });
    }
    }

    Now open calendar.component.html and paste below command:

    1
    2
    3
    4
    5
    6
    <div class="row">
    <div class="col-md-6">
    <button nbButton outline status="success" routerLink="/add-event" class="add_event">Add Event</button>
    </div>
    <hr>
    <full-calendar [options]="calendarOptions"></full-calendar>

    Now open add-event.component.ts file and paste below code in it:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    import { Component, OnInit } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    import { Router } from '@angular/router';
    import Swal from 'sweetalert2';
    import { ApiServiceService } from '../../../services/api-service.service';

    @Component({
    selector: 'app-add-event',
    templateUrl: './add-event.component.html',
    styleUrls: ['./add-event.component.css']
    })
    export class AddEventComponent implements OnInit {

    event = {
    title: '',
    date: ''
    };
    error: any;
    constructor(
    public http: HttpClient,
    private apiService: ApiServiceService,
    private router: Router
    ) { }

    ngOnInit() {
    }
    saveEvent() {
    const event = {
    title: this.event.title,
    date: this.event.date
    };
    this.apiService.addEvent(event)
    .subscribe(
    (response: any) => {
    if (response.type === 'success') {
    Swal.fire({
    position: 'center',
    icon: 'success',
    title: 'Your Event has been added successfully',
    showConfirmButton: false,
    timer: 1500
    });
    this.router.navigate(['/calendar']);
    }
    },
    err => {
    Swal.fire({
    position: 'center',
    icon: 'error',
    title: 'Something went wrong',
    showConfirmButton: false,
    timer: 1500
    });
    this.event.title = '';
    this.event.date = '';
    });
    }
    }

    Next, you need to add below code to add-event.component.html :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <div class="center">
    <div class="col-md-6">
    <form>
    <div class="form-group row">
    <label for="inputEmail1" class="label col-sm-3 col-form-label">Title</label>
    <div class="col-sm-9">
    <input type="email" nbInput fullWidth id="title" placeholder="Title" [(ngModel)]="event.title"
    [ngModelOptions]="{standalone: true}" required>
    </div>
    </div>
    <div class="form-group row">
    <label for="inputPassword2" class="label col-sm-3 col-form-label">Date</label>
    <div class="col-sm-9">
    <input type="date" nbInput fullWidth id="date" placeholder="Password" [(ngModel)]="event.date"
    [ngModelOptions]="{standalone: true}" required>
    </div>
    </div>
    <div class="form-group row">
    <div class="offset-sm-3 col-sm-9">
    <button type="submit" nbButton status="primary" (click)="saveEvent()">Add Event</button>
    </div>
    </div>
    </form>
    </div>

    Creating routes paths

    Now, we need to change our app-routing.module.ts
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import { NgModule } from '@angular/core';
    import { Routes, RouterModule } from '@angular/router';
    import { CalendarComponent } from './pages/calendar/calendar.component';
    import { AddEventComponent } from './pages/calendar/add-event/add-event.component';


    const routes: Routes = [
    { path: '', component: CalendarComponent },
    { path: 'add-event', component: AddEventComponent}
    ]

    @NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
    })
    export class AppRoutingModule { }

    Then change your app.component.html

    1
    2
    3
    4
    <div class="center">
    <h1>Angular Full Calendar Example</h1>
    </div>
    <router-outlet></router-outlet>

    Finally, you need tio change your app.module.ts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { HttpClientModule } from '@angular/common/http';

    import { AppRoutingModule } from './app-routing.module';
    import { FormsModule } from '@angular/forms';
    import { AppComponent } from './app.component';
    import { CalendarComponent } from './pages/calendar/calendar.component';
    import { FullCalendarModule } from '@fullcalendar/angular'; // the main connector. must go first
    import dayGridPlugin from '@fullcalendar/daygrid'; // a plugin
    import interactionPlugin from '@fullcalendar/interaction';
    import { AddEventComponent } from './pages/calendar/add-event/add-event.component';

    FullCalendarModule.registerPlugins([
    dayGridPlugin,
    interactionPlugin
    ]);

    @NgModule({
    declarations: [
    AppComponent,
    CalendarComponent,
    AddEventComponent
    ],
    imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    FullCalendarModule,
    FormsModule
    ],

    providers: [],
    bootstrap: [AppComponent]
    })
    export class AppModule { }

    Now, you can check our application by running ng serve in your client firectory and npm start in server directory.

    Conclusion

    In this tutorial, we implemented Angular Full Claendar example with Node.js API and MongoDB. If you have any issue regarding this tutorial, mention your issue in comment section or reach me through my E-mail. You can obtain complete source code for this tutorial from this GitHub repository.