Create a Picture Competition Website in Express JS, MEVN

MEVN stands for Mongo DB, Express JS, Vue JS, and Node JS. In this tutorial, we will teach you how you can create a picture competition web app using Node JS as a back-end server, Mongo DB as a database, and Vue JS as a front-end.

Demo:

What we are going to do:

  1. Setup the Project & User Registration
  2. Login with JWT (Json Web Token)
  3. Logout
  4. Forgot password
  5. Sign-in with Google
  6. Sign-in with Facebook
  7. Create Competition
  8. Show all Competitions
  9. Vote on Competition
  10. AJAX Load More
  11. Competition Detail
  12. Comments on Competition
  13. Delete Competition
  14. Search and Sort
  15. User Notifications
  16. User Profile
  17. Admin Panel

Download Assets

Although you do not need the assets to run this project. But if you want to have a beautiful bootstrap user interface, then you must download the required assets from this Github repo.

Setup the Project

First, you need to download and install Node JS, you can download it from their official site. Then you need to download and install Mongo DB, you can download this from their official site as well. To view the data in Mongo DB you need to download a software called “Mongo DB Compass“, you can download it from here.

Then create a new folder anywhere on your PC and rename it to “picture-competition“. Create a new folder “public” where we will place all our CSS and JS files including Bootstrap and jQuery etc. Create another folder “views” and leave it empty for now, we will place all our HTML files in it.

Node JS

Open a command prompt at the root folder of your project and run the following command:

npm init

And press “enter” for each question asked, it will set the default values. Then run the following command:

npm install express http

This will install the Express framework which we will use for routing and API calls. HTTP module will be used to start the server at a specific port. Then we need to run the following command:

sudo npm install -g nodemon

sudo is used to get admin privileges, nodemon module is used to restart the server automatically if there is any change in the server file. Then create a file named “server.js” and write the following code in it:

var express = require("express");
var app = express();

var http = require("http").createServer(app);

// [set EJS engine]

// [mongo db initialization code goes here]

http.listen(process.env.PORT || 3000, function () {
	console.log("Server started");

	// [mongo db connection goes here]
});

Now run the following command to start the server:

nodemon server.js

If you open the terminal, you will see the message that the server has been started.

User Registration

Login and Registration
Login and Registration

Now we need to display a registration form, when submitted should save the record in Mongo DB. To display HTML pages in Node JS, we need to include a module named “EJS“. So run the following command to install it:

npm install ejs

Make sure to restart the server by running the nodemon server.js command. Then write the following code in your server.js by replacing the [set EJS engine] section:

app.use(express.static(__dirname + "/public"));
app.set("view engine", "ejs");

Now you need to install the Mongo DB module, so run the following command:

npm install mongodb

After that, you need to write the following code in place of the [mongo db initialization code goes here] section:

var mongodb = require("mongodb");
var MongoClient = mongodb.MongoClient;
var ObjectId = mongodb.ObjectId;

After that, replace the section [mongo db connection goes here] with the following code:

MongoClient.connect("mongodb://localhost:27017", function (error, client) {
	if (error) {
		console.error(error);
		return false;
	}

	var db = client.db("picture_competition");
	console.log("Database connected");

	// [routes goes here]

});

Check your command prompt and you will see a message that the database has been connected as well. Now we need to create a route that will be called when accessed from the browser. Write the following code in the [routes goes here] section:

app.get("/", function (request, result) {
	result.render("home");
});

This will render the EJS file, so we need to create a file named “home.ejs” in your “views” folder and write the following code in it:

<%- include ("includes/header") %>

<%- include ("includes/footer") %>

Create a new folder includes inside views folder and create 2 files in it, header.ejs and footer.ejs. Following should be the content of the header.ejs file:

<!DOCTYPE html>
<html>
	<head>
		<title>Picture Competition</title>
		
		<link rel="stylesheet" type="text/css" href="/bootstrap.min.css" />
        <link rel="stylesheet" type="text/css" href="/style.css" />
		<link rel="stylesheet" type="text/css" href="/font-awesome/css/font-awesome.min.css" />

		<script src="/vue.min.js"></script>
		<script src="/vue-resource.js"></script>

		<script src="/jquery-3.3.1.min.js"></script>
		<script src="/bootstrap.min.js"></script>

		<script>
		    Vue.use(VueResource);

		    const mainURL = "http://localhost:3000";
            const accessTokenKey = "accessToken";
            var user = null;
		</script>
	</head>

	<body>

		<nav class="navbar navbar-expand-lg navbar-light bg-light" id="navApp">
			<a class="navbar-brand" v-bind:href="baseUrl">Picture Competition</a>

			<div class="collapse navbar-collapse" id="navbarSupportedContent">
				<ul class="navbar-nav mr-auto">
					<li class="nav-item active">
						<a class="nav-link" v-bind:href="baseUrl">Home</a>
					</li>

                    [search goes here]
				</ul>

				<ul class="navbar-nav">

		            <li v-if="!login" class="nav-item">
		                <a v-bind:href="baseUrl + '/login'" class="nav-link">Login</a>
		            </li>
		 
		            <li v-if="!login" class="nav-item">
		                <a v-bind:href="baseUrl + '/signup'" class="nav-link">Signup</a>
		            </li>
				</ul>
			</div>
		</nav>

