Build a Quiz Game App with Vue, Vuex and Firebase[Part 1]

This is the first of a three part tutorial on how to build a basic (but fun!!) web app quiz game using Vue.js

Omar Lopez
9 min readNov 15, 2017

If you’re a web-developer, I’m sure you’ve heard a lot about Vue.js (if you haven’t, then stop living under that rock of yours and start reading about it). And probably the 98% of everything you’ve heard is about how good it is, and the thing is that Vue is like the cool new kid on the block. In the b.v era (Before Vue), There were two frameworks that ruled over the whole others, Angular and React. Then this awesome framework showed up and added himself into the fight.

Anyway let’s start coding some lines so that you can fall in love with Vue for yourself.

Explanation:

The app that we are going to build will be a quiz-game. We are going to use a Trivia public API from opentdb.com. We are going to use Vuex, the official state management of Vue, to take control of the questions and the score. Also we’ll use Vue-Router to handle the different states of our app. After the game has been completed we’ll show the player his score with a doughnut chart with vue-chartjs. And the cherry on top… the player could upload his score (if he’s proud of it…) to our firebase database. And could watch the whole list of scores if he wants to.

In this app I used the css of bootstrap v4 for its grid and utility classes. But feel free to use whatever you feel comfortable with.

Disclaimer: Since we are going to explain this app in the easiest way possible, the refactoring of this code will not be made. And a personal recommendation: if you’re using chrome you’ll find the extension for vue called Vue.js devtools very useful.

Original Repo:

https://github.com/omar1893/vue-quiz.git

Styles Credits:

For this project I used the following ‘tools’ and styles to get this arcade aesthetic, even though you can use whatever you like, I wanted to give them the credit that they deserved so here we go:

  • Bootstrap 4: Style Framework. I downloaded its css and imported it in my main.js. You can download it from its website.
  • 8-bit Wonder: Font. I used this cool font in my project, just declaring it as a font-face. You can download it from here.
  • ‘Some Buttons…’: I used this awesome button style from the codepen project of Ottis Kelleghan. Just paste it in the App.vue style section. Check this codepen project.

Installation:

We are going to use node package manager (npm) so if you don’t have node.js installed yet just download it from its official page. Node.js.

Let’s install the vue-cli to get an initial template for our project:

npm install -g vue-cli

Then let’s initialize our project by running the next command:

vue init webpack vue-quiz

This is a straightforward procedure, the only thing to remember is to press “Y” when the Vue-Cli ask you if you want to use vue-router. Then:

cd vue-quiznpm installnpm run dev

It should open a basic template, which we’ll use to start our project.

Routes and First component

So we’re going to work in the src folder and the initial structure gotta look like something like this:

- src- — — assets- — — components- — — — — — hello.vue- — — router- — — — — — index.js- — — app.vue- — — main.js

So we’re going to work in the src folder and the initial structure has to look something like this:

Right now we’re going to work in the ‘components/hello.vue’ file. There is nothing in this component’s html and css that we’ll use for our app so let’s delete everything inside the <div class=”hello”> and also rename it to<div class=”start”> and delete everything inside the style tags.

Also let’s define the styles for our start component (don’t forget to remove the vue logo from App.vue 😉 ):

Also we’re going to change the name of this file from ‘hello.vue’ to ‘start.vue’ (this last part is not ULTRA-NECESSARY, it was made to make our app-structure as understandable as possible).

This is not the last thing that we’ve seen from this component. But for the moment we’re going to focus in something else.

Defining our first route.

If we take a look to the ‘router/index.js’ file, we’ll watch the following code:

In case you understand it, well… Good for you. In case you haven’t, don’t worry I’ll explain it for you.

First we have to import the parent component that defines our view. In our case we have to import the “start” component. It has to look like this:

import Start from ‘@/components/Start’

Then in our default object we create an array of objects. Every object we create in here represents a new route — OR view — of our app, and we choose which will be the name of the url, also we’ll specify the component that will be displayed in that url and set the name of the route with which we can programmatically access this view. Just that easy.

So… Let’s define the route of our first component. It should look something like this:

With this code we have set our first route. Now let’s initialize our store (Vuex).

Vuex.

I think that the Vuex documentation has the most simple and understandable concepts in the history of the mankind. Now in case you need something simpler, I’ll give it to you. The store is like a box where we can save data from one component and later access it from every other component (even if those components are not related).

First of all let’s install ‘vue-resource’ so our app can handle http requests. Let’s run the next command in the root of our project:

npm install vue-resource —-save

Now let’s install vuex in our root folder. In order to do this, we’ll run the next command:

npm install vuex —-save

After the installation is completed we need to create a folder inside ‘src’ called ‘store’, and inside of it, we should create a file called ‘store.js’. Inside our ‘store/store.js’ the first thing that we should do, is import Vue and Vuex, and initialize the structure of our store. Let’s do it:

