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

Creating the ASP.net Core 1.0 MVC project

Okay, this part of the series will be pretty much big. So take time and read all the stuffs that I’ve done here. We will create a ASP.net Core 1.0 MVC project first. If you don’t have it installed already, please visit this link and follow the instructions to install it in your favorite operating system,

https://docs.asp.net/en/latest/getting-started/index.html

After installing, open up your Visual Studio 2015 and create a new ASP.NET Web Application. Name it “AsnwerIt” and hit ok. From the template selection window select “Web Application” under the ASP.NET 5 templates section. You might be asking yourself where is ASP.NET Core 1.0 templates? Well ASP.net Core 1.0 is formerly known as ASP.net 5. Since it is still in the RC version they didn’t change the name yet. So don’t be confused.

To know why the name was changed to ASP.net Core 1.0, please refer to this link

https://blogs.msdn.microsoft.com/webdev/2016/02/01/an-update-on-asp-net-core-and-net-core/

Another thing you need to do before you hit the ok is to click on the “change authentication” button on the right and change your authentication type to “No Authentication”. After the project is created you will have a “wwwroot” folder where all the client side resources go. Before copy pasting the files and folders we've created for our client side app let’s do some housekeeping. The project has bower installed by default. Bower is a package manager tool for resolve client side javascript libraries (Just like NPM, which is used to resolve node packages and Nuget, which is used to resolve .net packages). Let’s install the client packages we need. We already have bootstrap installed, we only need Angular. Expand the “Dependencies” node and right click on the Bower and select “Install Bower Packages”. From the "Browse" tab search and install Angular. After installing, angular libraries will be available in the “Lib” folder under “wwwroot”. Likewise, also install the “ui-router” package.

So why did I installed these libraries when I’m using CDN links in client side? Well that’s the housekeeping I was talking about.

Expand the “Views” folder and then in the “Shared” folder you will have a .cshtml file named _Layout.cshtml. It’s a default shell for all the views which are loaded in the @RenderBody() method. On the top there are Asp.net tags which determines in which environment your application is currently in. Basically in the production and staging environment you would want to load the minified version of client libraries from CDN links where in the development environment you would resolve them through your downloaded library files. Nothing much to change in the <head> tag right now. Let’s scroll down and in the bottom section where all the scripts are loaded add the reference to your angular and ui-router scripts. Here is the sample,


<environment names="Development">
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
        <script src="~/lib/angular/angular.js"></script>
        <script src="~/lib/ui-router/release/angular-ui-router.js"></script>
        <script src="~/js/site.js" asp-append-version="true"></script>
    </environment>
    <environment names="Staging,Production">
        <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.4.min.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
                asp-fallback-test="window.jQuery">
        </script>
        <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.5/bootstrap.min.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
                asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal">
        </script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.min.js"
                asp-fallback-src="~/lib/angular/angular.min.js"
                asp-fallback-test="window.angular">
        </script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.min.js"
                asp-fallback-src="~/lib/ui-router/release/angular-ui-router.min.js"
                asp-fallback-test="window.angular && window.angular.module('ui.router')">
        </script>
        <script src="~/js/site.min.js" asp-append-version="true"></script>
    </environment>

Now import the resources from our client project. Remove all the existing styles from site.css under wwwroot > css and copy all the styles from cover.css and paste it in there. Replace the contents of js folder with the contents of the client app. Also add the templates folder in “wwwroot”.

Now, time to update the _Layout.cshtml with all the updated references. Here is the final look of the _Layout.cshtml,


<!DOCTYPE html>
<html ng-app="answerit">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - AnswerIt</title>

    <environment names="Development">
        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
        <link rel="stylesheet" href="~/css/site.css" />
    </environment>
    <environment names="Staging,Production">
        <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.5/css/bootstrap.min.css"
              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
              asp-fallback-test-class="nav" asp-fallback-test-property="display" asp-fallback-test-value="block" />
        <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
    </environment>