And following should be the content of the footer.ejs:

	</body>
</html>

We will be creating multiple Vue JS apps, so right now we will create a Vue JS app for this navigation bar. Write the following lines in your footer.ejs:

<script>
	var navApp = new Vue({
		el: "#navApp",
		data: {
			baseUrl: mainURL,
			login: false,
		}
	});
</script>

Access your project from the browser from this URL: http://localhost:3000/ and you will see the login and signup button at the top navigation bar. Because right now the user is not logged in. Click on the signup link and you will be redirected to the “404 not found” page.

Show Signup Form

Now we need to create that route in our server.js:

app.route("/signup")
    .get(function (request, result) {
        result.render("signup");
    });

Then create a new file named signup.ejs inside the views folder. We will be using AJAX many times in this project, so we will place it in a separate file so we can re-use it later easily.

Create a file named app.js inside the public folder and write the following code in it:

var myApp = {
	callAjax: function (url, formData, callBack = null) {
		var ajax = new XMLHttpRequest();
		ajax.open("POST", url, true);

		ajax.onreadystatechange = function () {
			if (this.readyState == 4) {
				if (this.status == 200) {
					if (callBack != null) {
						callBack(this.responseText);
					}
				}

				if (this.status == 500) {
					console.log(this.responseText);
				}
			}
		};

		ajax.send(formData);
	}
};

Then in your header.ejs you need to include that file as Javascript.

<script src="/app.js"></script>

We need to display an alert message when the user is signed up or when there is an error during sign-up. We will be using the Sweetalert library to display alerts. Include that in your header.ejs:

<script src="/sweetalert.min.js"></script>

Finally, we will write the following code in our signup.ejs:

<%- include ("includes/header") %>

<div class="container margin-container" id="registerApp">
	<div class="row">
		<div class="offset-md-3 col-md-6">

			<h2 class="text-center">Register</h2>

			<form method="POST" v-bind:action="baseUrl + '/signup'" v-on:submit.prevent="doRegister">
				<div class="form-group">
					<label>Name</label>
					<input type="text" name="name" class="form-control" required />
				</div>

				<div class="form-group">
					<label>Email</label>
					<input type="email" name="email" class="form-control" required />
				</div>

				<div class="form-group">
					<label>Password</label>
					<input type="password" name="password" class="form-control" required />
				</div>

				<input type="submit" name="submit" value="Register" class="btn btn-primary" />
			</form>
		</div>
	</div>
</div>

<script>
	var registerApp = new Vue({
		el: "#registerApp",
		data: {
			baseUrl: mainURL
		},
		methods: {
			doRegister: async function () {
				const form = event.target;

				// disable the submit button and show "Loading..." text
		        form.submit.setAttribute("disabled", "disabled");
		        form.submit.value = "Loading...";

				var formData = new FormData(form);
				myApp.callAjax(form.getAttribute("action"), formData, function (response) {
	                // convert the JSON string into Javascript object
	                var response = JSON.parse(response);

	                // enable the submit button
	                form.submit.removeAttribute("disabled");
	                form.submit.value = "Register";

	                // if the user is created, then redirect to login
	                if (response.status == "success") {
	                	swal("Registered", response.message, "success");
	                } else {
	                	swal("Error", response.message, "error");
	                }
				});
			}
		}
	});
</script>

<%- include ("includes/footer") %>

Refresh the page and you will see a signup form. But nothing happens when you click the submit button, it is because we need to create an API for signup.

Saving the Data in Mongo DB

We will be sending AJAX requests with a form data object in this project. To parse that form data object on the server-side, we need to install a module named express-formidable.

And we need to encrypt plain text password fields into a hashed string before saving them in Mongo DB. So we need to install a module named bcrypt.

After the user successfully registered, we need to send a verification link to his email address to verify the account. He will only be able to log in if the account is verified. To send an email we will be using a module named nodemailer.

Note: Please enable the “less secure apps” option from your google account from this page.

Then open your command prompt and run the following commands at the root of your project folder to install the above-discussed modules:

npm install express-formidable bcrypt nodemailer

Then open your server.js file and include the above-installed module at the top:

var expressFormidable = require("express-formidable");
app.use(expressFormidable());

const mainURL = "http://localhost:3000";

const nodemailer = require("nodemailer");
var nodemailerFrom = "your_email@gmail.com";
var nodemailerObject = {
	service: "gmail",
	host: 'smtp.gmail.com',
    port: 465,
    secure: true,
	auth: {
		user: "your_email@gmail.com",
		pass: "your_password"
	}
};

var bcrypt = require("bcrypt");

Enter your correct email and password of your Google account of whom you have enabled “less secure apps”. Then create a post route for “signup” by chaining it with GET route we created earlier.