Again I recommend that you use the documentation of Vuex, but I’ll try to explain it easier for you. In this chapter we are going to use three elements of Vuex — state, mutations and actions:

  • State: In this object we’re going to store the data that we want to share in our application.
  • Mutations: these are functions that perform modifications to the state.
  • Actions: These are also functions like mutations only with two differences. First, actions are used for asynchronous operations (So they are great for doing http requests), and second, the actions only should modify the store by calling mutations.

Now we’ll initialize the data of our game:

state: { 
results: { correct_answers: 0, incorrect_answers: 0 },
questions: [],
},

So, basically here we have the amount of correct and incorrect answers (initialized in 0), and the questions array (empty, of course). After we got this understood, let’s create our action to get the questions from the api.

So let’s write our actions:

actions: { 
getQuestions(context) {
return new Promise(function (resolve, reject) {
Vue.http.get('https://opentdb.com/api.php?amount=10')
.then(function (data) {
console.log(data.body.results);
context.commit('translateData', data.body.results)
resolve(true) })
.catch(function (data) {
console.log('Error:', data)
reject(false)
})
})
}
}

There are two important things here to understand. First ‘what is context?!’, the context parameter is an object that has the properties and methods of this store, so, using the context object we are going to be able of using the mutations (we’ll get there 😌, relax). Second, ‘why did you use a Promise around a http request that already handles promises?!’, i did this in order to know from ‘start’ component when this method is done. Now let’s proceed.

Now, the answers and the questions sometimes have special characters like (“?!) in a weird encoding type. So it’s our duty to transform it into something readable. Besides that we need to create another utility function that we’ll use in order to shuffle an array. We can achieve these goals by writing a couple of functions out of the store object just to use it like utilities… Nothing related to vue so relax.

htmlDecode = function (input) {
var e = document.createElement('div');
e.innerHTML = input;
return e.childNodes[0].nodeValue;
},
shuffle = function (a) {
for (let i = a.length; i; i--) {
let j = Math.floor(Math.random() * i);
[a[i - 1], a[j]] = [a[j], a[i - 1]];
}
return a;
}

Now let’s write our first mutation:

mutations: {
translateData: function (state, questions) {
var array = []
for (var i = 0; i < questions.length; i++) { questions[i].question = htmlDecode(questions[i].question); questions[i].correct_answer = htmlDecode(questions[i].correct_answer); questions[i].incorrect_answers.push(questions[i].correct_answer);
for (var j = 0; j < questions[i].incorrect_answers.length; j++) { questions[i].incorrect_answers[j] = htmlDecode(questions[i].incorrect_answers[j]);
}
shuffle(questions[i].incorrect_answers); }
state.questions = questions;
},
},

Now let’s make a quick recap of what we just did. First the ‘translateData’ function gets two parameters (state and questions). With the state parameter we can access to the state object of our store (Duh!). And the questions parameter is the array that we got on the ‘getQuestion’ action.

Then we iterate our questions array and decode the ‘question’. After that we push a reference of the correct answer inside the incorrect answers array to get the whole options available inside one array. Then we make another iteration to decode every answer in the incorrect_answers array. At the end of the initial ‘for’ loop, we’ll shuffle the incorrect_answers array because if we don’t do this, the correct answer will always be in the fourth position of our game (third position arraystically” speaking).

Finally in the ‘state.questions = questions;’ line we’ll save the questions array in our state.

Next, we’re going to import our store and vue-resource in order to use it in our app. So let’s open our ‘src/main.js’, and let’s do the following:

import {store} from './store/store'

Then inside of our ‘Vue’ instance let’s declare our store by adding:

store: store,

It should look something like this:

Now let’s get back to our ‘components/start.vue’ file.

Calling Our Action in our component.

Now inside our start component let’s write in the methods object the following function:

methods:{
getQuestions(){
var router = this.$router;
this.$store.dispatch('getQuestions')
.then(function(data){
console.log("Successful Request")
router.push({name: 'game'})
})
.catch(function(data){
console.log("Rejected Request")
})
}
}

So… let’s explain it. First with ‘this.$store.dispatch(‘getQuestions’)’ we invoke the action inside our store to get the questions from our api. Next to that, we have .then() and .catch() to know when our promise inside our action has been resolved. If the promise is successful we are going to get in console the “Successful Request” and we’re going to be headed to ‘/game’ (we’ll create this view in the next chapter ⌛️). If it doesn’t we get in our console “Rejected Request”.

Now we only have to make that our “Play” button call this function. In order to do that we have to attach an event to our button. In order to do that we only have to do:

<button class=”action-button animate green w-100 mb-3" v-on:click='getQuestions()'> 
Play
</button>

Now we have the beginning of our project done. In the next part we are going to face the game structure and logic.

See you then.

--

--