Create a Picture Competition Website in Express JS, MEVN

Show all Competitions

To show all competitions, first we need to create a separate file because we will be using the same layout multiple times in this project. So include that in your “home.ejs” file:

<%- include ("includes/competitions-slider") %>

Create Competition Layout

Then we need to create a file named “competitions-slider.ejs” inside our “views/includes” folder. It will have the following content:

<div class="row" style="margin-top: 20px;" id="carouselCompetitions">
	<div class="col-md-6" v-for="(competition, index) in competitions" style="margin-bottom: 50px;">
		<div class="card">
			<div class="card-body">
				<div style="display: flex;">
					<div style="position: relative;">
						<img v-bind:src="competition.user1.picture" class="img-responsive" />
						<div class="badge badge-primary" style="right: 0px;">{{ competition.user1.voters.length }}</div>

                        [delete competition button goes here]
					</div>

					<div style="position: relative;">
						<img v-bind:src="competition.user2.picture" class="img-responsive" />
						<div class="badge badge-primary" style="left: 0px;">{{ competition.user2.voters.length }}</div>
					</div>
				</div>

                [votes buttons goes here]
			</div>

			<div class="card-footer">
				<h2 class="text-center" style="color: #3498da;">
					{{ competition.user1.name }} vs {{ competition.user2.name }}
				</h2>
			</div>
		</div>
	</div>
</div>

It will loop through all the competitions fetched from database and create a Bootstrap card for each competition. In each card, it will display both users pictures and names side-by-side. Along with the number of votes to each user, we will create that function later where users will be able to vote on 1 user from the competition.

Fetch Competitions from API

Then you need to add the “competitions” and “allCompetitions” array to your Vue JS “competitionApp”. We are creating 2 arrays because that will be useful when we do the search functionality. Update the “competitionApp” object in your “footer.ejs” file:

var competitionApp = new Vue({
	el: "#competitionApp",
	data: {
		baseUrl: mainURL,
		competitions: [],
		allCompetitions: [],
	}
});

Now we need to fetch all the competitions from Mongo DB. So go back in your “home.ejs” and write the following Javascript code:

<script>
	const isHomePage = true;

	function getAllCompetitions() {
		var formData = new FormData();
        formData.append("accessToken", localStorage.getItem(accessTokenKey));

		myApp.callAjax(navApp.baseUrl + "/getAllCompetitions", formData, function (response) {
            // convert the JSON string into Javascript object
            var response = JSON.parse(response);
            // console.log(response);

            // if the user is created, then redirect to login
            if (response.status == "success") {
            	response.competitions.forEach(function (competition) {
            		competitionApp.competitions.push(competition);
            		competitionApp.allCompetitions.push(competition);
            	});
            } else {
            	swal("Error", response.message, "error");
            }
		});
	}
</script>

A function is created that calls an AJAX request to the server and append all the returned competitions in a local array. The competitions will automatically starts displaying because the “competitionApp” is a Vue JS app.

Now when the authenticated users data is fetched in “footer.ejs” we need to call the “getAllCompetitions()” function if the variable “isHomePage” exists and is true. Following code goes in the [logged in functions will be called here] section in “footer.ejs”:

if (typeof isHomePage !== "undefined" && isHomePage) {
    getAllCompetitions();
}

One final thing remaining is the actual API which we need to create in “server.js” that will fetch all the competitions from the database.

app.post("/getAllCompetitions", async function (request, result) {
	const accessToken = request.fields.accessToken;

	var user = await db.collection("users").findOne({
        "accessToken": accessToken
    });
    if (user == null) {
    	result.json({
            "status": "error",
            "message": "User has been logged out. Please login again."
        });
    }

    var competitions = await db.collection("competitions")
    	.find({})
    	.sort({
    		"createdAt": -1
    	})
    	.toArray();

    // [check if user has already voted]

    result.json({
        "status": "success",
        "message": "Data has been fetched.",
        "competitions": competitions
    });
});

Refresh your home page now and if you are logged in, you will be able to see all the competitions sorting by latest to oldest. We will create a separate sorting function too later in this tutorial that will allow the user to sort the competitions manually.