// route for signup requests
app.route("/signup")
 
    // get request accessed from browser
    .get(function (request, result) {
        // render signup.ejs file inside "views" folder
        result.render("signup");
    })
    // post request called from AJAX
    .post(async function (request, result) {
 
        // get values from signup form
        var name = request.fields.name;
        var email = request.fields.email;
        var password = request.fields.password;
		var verificationToken = new Date().getTime();
		var createdAt = new Date().getTime();
 
        // check if email already exists
        var user = await db.collection("users").findOne({
            "email": email
        });
 
        if (user != null) {
            result.json({
                "status": "error",
                "message": "Email already exists."
            });
            return true;
        }
 
        // encrypt the password
        bcrypt.hash(password, 10, async function (error, hash) {
 
            // insert in database
            await db.collection("users").insertOne({
                "name": name,
                "email": email,
                "password": hash,
                "picture": "",
                "accessToken": "",
                "notifications": [],
                "bio": "",
                "dob": "",
                "country": "",
                "phone": "",
                "website": "",
                "twitter": "",
                "facebook": "",
                "googlePlus": "",
                "linkedIn": "",
                "instagram": "",
                "resetToken": "",
                "isVerified": false,
                "verificationToken": verificationToken,
                "createdAt": createdAt
            });

            var transporter = nodemailer.createTransport(nodemailerObject);

			var html = "Please verify your account by click the following link: <br><br> <a href='" + mainURL + "/verifyEmail/" + email + "/" + verificationToken + "'>Confirm Email</a> <br><br> Thank you.";

			transporter.sendMail({
				from: nodemailerFrom,
				to: email,
				subject: "Email Verification",
				text: html,
				html: html
			}, function (error, info) {
				if (error) {
					console.error(error);
				} else {
					console.log("Email sent: " + info.response);
				}
				
				// send the response back to client
	            result.json({
	                "status": "success",
	                "message": "Signed up successfully. Kindly check your email to verify your account."
	            });
			});
        });
    });

Refresh the browser and try to signup. Fill the signup form and hit submit. You will see a Sweetalert that an account has been created and you will also receive an email of account confirmation.

Email Verification

Email Verification
Email Verification

Now we need to create a route to verify the email address because without this, the user will not be able to log in. So we just need to create a GET route in our server.js:

app.get("/verifyEmail/:email/:verificationToken", async function (request, result) {
	const email = request.params.email;
	const verificationToken = request.params.verificationToken;

	var user = await db.collection("users").findOne({
		$and: [{
			"email": email,
		}, {
			"verificationToken": parseInt(verificationToken)
		}]
	});

	if (user == null) {
		result.render("verify-email", {
			"status": "error",
			"message": "Email does not exists. Or verification link is expired."
		});

		return false;
	}

	await db.collection("users").findOneAndUpdate({
		$and: [{
			"email": email,
		}, {
			"verificationToken": parseInt(verificationToken)
		}]
	}, {
		$set: {
			"verificationToken": "",
			"isVerified": true
		}
	});

	result.render("verify-email", {
		"status": "success",
		"message": "Account has been verified. Please try login."
	});
});

If the account is confirmed successfully or if there is an error in the confirmation like we will add the expiry time to the link. So the user has to resend a verification email if a certain time is passed. In either case, we need to create a new file verify-email.ejs inside your views folder to display a success or an error message.

<%- include ("includes/header") %>

<div class="container margin-container">
	<div class="row">
		<div class="offset-md-3 col-md-6">

			<h2 class="text-center" style="margin-bottom: 20px;">Email Verification</h2>

			<% if (status == "success") { %>
				<div class="alert alert-success"><%= message %></div>
			<% } %>

			<% if (status == "error") { %>
				<div class="alert alert-danger"><%= message %></div>
			<% } %>
		</div>
	</div>
</div>

<%- include ("includes/footer") %>

Click on the link you receive in your provided email address, you will see a success message using a Bootstrap alert if the account is confirmed successfully.

Dynamic testimonial – PHP & MySQL, Vue JS

In this tutorial, we are going to teach you how you can add a dynamic testimonial to your website using PHP and MySQL in the backend and Vue JS in the frontend. Testimonials are displayed on a website to show the new users how your product satisfies your previous customers. We will be creating 2 pages, one for adding testimonials from the admin panel. And second to display all testimonials in a beautiful design.

If you do not have a dynamic testimonial, then you have to manually add, modify or delete a testimonial from your website. By going dynamic, you can perform all these actions from your admin panel.

Add Testimonial

First, download Bootstrap from here and Vue JS from here. Paste the CSS and JS files in your project, we will be using them in a moment. After that, we need to create a form from which we can add testimonials. Each testimonial will have a picture of the person, name, designation in his company, and his comments.

The following code goes in your admin panel from where you want to add testimonials.

<!-- include bootstrap -->
<link rel="stylesheet" type="text/css" href="bootstrap.min.css" />
 
<!-- include vue js -->
<script src="vue.min.js"></script>

