AngularJS and ASP.net Core 1.0 JumpStart (Part – II)

Adding about and howto controllers

Welcome to the part two of AngularJs and ASP.net Core 1.0 JumpStart! In part one we configured routing for our app and also attached views to those routes. We will implement the game logics in this post. To handle client side business logics, we will implement three angular controllers respective to the three views we have. Let’s implement the controllers for the howto and about views first since they are pretty much easier. In your app folder under js add two new folders named "howto" and "about" and add two javascript files named howto.controller.js and contact.controller.js respectively.

Here are the two scripts for them,

howto.controller.js


(function () {
    'use strict';

    angular
        .module('answerit')
        .controller('HowToController', HowToController);

    HowToController.$inject = [];

    function HowToController() {
        var vm = this;

        vm.howto = "Welcome to Answer It! A game for every age of people. Start the game and try answering all the questions in the limited amout of time. If you get all the answers correct, You will get the glorious prize of 'NOTHING'";

        activate();

        ////////////////

        function activate() {}
    }
})();

about.controller.js


(function () {
    'use strict';

    angular
        .module('answerit')
        .controller('AboutController', AboutController);

    AboutController.$inject = [];

    function AboutController() {
        var vm = this;

        vm.about = "This app is created using Angular.js and Asp.Net Core 1.0. To know more about Angular.js, please go to https://angularjs.org/ and for ASP.net Core 1.0, go to https://docs.asp.net/en/latest/";

        activate();

        ////////////////

        function activate() {}
    }
})();

Here HowToController and AboutController, both are controllers under answerit module. They don’t have any dependencies (empty third braces []). Here vm means ViewModel and it works just like the $scope variable which Angular provides for two way data binding. We add a string type property named about to the vm in AboutController and initialized with some about text. Likewise, in HowToController we add a string type property named howto to the vm and initialized with some howto text.

To glue up the controllers with the routes, let’s go to app.module.js and add two properties, controller and controllerAs to the appropriate routes. The name of the controller goes into the controller property where the name of the ViewModel, vm goes into the controllerAs property. Here is how the app.module.js looks now,


(function () {
    'use strict';

    angular.module('answerit', [
        'ui.router'
    ])
        .config(['$urlRouterProvider', '$stateProvider', configRoutes])
        .run(['$rootScope', '$state', configureStateWithScope]);

    function configRoutes($urlRouterProvider, $stateProvider) {
        $urlRouterProvider.otherwise('/play');

        $stateProvider
            .state('play', {
                url: '/play',
                templateUrl: 'templates/play.html'
            })
            .state('howto', {
                url: '/howto',
                templateUrl: 'templates/howto.html',
                controller: 'HowToController',
                controllerAs: 'vm'
            })
            .state('about', {
                url: '/about',
                templateUrl: 'templates/about.html',
                controller: 'AboutController',
                controllerAs: 'vm'
            });
    }

    function configureStateWithScope($rootScope, $state) {
        $rootScope.$state = $state;
    }
})();

Let’s show the "howto" and "about" text in their respective views. Add these respective markups in the howto.html and about.html files,

howto.html


<h2>{{vm.howto}}</h2>

about.html


<h2>{{vm.about}}</h2>

Finally add the howto.controller.js and about.controller.js references after the app.module.js in the index.html file. Run the app and you will get the "howto" and "about" text in the respective views.

Adding the play controller

Just like we add the controllers for about.html and howto.html, we will also add a controller for the play view. Do the same as before. Add a folder under app, create a javascript file named play.controller.js and add the following scripts.


(function () {
    'use strict';

    angular
        .module('answerit')
        .controller('PlayController', PlayController);

    PlayController.$inject = [];

    function PlayController() {
        var vm = this;

        vm.test = "This is for testing";

        activate();

        ////////////////

        function activate() {}
    }
})();

I’ve added the test property just to check whether the routes are working correctly or not. Update the routesConfig like before with controller and controllerAs properties for play state.


(function () {
    'use strict';

    angular.module('answerit', [
        'ui.router'
    ])
        .config(['$urlRouterProvider', '$stateProvider', configRoutes])
        .run(['$rootScope', '$state', configureStateWithScope]);

    function configRoutes($urlRouterProvider, $stateProvider) {
        $urlRouterProvider.otherwise('/play');

        $stateProvider
            .state('play', {
                url: '/play',
                templateUrl: 'templates/play.html',
                controller: 'PlayController',
                controllerAs: 'vm'
            })
            .state('howto', {
                url: '/howto',
                templateUrl: 'templates/howto.html',
                controller: 'HowToController',
                controllerAs: 'vm'
            })
            .state('about', {
                url: '/about',
                templateUrl: 'templates/about.html',
                controller: 'AboutController',
                controllerAs: 'vm'
            });
    }

    function configureStateWithScope($rootScope, $state) {
        $rootScope.$state = $state;
    }
})();