</head>
<body>
    <div class="site-wrapper">

        <div class="site-wrapper-inner">

            <div class="cover-container">

                <div class="masthead clearfix">
                    <div class="inner">
                        <h3 class="masthead-brand">Answer It!</h3>
                        <nav>
                            <ul class="nav masthead-nav">
                                <li ng-class="{ 'active': $state.includes('play') }"><a ui-sref="play">Play</a></li>
                                <li ng-class="{ 'active': $state.includes('howto') }"><a ui-sref="howto">How To</a></li>
                                <li ng-class="{ 'active': $state.includes('about') }"><a ui-sref="about">About</a></li>
                            </ul>
                        </nav>
                    </div>
                </div>

                <div class="inner cover">
                    @RenderBody()
                </div>

                <div class="mastfoot">
                    <div class="inner">
                        <p>Answer It!, by <a href="https://twitter.com/FiyazBinHasan">@@FiyazBinHasan</a>.</p>
                    </div>
                </div>

            </div>

        </div>
    </div>


    <environment names="Development">
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
        <script src="~/lib/angular/angular.js"></script>
        <script src="~/lib/ui-router/release/angular-ui-router.js"></script>
        <script src="~/js/app/app.module.js" asp-append-version="true"></script>
        <script src="~/js/app/howto/howto.controller.js" asp-append-version="true"></script>
        <script src="~/js/app/about/about.controller.js" asp-append-version="true"></script>
        <script src="~/js/app/play/play.controller.js" asp-append-version="true"></script>
    </environment>
    <environment names="Staging,Production">
        <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.4.min.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
                asp-fallback-test="window.jQuery">
        </script>
        <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.5/bootstrap.min.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
                asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal">
        </script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.min.js"
                asp-fallback-src="~/lib/angular/angular.min.js"
                asp-fallback-test="window.angular">
        </script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.min.js"
                asp-fallback-src="~/lib/ui-router/release/angular-ui-router.min.js"
                asp-fallback-test="window.angular && window.angular.module('ui.router')">
        </script>
        <script src="~/js/app/app.module.js"></script>
        <script src="~/js/app/howto/howto.controller.js"></script>
        <script src="~/js/app/about/about.controller.js"></script>
        <script src="~/js/app/play/play.controller.js"></script>
    </environment>

    @RenderSection("scripts", required: false)
</body>
</html>

Notice that, in place of the <ui-view> directive we have @RenderBody(). Since MVC routing is configured to show the Index.cshtml under "Home" folder we must replace the markup of the file with the <ui-view> directive. Here goes the new Index.cshtml markup


<ui-view></ui-view>

Remove the About.cshtml and Contact.cshtml under "Home" folder since we don’t need them anymore. Run your app and everything should be working fine.

Creating a API

All we’ve been doing is showing a single question with possible answers. Let’s add a bunch of questions and answers like this one. But we won’t do it in the PlayController. Let’s create a new folder under “wwwroot” named "data" and add a json file named "qas.json". Paste this json provided below in that file.


[
  {
    "question": "What is the scientific name of Leopard?",
    "answers": [
      {
        "answer": "Panthera tigris",
        "result": false
      },

      {
        "answer": "Panthera leo",
        "result": false
      },

      {
        "answer": "Vulpes vulpes",
        "result": false
      },

      {
        "answer": "Panthera pardus",
        "result": true
      }
    ]
  },
  {
    "question": "What is the scientific name of Cat?",
    "answers": [
      {
        "answer": "Felis catus",
        "result": true
      },

      {
        "answer": "Canis lupus familiaris",
        "result": false
      },

      {
        "answer": "Canis lupus",
        "result": false
      },

      {
        "answer": "Ursidae",
        "result": false
      }
    ]
  },
  {
    "question": "What is the scientific name of Dolphon?",
    "answers": [
      {
        "answer": "Spheniscidae",
        "result": false
      },

      {
        "answer": "Selachimorpha",
        "result": false
      },

      {
        "answer": "Delphinidae",
        "result": true
      },

      {
        "answer": "Phocidae",
        "result": false
      }
    ]
  }
]

As you can see we have three questions and their possible answers in a json format. Two things we will do now,

  • Make an API service which reads all the contents of the qas.json file and returns them in a json format
  • Make an Angular service script which will call the API through a $http service and extract the json from it and use it the PlayController