<!-- container for vue js app -->
<div class="container" style="margin-top: 50px; margin-bottom: 50px;" id="addTestimonialApp">
    <div class="row">
        <!-- center align form -->
        <div class="offset-md-3 col-md-6">
            <h2 style="margin-bottom: 30px;">Add Testimonial</h2>
 
 			<!-- form to add testimonial -->
            <form v-on:submit.prevent="store" enctype="multipart/form-data">

            	<!-- picture of user -->
                <div class="form-group">
                    <label>Picture</label>
                    <input type="file" name="picture" accept="image/*" class="form-control" />
                </div>

                <!-- name of user -->
                <div class="form-group">
                    <label>Name</label>
                    <input type="text" name="name" class="form-control" />
                </div>

                <!-- designation of user -->
                <div class="form-group">
                    <label>Designation</label>
                    <input type="text" name="designation" class="form-control" />
                </div>

                <!-- comment -->
                <div class="form-group">
                    <label>Comment</label>
                    <textarea name="comment" class="form-control"></textarea>
                </div>

                <!-- submit button -->
                <input type="submit" name="submit" class="btn btn-info" value="Add Testimonial" />
            </form>
        </div>
    </div>

    [show all testimonials for deleting]
</div>

This will show a form with input fields. But when you click on the “Add Testimonial” button, nothing happens. This is because we need to render it using Vue JS.

<script>
    // initialize vue js app
    var addTestimonialApp = new Vue({
        el: "#addTestimonialApp", // id of container div
        data: {
            // all values used in this app
            testimonials: []
        },
        // all methods
        methods: {
            // [other methods goes here]

             // called when form is submitted
            store: function () {
                // get this app instance
                var self = this;
                var form = event.target;
             
                // call an AJAX to create a new entry in testimonials
                var ajax = new XMLHttpRequest();
                ajax.open("POST", "store.php", true);
             
                ajax.onreadystatechange = function () {
                    if (this.readyState == 4) { // response received
                        if (this.status == 200) { // response is successfull
                            // console.log(this.responseText);
             
                            // parse the response from JSON string to JS arrays and objects
                            var response = JSON.parse(this.responseText);
                            // console.log(response);

                            alert(response.message);
             
                            // if there is no error
                            if (response.status == "success") {
                                self.testimonials.unshift(response.testimonial);
                                form.reset();
                            } else {
                                // when there is any error
                            }
                        }
             
                        if (this.status == 500) {
                            console.log(this.responseText);
                        }
                    }
                };
             
                // create form data object and form to it
                var formData = new FormData(form);
             
                // actually sending the request
                ajax.send(formData);
            },
        },

        // [mount code goes here]
    });
</script>

Refresh the page now and you will be able to submit the form, this is because of Vue JS. An AJAX request will be sent to the server to store the picture attached and save the other fields in the MySQL database using PHP.

Create a new file named “store.php” and paste the following code in it:

<?php
 
// connect with database
$conn = new PDO("mysql:host=localhost;dbname=test", "root", "");

// create tables if not exists
$sql = "CREATE TABLE IF NOT EXISTS testimonials (
    id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
    picture TEXT NULL,
    name VARCHAR(255) NULL,
    designation VARCHAR(255) NULL,
    comment TEXT NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)";
$result = $conn->prepare($sql);
$result->execute();

$file_path = "";
if ($_FILES["picture"]["error"] == 0)
{
    $folder_name = "testimonials";
    mkdir($folder_name);
    $file_path = $folder_name . "/" . $_FILES["picture"]["name"];
    move_uploaded_file($_FILES["picture"]["tmp_name"], $file_path);
}

// insert in testimonials table
$sql = "INSERT INTO testimonials (picture, name, designation, comment, created_at) VALUES (?, ?, ?, ?, NOW())";
$result = $conn->prepare($sql);
$result->execute([
    $file_path,
    $_POST["name"],
    $_POST["designation"],
    $_POST["comment"],
]);
$testimonial_id = $conn->lastInsertId();

// get the testimonial just inserted in database
$sql = "SELECT * FROM testimonials WHERE id = ?";
$result = $conn->prepare($sql);
$result->execute([
    $testimonial_id
]);
$testimonial = $result->fetch();

echo json_encode([
    "status" => "success",
    "message" => "Testimonial has been added.",
    "testimonial" => $testimonial
]);
exit();

If you refresh the page now, upload the picture, enter the fields and hit submit, it will create a new table in the database if not already created. Then it will create a folder named “testimonials” and save the image file in it. Then it will insert a new row in it. And finally, it will return the new row back to the client (AJAX).

From there we will prepend it in our local testimonials array. Now we need to display all the added testimonials in an HTML table with a button to delete them.

Display all Testimonials to Admin

The following code goes in the [show all testimonials for deleting] section:

<!-- show all testimonials -->
<div class="row" style="margin-top: 50px;">
    <div class="col-md-12">
        <table class="table table-bordered">
            <!-- table heading -->
            <tr>
                <th>ID</th>
                <th>Picture</th>
                <th>Name</th>
                <th>Designation</th>
                <th>Comment</th>
                <th>Actions</th>
            </tr>
 
            <!-- loop through an array of testimonials -->
            <tr v-for="(testimonial, index) in testimonials">
                <td v-text="testimonial.id"></td>
                <td>
                    <img v-bind:src="testimonial.picture" style="width: 300px;" />
                </td>
                <td v-text="testimonial.name"></td>
                <td v-text="testimonial.designation"></td>
                <td v-text="testimonial.comment"></td>
                <td>
                    <!-- form to delete testimonial -->
                    <form v-on:submit.prevent="deleteTestimonial">
                        <input type="hidden" name="id" v-model="testimonial.id" required />
                        <input type="submit" class="btn btn-danger btn-sm" value="Delete" />
                    </form>
                </td>
            </tr>
        </table>
    </div>