I’ve updated the play.html file with following markup


<h2>{{vm.test}}</h2>

Now add reference to the play.controller.js script into the index.html file and give your app a spin. Everything should work fine. You should see the test text in the play view.

Implementing game logic

Copy and paste the following scripts into your PlayController,


(function () {
    'use strict';

    angular
        .module('answerit')
        .controller('PlayController', PlayController);

    PlayController.$inject = ['$timeout'];

    function PlayController($timeout) {
        var vm = this;

        var TIMER_MAX_VALUE = 5;

        vm.timerValue = 0;
        vm.isTimerStopped = false;
        vm.isAnswered = false;
        vm.isRightAnswer = false;

        vm.timer = timer;
        vm.giveAnswer = giveAnswer;

        function giveAnswer(ans) {
            vm.selectedAnswer = ans;
            vm.isAnswered = true;
            vm.isRightAnswer = ans.result;
        }

        function timer() {
            $timeout(function () {
                if (vm.isAnswered || (vm.timerValue === TIMER_MAX_VALUE)) {
                    vm.isTimerStopped = true;
                } else {
                    vm.timerValue++;
                    timer();
                }
            }, 1000);
        }

        activate();

        function activate() {
            vm.question = "What is the scientific name of Leopard?";

            vm.answers = [{
                    "answer": "Panthera tigris",
                    "result": false
                },
                {
                    "answer": "Panthera leo",
                    "result": false
                },
                {
                    "answer": "Vulpes vulpes",
                    "result": false
                },
                {
                    "answer": "Panthera pardus",
                    "result": true
                }];

            vm.timer();
        }
    }
})();

The code is pretty simple. Basically when a user goes to the play view, a timer starts. In the activate() function we have two properties, one, a string property named vm.question which is in initialized with a default question and two, an array of possible answers with result flags. The maximum amount of time per question has been set with a constant called MAX_TIMER_VALUE. So when the timer starts a user got five seconds to give a possible answer. Every time when a second is passed vm.timerValue is incremented. If the vm.timerValue is equals to the MAX_TIMER_VALUE then the time is up for giving an answer, means the player lost the game and he/she should be able see what was the right and the wrong answers. If the user gave an answer between five seconds but he/she is wrong, then only the wrong answer that was selected is highlighted and the correct answer is also shown. We have different Boolean flags to handle these two situations. We will use these flags in our view (play.html) to handle the stylings when one of those two scenario occurs. Also notice that $timeout is an angular service which helps us to execute a task after a specified time. To be able to use the $timeout service we inject it in our controller. That’s all for now. Let’s go back to the play.html and modify the existing markup


<div class="row">
    <h1 class="question">{{vm.timerValue}}</h1>
</div>

<div class="row">
    <h1 class="text-center">{{vm.question}}</h1>
</div>
<br />
<div class="row">
    <div class="col-md-6 col-md-offset-3">
        <div class="list-group">
            <a href="#" class="list-group-item" ng-repeat="ans in vm.answers" ng-class="{'list-group-item-success': (vm.isTimerStopped && ans.result) || (vm.isRightAnswer && ans.result), 'list-group-item-danger':  vm.isAnswered && !vm.isRightAnswer && !ans.result && vm.selectedAnswer === ans}" ng-click="vm.giveAnswer(ans)">
                <span class="badge">{{ans.result}}</span>{{ans.answer}}
            </a>
        </div>
    </div>

As you can see we have used some html tags for showing the timer value and the question. Then we have bootstrap list-group, under which we have used an angular directive called ng-repeat to repeat through the answers and show it as a list-group-item individually. In ng-class we have styling conditions for two scenarios as I mentioned above. The conditions are given below

  • If the timer is stopped, means the time is up (user didn’t give any answer) and the result flag of the answer is true then decorate the correct answers with a class of list-group-item-success (green fill on the list item). Do the same if the user gave the right answer.
  • If the question was answered but the selected answer was incorrect then apply list-group-item-danger (red fill on the list item) just on the selected answer. Do the same if the timer has stopped and the player didn’t answer the question (wrong answers are red).

Run and check the app if its working correctly. If you find another easy logic to achieve the same scenario, post it in the comment section.

N.B: The right side Boolean flag badged are there only for testing. It will be removed when we will finish building the full app.

In next post we will build a server using ASP.net Core 1.0 MVC and serve up multiple questions using API. See you in the next one.