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.
Hello, can you help me please?
I cloned your github repository with this app, and it works perfectly in the browser on my laptop, but when i build it , and install the apk, i’ve tested in all ways possible, but the submit button won’t do anything:(
You can start the quizz, you can go to the next question, but the submit button won’t do anything, not even alerting if i alert(‘blabla’), ONLY on mobile, on browser works perfectly. Android 7.0 samsung galaxy s6 edge.
I think this is one of the most important info for me. And i’m glad reading your article. But should remark on some general things, The web site style is perfect, the articles is really excellent : D. Good job, cheers
I am currently working on my application in tutored teacher evaluation project I would like if you have guidance on a said realization especially at the level of creating dynamic question. thank you
It was really a great explanation. Thanks a lot for such deep and simple manner explanations. Keep it on. Cheers!!! 🙂
Excellent article, great to read unique content we can all learn between all users. I do as make tutorials and videos for others, if you have Instagram I would be glad to have you as fan : https://www.instagram.com/earthbydrones Have a good day! Earth By Drones Com Jeff