</div>

This will create an empty HTML table because we need to load the in it first. We will call an AJAX to fetch all the stored testimonials using PHP and MySQL and display them using Vue JS. The following code goes in the [mount code goes here] section:

mounted: function () {
    this.getData();
}

Now we need to create a function named “getData” in our Vue JS instance. Replace the code in section [other methods goes here] with the following:

// [delete method]

getData: function () {
    // get this app instance
    var self = this;
 
    // call an AJAX to get all testimonials
    var ajax = new XMLHttpRequest();
    ajax.open("POST", "fetch.php", true);
 
    ajax.onreadystatechange = function () {
        if (this.readyState == 4) { // response received
            if (this.status == 200) { // response is successfull
                // console.log(this.responseText);
 
                // parse the response from JSON string to JS arrays and objects
                var response = JSON.parse(this.responseText);
                // console.log(response);
 
                // if there is no error
                if (response.status == "success") {
                    self.testimonials = response.data;
                } else {
                    // when there is any error
                }
            }
 
            if (this.status == 500) {
                console.log(this.responseText);
            }
        }
    };
 
    // create form data object
    var formData = new FormData();
 
    // actually sending the request
    ajax.send(formData);
},

Finally, we need to create a new file named “fetch.php” that will fetch all the testimonials from the MySQL database using PHP.

<?php

// connect with database
$conn = new PDO("mysql:host=localhost;dbname=test", "root", "");

// fetch all testimonials
$sql = "SELECT * FROM testimonials ORDER BY id DESC";
$statement = $conn->prepare($sql);
$statement->execute();
$data = $statement->fetchAll();

// create new field for full comment text
// because we will be displaying less text and display 'show more' button
for ($a = 0; $a < count($data); $a++)
{
	$data[$a]["comment_full"] = $data[$a]["comment"];
	$data[$a]["comment"] = substr($data[$a]["comment"], 0, 50);
}

// send the response back to client
echo json_encode([
    "status" => "success",
    "message" => "Testimonial has been fetched.",
    "data" => $data
]);
exit();

?>

Refresh the page now and you will be able to see all the testimonials added. Also, if you add a new testimonial, it will automatically be prepended in the HTML table. Now we need to make it able to delete the testimonial.

Delete Testimonial

We need to follow the following steps to delete the testimonial:

  1. Call an AJAX with an ID of testimonial.
  2. On server side, fetch the testimonial using ID.
  3. Delete the picture from the “testimonials” folder using PHP unlink() function.
  4. Delete the testimonial from MySQL database.
  5. Send the response back to client.
  6. The client will remove the testimonial from local array.
  7. It will automatically be removed from the HTML table.

Replace the section [delete method] with the following code:

// method to delete testimonial
deleteTestimonial: function () {
    // get this app instance
    var self = this;
 
    // get form
    var form = event.target;
 
    // call an AJAX to delete the testimonial
    var ajax = new XMLHttpRequest();
    ajax.open("POST", "delete.php", true);
 
    ajax.onreadystatechange = function () {
        if (this.readyState == 4) { // response received
            if (this.status == 200) { // response is successfull
                // console.log(this.responseText);
 
                // parse the response from JSON string to JS arrays and objects
                var response = JSON.parse(this.responseText);
                console.log(response);
 
                // remove from local array if deleted from server
                if (response.status == "success") {
                    for (var a = 0; a < self.testimonials.length; a++) {
                        var testimonial = self.testimonials[a];
                        if (testimonial.id == form.id.value) {
                            self.testimonials.splice(a, 1);
                            break;
                        }
                    }
                } else {
                    // display an error message
                    alert(response.message);
                }
            }
 
            if (this.status == 500) {
                console.log(this.responseText);
            }
        }
    };
 
    // append form in form data object
    var formData = new FormData(form);
 
    // call AJAX with form data
    ajax.send(formData);
},

Then we need to create a new file named “delete.php” that will handle this request. It will have the following code:

<?php
 
// connect with database
$conn = new PDO("mysql:host=localhost;dbname=test", "root", "");

// get the testimonial just inserted in database
$sql = "SELECT * FROM testimonials WHERE id = ?";
$result = $conn->prepare($sql);
$result->execute([
    $_POST["id"]
]);
$testimonial = $result->fetch();

if (!$testimonial)
{
    // send the response back to client
    echo json_encode([
        "status" => "error",
        "message" => "Testimonial not found."
    ]);
    exit();
}

// remove the picture from folder
unlink($testimonial["picture"]);

// create a query to delete the pricing table from database
$sql = "DELETE FROM testimonials WHERE id = ?";
 
// prepare the query
$result = $conn->prepare($sql);
 
// execute the query
$result->execute([
    $_POST["id"]
]);
 
// send the response back to client
echo json_encode([
    "status" => "success",
    "message" => "Testimonial has been deleted."
]);
exit();

