Appointment Booking using Angularjs, Nodejs, Mongodb

Facebook
Reddit
Twitter
Whatsapp
Cover image

In this tutorial, we will create a simple, real and single page Appointment Booking web application using Javascript Fullstack or MEAN Stack (MongoDB + Express + AngualrJS + NodeJS) which can also be used as an Event Scheduler.

  • The Javascript fullstack will have both Client and Server architecture and will share the same JSON (Javascript Simple Object Notation) format across Client, Server and Database.
  • The code will be fast, modular and less redundant.
  • We will integrate Google Material Design guidelines into our code for a better UI experience.
  • We will use document based no-sql database named MongoDB

Live Demo

Installation

Install Required Softwares

'NodeJS is a non blocking, event-driven javascript framework for building network based applications. This comes with Node Package Manager. (npm)'

Install NodeJS and run node

"MongoDB is a Javascript based cross-platform document-oriented NoSQL database."

Install MongoDB and run mongod in a separate shell to keep an instance of the MongoDB Daemon running

Start the application Open Command Prompt / Terminal and Run the following commands

npm install : Installs all required server dependencies. bower install: Installs front-end packages required for our application. node server: Starts the application on port 8080 (http://localhost:8080)

Open your web browser and point to http://localhost:8080

Code

client/index.html The starting point for our application front end.

<html ng-app="myApp">
<div ng-controller = "myCtrl">
<input type = "file" file-model = "myFile"/>
<button ng-click = "uploadFile()">Upload File</button>
</div>
</html>
<script src="angular.min.js"></script>
<script>
var myApp = angular.module('myApp', []);
myApp.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
myApp.service('fileUpload', ['$http', function ($http) {
this.uploadFileToUrl = function(file, uploadUrl){
var fd = new FormData();
fd.append('file', file);
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function(){
})
.error(function(){
});
}
}]);
myApp.controller('myCtrl', ['$scope', 'fileUpload', function($scope, fileUpload){
$scope.uploadFile = function(){
var file = $scope.myFile;
var uploadUrl = "/savedata";
fileUpload.uploadFileToUrl(file, uploadUrl);
};
}]);
</script>

client/app.js It starts the angular module and links the required external dependencies.

'use strict';
angular.module('appointmentApp', ['ngMaterial'])
view raw appointment-app.js hosted with ❤ by GitHub

client/customer.html Appointment details with customer information.

<md-dialog aria-label="Mango (Fruit)" ng-cloak flex-lg="25" flex="50" flex-sm="100">
<form ng-submit="answer(customer)">
<md-toolbar>
<div class="md-toolbar-tools">
<h2>Slot: {{customer.slot.date | date:'dd-MMM-yyyy @ h:mma'}} </h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="cancel()">
<md-icon md-svg-src="img/icons/ic_close_24px.svg" aria-label="Close dialog"></md-icon>
</md-button>
</div>
</md-toolbar>
<md-dialog-content>
<md-content layout="column" layout-align="start center">
<md-input-container>
<label>Name</label>
<input ng-model="customer.name" required/>
</md-input-container>
<md-input-container>
<label>phone no</label>
<input ng-model="customer.phone" required/>
</md-input-container>
<md-input-container layout-align="center center">
</md-input-container>
</md-content>
<md-dialog-actions layout="row" layout-align="center center">
<md-button type="submit" class="md-raised md-warn">Book Appointment</md-button>
</md-dialog-actions>
</md-dialog-content>
</form>
</md-dialog>

client/main.css All css files for main.html