If you are asking yourself, why is he creating a API to serve the json content? Well I could have just call the qas.json from my application URL since it is in the “wwwroot” folder. I’m doing the round tripping just to make you familiar with the notion of API controller. If you ever feel like you will be going to download all the questions and answers from another server or an attached database (remove the JsonProperty attributes), this is how will do it. So, let’s create the API first. Under "Controllers" folder, add a Web API Controller class. Name it QasController

We want to read the content of the qas.json, deserialize them into a list of objects and send it back. Let’s create the object classes first. Right click on the project and add a folder named “Models” and add these two POCO classes,

Question.cs


using Newtonsoft.Json;
using System.Collections.Generic;

namespace AnswerIt.Models
{
    public class Question
    {
        public Question()
        {
            Answers = new List();
        }
        [JsonProperty("question")]
        public string Text { get; set; }
        [JsonProperty("answers")]
        public ICollection Answers { get; set; }
    }
}

Answer.cs


using Newtonsoft.Json;

namespace AnswerIt.Models
{
    public class Answer
    {
        [JsonProperty("answer")]
        public string Text { get; set; }
        [JsonProperty("result")]
        public bool Result { get; set; }
    }
}

Notice that the attribute named JsonProperty is part of the Json.Net library. So we have to add the namespace for it in the usings section. The attribute is used for mapping POJO (plain old javascript objects) properties to POCO (plain old class objects) properties.


Now go back to the API and replace the existing code with the code given below,

using System.Collections.Generic; using Microsoft.AspNet.Mvc; using Newtonsoft.Json; using System.IO; using Microsoft.AspNet.Hosting; using AnswerIt.Models; namespace AnswerIt.Controllers { [Route("api/[controller]")] public class QasController : Controller { [HttpGet] public IEnumerable Get() { var virtualPath = "data/qas.json"; var env = new HostingEnvironment(); if (string.IsNullOrWhiteSpace(env.WebRootPath)) { env.WebRootPath = Directory.GetCurrentDirectory(); } var filePath = env.MapPath(virtualPath); var json = System.IO.File.ReadAllText(filePath); var qas = JsonConvert.DeserializeObject>(json); return qas; } } }

This code loads the qas.json from the "data" folder, reads all the contents and deserialize them in a list of Question objects

Creating the play service

Under wwwroot > js > app > play, create an AngularJs Service script and name it play.service.js.

By the way you won’t find any AngularJs predefined templates if you don’t have sidewaffle templates installed into Visual Studio. Here is link to download sidewaffle,

http://sidewaffle.com/

If you don’t want sidewaffle templates then just create an empty javascript file and paste the following script into it,


(function () {
    'use strict';

    angular
        .module('qa.app')
        .service('PlayService', PlayService);

    PlayService.$inject = ['$http'];

    function PlayService($http) {
        this.getQas = getQas;

        function getQas() {
            return $http({
                'url': '/api/qas',
                'method': 'GET',
                'content-type': 'application/json'
            }).then(function (qas) {
                return qas;
            });
        }
    }
})();

In PlayService we inject Angular’s $http service. In getQas() function we made a HTTP GET call to our API. Notice that Angular $http service return a promise. To extract result from this promise we have to resolve it. We will make a call to this PlayService from our routeConfig() function in app.module.js. Then we will resolve the promise and pass the result into PlayController. Before we update the PlayController let’s add the play.service.js script in the _Layout.cshtml. Here is the updated references in _Layout.cshtml