Refresh the page now and you will be able to delete the testimonials as well. The only thing left is to show the testimonials on the user side.

Display Testimonials on User Side

To display testimonials on the user side, you need to download font-awesome and slick, you already have the Bootstrap and Vue JS files in your project folder. You can download font-awesome from here and slick from here. After downloading, paste both folders into your project, we will include them on your user side.

The following code will display the layout for each testimonial using HTML. Which we will render using Vue JS in the next step.

<!-- include bootstrap CSS -->
<link rel="stylesheet" type="text/css" href="bootstrap.min.css" />

<!-- include font awesome -->
<link rel="stylesheet" type="text/css" href="font-awesome/css/font-awesome.min.css" />

<!-- include slick -->
<link rel="stylesheet" type="text/css" href="slick.css" />
<link rel="stylesheet" type="text/css" href="slick-theme.css" />

<!-- include vue js -->
<script src="vue.min.js"></script>

<div class="container" id="testimonialApp" style="margin-top: 30px;">
	<div class="row">
		<div class="col-md-12">
			<h2 class="text-center">Testimonials</h2>
		</div>
	</div>

	<div class="row">
		<div class="col-md-12">
			<div class="items">

				<div class="card" v-for="(testimonial, index) in testimonials">
			        <div class="card-body">
			            <h4 class="card-title">
			            	<img src="https://img.icons8.com/ultraviolet/40/000000/quote-left.png" />
			            </h4>
			            
			            <div class="template-demo">
			                <p>
			                	<span v-text="testimonial.comment"></span>

			                	<span class="show-more-text" v-on:click="loadMoreContent" v-bind:data-index="index">show more</span>
			                </p>
			            </div>

			            <h4 class="card-title">
			            	<img src="https://img.icons8.com/ultraviolet/40/000000/quote-right.png" style="margin-left: auto;" />
			            </h4>
			            
			            <hr />
			            
			            <div class="row">
			                <div class="col-sm-3">
			                	<img class="profile-pic" v-bind:src="testimonial.picture" />
			                </div>
			                
			                <div class="col-sm-9">
			                    <div class="profile">
			                        <h4 class="cust-name" v-text="testimonial.name"></h4>
			                        <p class="cust-profession" v-text="testimonial.designation"></p>
			                    </div>
			                </div>
			            </div>
			        </div>
			    </div>
			    
			</div>
		</div>
	</div>
</div>

<!-- include jquery -->
<script src="jquery-3.3.1.min.js"></script>

<script src="slick.min.js"></script>

<!-- include bootstrap JS -->
<script src="bootstrap.min.js"></script>

<!-- your JS code -->
<script src="script.js?v=<?php echo time(); ?>"></script>

Create a new file named “script.js” and paste the following code in it:

var mainURL = window.location.origin + "/" + window.location.pathname + "/";

var testimonialApp = new Vue({
    el: "#testimonialApp",
    data: {
        testimonials: []
    },
    methods: {
        loadMoreContent: function () {
            var node = event.target;
            var index = node.getAttribute("data-index");

            if (this.testimonials[index].comment.length > 50) {
                // it needs to display less
                node.innerHTML = "show more";
                this.testimonials[index].comment = this.testimonials[index].comment_full.substr(0, 50);
            } else {
                // it needs to display more
                node.innerHTML = "show less";
                this.testimonials[index].comment = this.testimonials[index].comment_full;
            }
        },

        getData: function () {
            // get this app instance
            var self = this;
         
            // call an AJAX to get all testimonials
            var ajax = new XMLHttpRequest();
            ajax.open("POST", "fetch.php", true);
         
            ajax.onreadystatechange = function () {
                if (this.readyState == 4) { // response received
                    if (this.status == 200) { // response is successfull
                        // console.log(this.responseText);
         
                        // parse the response from JSON string to JS arrays and objects
                        var response = JSON.parse(this.responseText);
                        // console.log(response);
         
                        // if there is no error
                        if (response.status == "success") {
                            self.testimonials = response.data;

                            setTimeout(function () {
                                $('.items').slick({
                                    dots: true,
                                    infinite: true,
                                    speed: 800,
                                    autoplay: false,
                                    slidesToShow: 2,
                                    slidesToScroll: 2,
                                    responsive: [{
                                            breakpoint: 1024,
                                            settings: {
                                                slidesToShow: 3,
                                                slidesToScroll: 3,
                                                infinite: true,
                                                dots: true
                                            }
                                        }, {
                                            breakpoint: 600,
                                            settings: {
                                                slidesToShow: 2,
                                                slidesToScroll: 2
                                            }
                                        }, {
                                            breakpoint: 480,
                                            settings: {
                                                slidesToShow: 1,
                                                slidesToScroll: 1
                                            }
                                        }
                                    ]
                                });
                            }, 100);
                        } else {
                            // when there is any error
                        }
                    }
         
                    if (this.status == 500) {
                        console.log(this.responseText);
                    }
                }
            };
         
            // create form data object
            var formData = new FormData();
         
            // actually sending the request
            ajax.send(formData);
        }
    },
    mounted: function () {
        this.getData();
    }
});

