Building a Simple Quiz App using AngularJS

AngularJS has been out there for quite a while now and is getting increasingly popular. It is a very powerful Javascript MVC framework for building well-structured and maintainable web applications.

This tutorial is not an introduction to AngularJS but to demonstrate how quickly one can build a simple well-structured web application. This is targeted towards people who are already familiar with the basics of AngularJS.

The app will have a very simple structure – a single page app, one question at a time on the screen with four options, a submit button and score. For simplicity, there is no database of questions, instead we define a set of questions in the app itself. This will be the final output.

Let’s start building.

We need two html files for markup, one will have the main markup and the other will have the template for our quiz section. Let’s call them index.html and template.html. We also need an app.js file which will have our app logic and a style.css.

The markup in index.html is straightforward.

<!DOCTYPE html>
<html ng-app="quizApp">
    <head>
        <meta charset="utf-8" />
        <title>QuizApp</title>
        <link rel="stylesheet" href="style.css" />
        <script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
        <script src="app.js"></script>
    </head>
 
    <body>
        <div class="container">
            <h1 class="title">QuizApp</h1>
            <quiz/>
        </div>
    </body>
</html>

The only two interesting things here are the ng-app attribute and the quiz tag. We will define a custom directive for the quiz tag later. Let’s go to the template now. The quiz template will replace the quiz tag above. It consists of 2 sections: a quiz section when the quiz is in progress and an intro section when the quiz app is loaded initially.

<div class="quiz-area" ng-show="inProgress">
</div>
 
<div class="intro" ng-show="!inProgress">
	<p>Welcome to the QuizApp</p>
	<button id="startQuiz" ng-click="start()">Start the Quiz</button>
</div>

As you notice, we used two built-in directives here: ng-show and ng-click. inProgress and start() will be defined later but are self-explanatory. In the quiz area, we either show a question or a message when the quiz is over. We also need a section for score.

<div class="quiz-area" ng-show="inProgress">
	<div ng-show="!quizOver">
	</div>
 
	<div ng-show="quizOver">
		<h2>Quiz is over</h2>
		<button ng-click="reset()">Play again</button>
	</div>
 
	<div id="score">
	</div>
</div>

In app.js we defined a custom directive called quiz. It looks like below:

var app = angular.module('quizApp', []);
 
app.directive('quiz', function() {
    return {
	restrict: 'AE',
	scope: {},
	templateUrl: 'template.html',
	link: function(scope, elem, attrs) {
	    //TODO
	}
    }
});

Now we need some methods to start a quiz, get a question and check the answer.

var app = angular.module('quizApp', []);
 
app.directive('quiz', function(quizFactory) {
	return {
		restrict: 'AE',
		scope: {},
		templateUrl: 'template.html',
		link: function(scope, elem, attrs) {
			scope.start = function() {
			};
 
			scope.getQuestion = function() {
			};
 
			scope.checkAnswer = function() {
			};
		}
	}
});

Let’s implement these methods now.

scope.start = function() {
	scope.id = 0;
        scope.quizOver = false;
	scope.inProgress = true;
	scope.getQuestion();
};

Here scope.id is the id of the current question. We used inProgress in the template and it will be true while the quiz is running.

To get a question, we need a source. We create a factory to get the questions. The factory will return a question when a given question id is requested.

app.factory('quizFactory', function() {
	var questions = [
		{
			question: "Which is the largest country in the world by population?",
			options: ["India", "USA", "China", "Russia"],
			answer: 2
		},
		{
			question: "When did the second world war end?",
			options: ["1945", "1939", "1944", "1942"],
			answer: 0
		},
		{
			question: "Which was the first country to issue paper currency?",
			options: ["USA", "France", "Italy", "China"],
			answer: 3
		},
		{
			question: "Which city hosted the 1996 Summer Olympics?",
			options: ["Atlanta", "Sydney", "Athens", "Beijing"],
			answer: 0
		},
		{	
			question: "Who invented telephone?",
			options: ["Albert Einstein", "Alexander Graham Bell", "Isaac Newton", "Marie Curie"],
			answer: 1
		}
	];
 
	return {
		getQuestion: function(id) {
			if(id < questions.length) {
				return questions[id];
			} else {
				return false;
			}
		}
	};
});

Now we can implement the getQuestion() method in our quiz directive.

scope.getQuestion = function() {
	var q = quizFactory.getQuestion(scope.id);
	if(q) {
		scope.question = q.question;
		scope.options = q.options;
		scope.answer = q.answer;
	} else {
		scope.quizOver = true;
	}
};

Now we can go back to our template and fill the details.

<div class="quiz-area" ng-show="inProgress">
	<div ng-show="!quizOver">
		<h2 id="question">{{question}}</h2>
		<ul id="options">
			<li ng-repeat="option in options">
				<label>
					<input type="radio" name="answer" value="{{option}}">
					{{option}}
				</label>
			</li>
		</ul>
		<button ng-click="checkAnswer()" ng-show="answerMode">Submit</button>
 
                <div ng-show="!answerMode">
			<button ng-click="nextQuestion()" class="next-question">Next</button>
			<span ng-show="correctAns">That is correct!</span>
			<span ng-show="!correctAns">Sorry, that is an incorrect answer.</span>
		</div>
	</div>
 
	<div ng-show="quizOver">
		<h2>Quiz is over</h2>
		<button ng-click="reset()">Play again</button>
	</div>
 
	<div id="score">
		Score: {{score}}
	</div>
</div>

The checkAnswer() method needs to compare the selected option with the correct answer. We also need answerMode so we display the options or the result of current question accordingly. When the user clicks ‘Next’, we need to go to the next question. nextQuestion() does that. Our quiz directive looks like below now:

app.directive('quiz', function(quizFactory) {
	return {
		restrict: 'AE',
		scope: {},
		templateUrl: 'template.html',
		link: function(scope, elem, attrs) {
			scope.start = function() {
				scope.id = 0;
				scope.quizOver = false;
				scope.inProgress = true;
				scope.getQuestion();
			};
 
			scope.reset = function() {
				scope.inProgress = false;
				scope.score = 0;
			}
 
			scope.getQuestion = function() {
				var q = quizFactory.getQuestion(scope.id);
				if(q) {
					scope.question = q.question;
					scope.options = q.options;
					scope.answer = q.answer;
					scope.answerMode = true;
				} else {
					scope.quizOver = true;
				}
			};
 
			scope.checkAnswer = function() {
				if(!$('input[name=answer]:checked').length) return;
 
				var ans = $('input[name=answer]:checked').val();
 
				if(ans == scope.options[scope.answer]) {
					scope.score++;
					scope.correctAns = true;
				} else {
					scope.correctAns = false;
				}
 
				scope.answerMode = false;
			};
 
			scope.nextQuestion = function() {
				scope.id++;
				scope.getQuestion();
			}
 
			scope.reset();
		}
	}
});

As we see, within 100 lines of javascript code we built a simple but fully functional quiz app. Also the markup is pretty expressive and anybody going through it can grasp the logic fairly quickly. That’s the beauty of AngularJS 🙂

You can see the implementation here.
The full source code can be viewed at my GitHub repository.