<environment names="Development">
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
        <script src="~/lib/angular/angular.js"></script>
        <script src="~/lib/ui-router/release/angular-ui-router.js"></script>
        <script src="~/js/app/app.module.js" asp-append-version="true"></script>
        <script src="~/js/app/howto/howto.controller.js" asp-append-version="true"></script>
        <script src="~/js/app/about/about.controller.js" asp-append-version="true"></script>
        <script src="~/js/app/play/play.controller.js" asp-append-version="true"></script>
        <script src="~/js/app/play/play.service.js" asp-append-version="true"></script>
    </environment>
    <environment names="Staging,Production">
        <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.4.min.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
                asp-fallback-test="window.jQuery">
        </script>
        <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.5/bootstrap.min.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
                asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal">
        </script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.min.js"
                asp-fallback-src="~/lib/angular/angular.min.js"
                asp-fallback-test="window.angular">
        </script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.min.js"
                asp-fallback-src="~/lib/ui-router/release/angular-ui-router.min.js"
                asp-fallback-test="window.angular && window.angular.module('ui.router')">
        </script>
        <script src="~/js/app/app.module.js"></script>
        <script src="~/js/app/howto/howto.controller.js"></script>
        <script src="~/js/app/about/about.controller.js"></script>
        <script src="~/js/app/play/play.controller.js"></script>
        <script src="~/js/app/play/play.service.js"></script>
    </environment>

Updating the play controller

Go to app.module.js and update the routeConfig() with the script given below,


(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',
                resolve: {
                    qas: ['PlayService', function (PlayService) {
                        return PlayService.getQas();
                    }]
                }
            })
            .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;
    }
})();

As you can see we are resolving the promise returned from our PlayService and pass it as an object named qas to the PlayController. Here is the final script for the PlayController,


(function () {
    'use strict';

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

    PlayController.$inject = ['PlayService', 'qas', '$timeout'];

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

        var TIMER_MAX_VALUE = 5;

        vm.timer = timer;
        vm.giveAnswer = giveAnswer;
        vm.goNext = goNext;
        vm.startAgain = startAgain;

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

        function goNext() {
            vm.current++;

            vm.currentQuestion = vm.qas[vm.current].question;
            vm.currentAnswers = vm.qas[vm.current].answers;

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

            vm.timer();

        }

        function startAgain() {
            activate();
        }

        function timer() {
            $timeout(function () {
                if (vm.isAnswered || (vm.timerValue === TIMER_MAX_VALUE)) {
                    vm.isTimerStopped = true;
                    if (vm.current < vm.qasCount - 1) {
                        vm.hasNext = true;
                    } else {
                        vm.hasNext = false;
                        vm.gameOver = true;
                    }
                } else {
                    vm.timerValue++;
                    vm.timer();
                }
            }, 1000);
        }

        activate();

        function activate() {
            vm.qas = qas.data;
            vm.qasCount = vm.qas.length;

            vm.current = 0;
            vm.hasNext = false;
            vm.gameOver = false;


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


            vm.currentQuestion = vm.qas[vm.current].question;
            vm.currentAnswers = vm.qas[vm.current].answers;

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

Notice that we’ve updated the dependencies. We have injected the PlayService and the resolved qas object into it. Other than this the game logic is pretty simple. It is almost same as before except that we have now modify the script to be able to work with multiple questions and answers. Important thing to notice is we have two new functions named goNext() and startAgain(). The goNext() function is bind to a button which is only shown when the user gave an answer to the current question or the time is up for the user to answer the current question. The startAgain() function is bind to a button which is shown when all the questions are answered. startAgain() calls to the activate() function which eventually reset the game by initializing all the variable to their initial state.

Updating the play view

Finally let’s update the play view. We will only add two new buttons, functions of which we mentioned earlier, one for going to the next view and another for resetting the game. Here is the final markup for the play.html,


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

<div class="row">
    <h1 class="text-center">{{vm.currentQuestion}}</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.currentAnswers" 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>
</div>
<br />
<div class="row">
    <button class="btn btn-info text-center" ng-show="vm.hasNext" ng-click="vm.goNext()">Next</button>
    <button class="btn btn-info text-center" ng-show="vm.gameOver" ng-click="vm.startAgain()">Start again</button>
</div>

And that’s it we are finished. Build and run the app and you will have a fully working Q&A game built with Angular and ASP.net Core 1.0.

For further improvement to the app add a result view which will show at the end with the total points acquired by the player. I’ve attached the download link for the project at the end of the article. Download and play with it. Use your imaginations and add features to it. Post a comment if you are stuck or didn't understand something. Thanks for reading.

Download source code

Download it from GitHub - https://github.com/fiyazbinhasan/Answer-It.git

If you like this repository, please give a star to it so that you can have updates when something new happens in the repo