At this point, you will be able to view the testimonials added from the admin panel on your website. It will also have a carousel slider. Finally, you can apply some CSS styles to make it look better for the users.

<style>
	.show-more-text {
		background-color: #72a4d5;
	    color: white;
	    padding: 3px 5px;
	    border-radius: 5px;
	    margin-left: 3px;
	    cursor: pointer;
	}
	.more {
		display: none;
	}

	@media (max-width:991.98px) {
		.padding {
			padding: 1.5rem
		}
	}

	@media (max-width:767.98px) {
		.padding {
			padding: 1rem
		}
	}

	.padding {
		padding: 5rem
	}

	.card {
		position: relative;
		display: flex;
		width: 350px;
		flex-direction: column;
		min-width: 0;
		word-wrap: break-word;
		background-color: #fff;
		background-clip: border-box;
		border: 1px solid #d2d2dc;
		border-radius: 11px;
		-webkit-box-shadow: 0px 0px 5px 0px rgb(249, 249, 250);
		-moz-box-shadow: 0px 0px 5px 0px rgba(212, 182, 212, 1);
		box-shadow: 0px 0px 5px 0px rgb(161, 163, 164)
	}

	.card .card-body {
		padding: 1rem 1rem
	}

	.card-body {
		flex: 1 1 auto;
		padding: 1.25rem
	}

	p {
		font-size: 0.875rem;
		margin-bottom: .5rem;
		line-height: 1.5rem
	}

	h4 {
		line-height: .2 !important
	}

	.profile {
		margin-top: 16px;
		margin-left: 11px
	}

	.profile-pic {
		width: 100px;
	}

	.cust-name {
		font-size: 18px
	}

	.cust-profession {
		font-size: 10px
	}

	.items {
		width: 90%;
		margin: 0px auto;
		margin-top: 30px
	}

	.slick-slide {
		margin: 10px;
		height: auto !important;
	}
</style>

So that’s it, you have a dynamic testimonial section fully manageable from the admin panel. If you face any problems in following this, kindly do let us know in the comments section below.

Also learn how to add dynamic pricing table in your website.

Download

[wpdm_package id=’1399′]

10 Javascript Libraries for Every Web Project

In this article, we are going to discuss 10 useful Javascript libraries that you need to use in almost every web project. We will discuss the following libraries along with their use:

  1. Sweetalert
  2. Datetime Picker by XD Soft
  3. Socket IO
  4. DataTable
  5. RichText
  6. PDFObject
  7. Vue JS
  8. Notify JS
  9. Chart JS
  10. TimeCircles

1. Sweetalert

Sweetalert is a JS library used to display alert messages. You can also use it for displaying confirm messages i.e. ask for the user’s confirmation before performing any action. To display a simple alert message:

swal("Message goes here");

To display an alert with a title:

swal("My Title", "Text goes here");

Success alert:

swal("Done", "Your message goes here", "success");

Error alert:

swal("Error", "Error message goes here", "error");

For confirmation dialog:

swal({
	title: "Confirm",
	text: "Are you sure, this data will be removed ?",
	icon: "warning",
	buttons: true,
	dangerMode: true,
})
.then((isOkay) => {
	if (isOkay) {
		//
	}
});

Learn more about how to use the confirmation dialog properly from here. You can also use it as a prompt:

swal("Enter your name:", {
    content: "input",
})
.then((value) => {
    if (value == "") {
        return false;
    }
    console.log(value);
});

You can read more about it from their official page.

2. Datetime Picker by XD Soft

It is a JS library that is used to display a calendar with a date and time picker. You can use it to get the user’s date of birth during registration. The basic usage is, you need to give your input field a unique ID:

<input type="text" id="datetimepicker" />

Then you can display the datetimepicker by calling the function:

jQuery("#datetimepicker").datetimepicker();

Make sure you have jQuery included in your project, this library requires jQuery. You can see this library in action from here. The complete documentation of this library is found on their official page.

3. Socket IO

Socket IO is used for real-time communication. If you are working on a feature that requires data from the server after regular intervals, there are 2 options to achieve this:

  1. Polling
  2. Sockets

Polling

This technique requires calling an AJAX request to the server every few seconds. This will overload the server with a lot of requests. There will be a lot of unnecessary requests to the server and if you have a lot of users, then your website might get crashed.

Sockets

The solution to the above problem is the sockets. Sockets attach a listener to the client-side and emit events from the server. The client is continuously listening to that event, whenever that event is received, the client will perform the action accordingly.

The difference between Polling and Sockets can be explained by this example:

For example, you are going on a trip with your family. Your Dad is driving the car and you are in the back seat. And you are continuously asking your Dad “did we reached the destination ?” every few minutes. This is Polling.

And in the other case, you ask your Dad “please inform me when we reach our destination”. Now you are silently listening to your Dad, who will send an event to you only when you reached your destination. This is Socket.

Learn how to use sockets with Node JS. Complete documentation can be found on their official page.

4. DataTable

This library is used to display data in tabular form. It has a built-in search feature, that allows you to search the entire table. You can search value from any row from any column. The search is case insensitive so you do not have to worry about capital or small letters.