.navbar-text { margin-left: 15px; }
.gray { background: #f5f5f5; }
.green { background: #b9f6ca; }
.yellow { background: #ffff8d; }
.blue { background: #84ffff; }
.darkBlue { background: #80d8ff; }
.deepBlue { background: #448aff; }
.purple { background: #b388ff; }
.lightPurple { background: #8c9eff; }
.red { background: #ff8a80; }
.pink { background: #ff80ab; }
.footer .footer-content {
text-align: center;
padding: 30px 0;
margin-top: 70px;
border-top: 1px solid #E5E5E5;
}
view raw appointment-main.css hosted with ❤ by GitHub

client/main.html Application’s front end

<section layout="row">
<md-content layout="row" flex="66" layout-wrap>
<md-card ng-repeat="d in $ctrl.dates" ng-class="$ctrl.getColor($index)" class="md-whiteframe-z2" flex flex-sm="40" flex-xs="100">
<md-card-content>
<h2>{{d.k | date:'dd-MMM-yyyy'}}</h2>
<p layout="column">
<md-button class="md-primary md-raised"
ng-repeat="i in d.v"
ng-click="$ctrl.showAdvanced(i)"
ng-disabled="!i.active"
flex>
{{i.date | date:'h:mma'}}
</md-button>
</p>
</md-card-content>
<md-card-footer></md-card-footer>
</md-card>
</md-content>
<md-card class="md-whiteframe-z2" flex="33">
<md-card-content>
<h2>Booked Appointments</h2>
<p layout="column" ng-repeat="a in $ctrl.appointments">
{{a.name}} ({{a.phone}}) => {{a.date | date:'dd-MMM-yyyy @ h:mma'}}
</p>
</md-card-content>
<md-card-footer></md-card-footer>
</md-card>
</section>

client/main.js It implements required logics.

'use strict';
(function() {
class MainController {
constructor($http,$mdMedia,$mdDialog) {
this.message = 'Hello';
this.$http=$http;
this.appointment = [];
this.slots = [];
this.$mdMedia = $mdMedia;
this.$mdDialog = $mdDialog;
// Configure dates
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth()+1; //January is 0!
var yyyy = today.getFullYear();
if(dd<10) {
dd='0'+dd
}
if(mm<10) {
mm='0'+mm
}
// Create date and slots array
this.days = [{dd:dd,mm:mm,yyyy:yyyy},{dd:dd+1,mm:mm,yyyy:yyyy},{dd:dd+2,mm:mm,yyyy:yyyy},{dd:dd+3,mm:mm,yyyy:yyyy}];
this.slots= [{h:'10',m:'00'},{h:'10',m:'15'},{h:'10',m:'30'},{h:'10',m:'45'},{h:'11',m:'00'},{h:'11',m:'15'},{h:'11',m:'30'}];
}
save(appointment){
appointment.active=true;
this.$http.post('/api/appointments',appointment).then(res=>{
this.dates = this.allot(this.slots,this.days,this.appointments);
this.$http.get('/api/appointments').then(response=>{
this.appointments=response.data;
this.dates = this.allot(this.slots,this.days,this.appointments);
});
});
}
$onInit(){
var vm = this;
this.$http.get('/api/appointments').then(response=>{
this.appointments=response.data;
this.dates = this.allot(this.slots,this.days,this.appointments);
});
}
delete(d){
this.$http.delete('/api/appointments/'+d._id);
}
allot(slots, days, appointments){
var a = [];
_.each(days, function(d) {
var k=new Date(d.yyyy,d.mm,d.dd);
var v = [];
_.each(slots, function(s) {
var x = new Date(d.yyyy,d.mm,d.dd,s.h,s.m);
var mx = moment(x);
var active = true;
_.each(appointments, function(g) {
var sdt = moment(new Date(g.date));
if(moment.duration(sdt.diff(mx))._milliseconds===0){
active = false;
}
})
v.push({date:x,active:active});
})
a.push({k:k,v:v});
})
return a;
}
showAdvanced(slot) {
var vm = this;
var useFullScreen = (this.$mdMedia('sm') || this.$mdMedia('xs')) && this.customFullscreen;
this.$mdDialog.show({
controller: function($scope,$mdDialog,slot){
$scope.customer = {};
$scope.customer.slot = slot;
$scope.answer = function(answer){
$mdDialog.hide(answer);
};
},
templateUrl: 'app/customer.html',
locals : {
slot : slot
},
clickOutsideToClose:true,
fullscreen: useFullScreen
})
.then(function(answer) {
answer.date = answer.slot.date;
vm.save(answer);
});
}
getColor($index) {
var _d = ($index + 1) % 11;
var bg = '';
switch(_d) {
case 1: bg = 'green'; break;
case 2: bg = 'darkBlue'; break;
case 3: bg = 'blue'; break;
case 4: bg = 'yellow'; break;
case 5: bg = 'pink'; break;
case 6: bg = 'darkBlue'; break;
case 7: bg = 'purple'; break;
case 8: bg = 'deepBlue'; break;
case 9: bg = 'lightPurple'; break;
case 10: bg = 'red'; break;
default: bg = 'yellow'; break;
}
return bg;
}
}
angular.module('appointmentApp')
.component('main', {
templateUrl: 'app/main.html',
controller: MainController
});
})();
view raw appointment-main.js hosted with ❤ by GitHub

server/index.js It creates the appointments ReST API routes.

// Dependencies
var express = require('express');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
// MongoDB
mongoose.connect('mongodb://localhost/appointment');
// mongoose.connection.on('error', function(){});
// Express
var app = express();
app.use(express.static(__dirname + './../public'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Routes
app.use('/api', require('./api/appointment/'));
// Start server
var port = 8080
, ip = "127.0.0.1";
app.listen(port, ip, function() {
console.log('Express server listening on %d', port);
});
view raw appointment-index.js hosted with ❤ by GitHub

server/api/appointment/index.js The appointments ReST API routes are defined here.

// Dependencies
var express = require('express');
var router = express.Router();
//Product
var Appointment = require('./model');
Appointment.methods(['get', 'put', 'post', 'delete']);
Appointment.register(router, '/appointments');
// Return router
module.exports = router;

server/api/appointment/model.js Defines the appointments database model.

// Dependencies
var restful = require('node-restful');
var mongoose = restful.mongoose;
// Schema
var appointmentSchema = new mongoose.Schema({
date: Date,
name: String,
phone: String,
status: Boolean
});
// Return model
module.exports = restful.model('Appointments', appointmentSchema);
view raw appointment-model.js hosted with ❤ by GitHub

package.json It carries node dependencies which are required for this application.

{
"name": "appointments",
"version": "1.0.0",
"description": "Appointments using AngularJS, NodeJS and MongoDB (Javascript Fullstack)",
"dependencies": {
"body-parser": "^1.14.2",
"express": "^4.13.4",
"mongoose": "^4.1.3",
"node-restful": "^0.2.2"
},
"devDependencies": {},
"scripts": {
"start": "node server"
},
"repository": {
"type": "git",
"url": "git+https://github.com/itswadesh/simple-appointment.git"
},
"author": "Swadesh Behera",
"license": "ISC",
"bugs": {
"url": "https://github.com/itswadesh/simple-appointment/issues"
},
"homepage": "https://github.com/itswadesh/simple-appointment#readme"
}
Subscribe my updates via Email