It also has a sorting feature that allows you to sort the data based on any column. It also allows you to decide how many records you want to show on one page (e.g. 25, 50, 75, or 100). It has built-in pagination based on the number of records you are displaying on one page.

Its usage is rather simple. You just need to give your table tag a unique ID:

<table id="table"></table>

Then in your Javascript, simply initialize the library:

$("#table").DataTable();

It also requires jQuery to be included in your project. You can see this library in action from here. You can learn more about it from their official page.

5. RichText

This library applies to your textarea tag. It makes your text area as WYSIWYG (What You See Is What You Get). It enhances the Textarea field and gives it more options like:

  1. Text formatiing (font size, bold, italic, underline, color).
  2. Text alignment (left, center, right, justify).
  3. Headings & paragraphs.
  4. Upload images.
  5. Attach URLs.
  6. Adding tables.
  7. Or you can even add HTML tags too.

Its usage is almost the same as the DataTable library. You need to give your textarea a unique ID:

<textarea id="content" name="content"></textarea>

And in your Javascript, you need to initialize it like this:

$("#content").richText();

This library also requires jQuery to be included. Full documentation of this library can be found here.

6. PDFObject

When working with PDF files, if you want to show PDF files from your website, you have 3 options:

  1. Make the PDF file downloadable, so user can view it offline too.
  2. Display the PDF in a new window or tab.
  3. Embed the PDF in your website specific section.

This library fulfills the 3rd approach. It embeds the PDF file in your website using a div tag. Place a div tag anywhere in your code where you want to show the PDF file:

<div id="pdf"></div>

Then in your Javascript, initialize the library with a URL of the PDf file:

PDFObject.embed("my-file.pdf", "#pdf");

More information can be found on pdfobject.com.

7. Vue JS

Vue JS is more a framework than a library. But since it can be integrated with any project whether it is in core PHP, Laravel, WordPress, or Node JS, it can be called a library. You can learn all functions of it from their official guide. You can check our following projects developed in Vue JS:

  1. Picture Competition Web App – Vue JS, Node JS, Mongo DB
  2. Firebase Storage – Vue JS
  3. Financial Ledger Web App – Vue JS, Node JS, Mongo DB

You will learn a lot about Vue JS from the above projects.

8. Notify JS

This lightweight library is used to display notification pop-ups on the corners of the browser window. This library also requires jQuery to be included before using it. Its usage is extremely easy, you simply need to call the following function when you want to show the notification:

$.notify("New message");

By default, it displays a pop-up on the top right, but you can decide the position of it. You can learn more about this library from here.

9. Chart JS

As the name suggests, this library is used to draw charts and graphs on your website. You can show statistics to the users using this library. It uses canvas to render charts. The following code will create a simple bar chart:

<canvas id="myChart" width="400" height="400"></canvas>
<script>
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
    type: 'bar',
    data: {
        labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
        datasets: [{
            label: '# of Votes',
            data: [12, 19, 3, 5, 2, 3],
            backgroundColor: [
                'rgba(255, 99, 132, 0.2)',
                'rgba(54, 162, 235, 0.2)',
                'rgba(255, 206, 86, 0.2)',
                'rgba(75, 192, 192, 0.2)',
                'rgba(153, 102, 255, 0.2)',
                'rgba(255, 159, 64, 0.2)'
            ],
            borderColor: [
                'rgba(255, 99, 132, 1)',
                'rgba(54, 162, 235, 1)',
                'rgba(255, 206, 86, 1)',
                'rgba(75, 192, 192, 1)',
                'rgba(153, 102, 255, 1)',
                'rgba(255, 159, 64, 1)'
            ],
            borderWidth: 1
        }]
    },
    options: {
        scales: {
            y: {
                beginAtZero: true
            }
        }
    }
});
</script>

There are many graphs & charts provided by this library. You can learn the implementation of all chars from their official page.

10. TimeCircles

This is used to create a countdown timer. You can set the date and the timer will start counting down till that time. This also requires jQuery on your website. Its usage is simple, you just need to give a data-date attribute to the div tag where you want to show the countdown timer. data-date attribute’s value will be the date and time when the timer hits zero. Give it a unique ID so it can be accessible in Javascript.

<div data-date="2021-10-12 23:02:55" id="count-down"></div>

Then in your Javascript, initialize the countdown timer with the following function:

$("#count-down").TimeCircles();

Learn How to Create a Dynamic Countdown Timer. Download the library from here.

What’s Next

You should learn all the libraries above and create small programs in them. But then you need to create a web application where you should apply all the above libraries together. We are going to give you a simple task that helps you learn how to combine all libraries together to create great web applications.

Assignment

Create a simple website that inputs date using DateTimePicker, description using RichText, an input field to upload PDF files and a submit button. When the submit button is pressed, it should show a message using Sweetalert that the message has been sent. It should send an event to the server using Socket IO and the other user should receive that event and display the description in a table using DataTable.

Another user should also see a notification using NotifyJS that a new event has been received. You need to show a countdown timer using TimeCircles till the date field value ends. You should also display the uploaded PDF file in a div tag using the PDFObject library.

Complete assignment’s frontend should be done in Vue JS. If you face any problem in doing this assignment, kindly do let us know in the comments section below.