December 5, 2012 3:08pm
AngularJs, NancyFx, TypeScript, and BootStrap Oh My!

An increasingly popular trend lately is to have the client handle rendering of your content.  So long server side rendering you have been replaced with some fancy JavaScript libraries. 

So what are we building?

Well as you might know I work at a credit union so my natural demo is something financial related.  So we are building AngularBanking.  A rudimentary online banking application that allows the user to login, view balances of their accounts, change the description of the account, view history and perform transfers between each account.  The demo application is single user since I did not want to increase complexity.

So what are we using?

AngularJs – A very powerful MVC style framework for client side routing, data binding, and provides you with a framework to create rich easily testable JavaScript applications.

TypeScript – A sueprset of JavaScript that gives you ability to add type annotations so you can gain confidence in your JavaScript as you develop large scale applications.  TypeScript compiles down to JavaScript so you gain the power of typing

BootStrap – A very popular framework of css and js that allows you to create websites faster.

NancyFX – Amazing library that allows you to build http based services. 

Asp.Net Web Application – Our hosting ground for NancyFX and the corresponding bits for api.

The Bits

Asp.net Web Application

This is as bare bones as you can with a web application.  The project started off as New Project, Empty Web Application as I did not want any pieces configured from the start, no MVC, no web forms, just a site to host my api.

NancyFX

Nancy is so easy to use and get up and running quickly.  All you have to do is add the Nuget packages for Nancy and Nancy.Hosting.Aspnet.  With these two packages everything is configured by self discovery no need to mess with registration of modules or declaring dependencies it is automagical. (you can do it manually if needed)

Side Note

If you need to support PUT or DELETE you will need to add some handlers into your web.config to enable these verbs.

   1: <handlers>
   2:   <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
   3:   <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
   4:   <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
   5:   <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" 
   6:     modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" 
   7:     preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
   8:   <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" 
   9:     modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" 
  10:     preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
  11:   <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" 
  12:     resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" />
  13:   <add name="Nancy" verb="*" type="Nancy.Hosting.Aspnet.NancyHttpRequestHandler" path="/api" />
  14: </handlers>

The Modules

Balances

This module is our primary resource in the project as it helps us retrieve our listing of checking accounts.  You wire up the appropriate routes in Nancy by supplying the url in the appropriate http verb of get, put, and delete. (patch is also available but not used).  After the setup of your routes you supply a simple lambda expression on what should run when the route is matched.  You are supplied with a dynamic parameters object which you can simply pull of the values (if any) from your route.  If your route is in the form of /dogs/{id} you can access the id token via the dynamic object in the fashion of parameters.id.  Simple right?  In your expression you do the appropriate logic and return the appropriate response either a simple 404 or a complex json object. 

   1: public class BalancesModule : NancyModule
   2:     {
   3:         private readonly ICheckingAccountsData _accountsData;
   4:  
   5:         public BalancesModule(ICheckingAccountsData accountsData) : base("/api")
   6:         {
   7:             this.RequiresAuthentication();
   8:             _accountsData = accountsData;
   9:             Get["/CheckingAccounts"] = parameters => { return Response.AsJson(accountsData.GetAll()); };
  10:  
  11:             Get["/CheckingAccounts/{id}"] = parameters =>
  12:                 {
  13:                     int id = int.Parse(parameters.id);
  14:                     CheckingAccount checkingAccount = accountsData.Get(id);
  15:  
  16:                     if (checkingAccount == null)
  17:                     {
  18:                         return new Response
  19:                             {
  20:                                 StatusCode = HttpStatusCode.NotFound
  21:                             };
  22:                     }
  23:                     return Response.AsJson(checkingAccount);
  24:                 };
  25:             Put["/CheckingAccounts/{id}"] = parameters =>
  26:                 {
  27:                     var account = this.Bind<CheckingAccount>();
  28:  
  29:                     _accountsData.Update(account);
  30:  
  31:                     return new Response
  32:                         {
  33:                             StatusCode = HttpStatusCode.OK
  34:                         };
  35:                 };
  36:  
  37:             Delete["/CheckingAccounts/{id}"] = parameters =>
  38:                 {
  39:                     int id = int.Parse(parameters.id);
  40:  
  41:                     _accountsData.Delete(id);
  42:  
  43:                     return new Response
  44:                         {
  45:                             StatusCode = HttpStatusCode.OK
  46:                         };
  47:                 };
  48:  
  49:             Post["/CheckingAccounts"] = parameters =>
  50:                 {
  51:                     var account = this.Bind<CheckingAccount>();
  52:  
  53:                     _accountsData.Add(account);
  54:  
  55:                     return new Response
  56:                         {
  57:                             StatusCode = HttpStatusCode.OK
  58:                         };
  59:                 };
  60:         }
  61:     }
Transfers

Transfers is another Module that only has one Post configuration one thing to notice is the use of this.Bind<>.  This will take the data posted to your url and bind the data to an appropriate object supplied in the generic parameter.  You will also notice that if an error occurs I use the http status of BadRequest so any consumer of the Api can handle the error correctly and effectively. 

   1: public class TransfersModule : NancyModule
   2: {
   3:     private readonly ICheckingAccountsData _accountsData;
   4:  
   5:     public TransfersModule(ICheckingAccountsData accountsData) : base("/api")
   6:     {
   7:         this.RequiresAuthentication();
   8:         _accountsData = accountsData;
   9:  
  10:         Post["/PerformTransfer"] = parameters =>
  11:             {
  12:                 var account = this.Bind<TransferRequest>();
  13:  
  14:                 CheckingAccount source = _accountsData.Get(account.SourceId);
  15:                 CheckingAccount destination = _accountsData.Get(account.DestinationId);
  16:  
  17:                 if (source.Balance < account.Amount)
  18:                 {
  19:                     return Response.AsJson(new ApiError
  20:                         {
  21:                             ErrorMessage = "Not enough money from source share.",
  22:                             ErrorCode = 1234,
  23:                             ErrorName = "not_enough_money"
  24:                         }, HttpStatusCode.BadRequest);
  25:                 }
  26:  
  27:                 // Logic to perform the transfer
  28:  
  29:  
  30:                 return new Response
  31:                     {
  32:                         StatusCode = HttpStatusCode.Created
  33:                     };
  34:             };
  35:     }
  36: }
Authentication

This module handles all the api endpoints for some very basic authentication and setting of a cookie.  If the LogOff url is hit the cookie is expired, super simple and super terrible at the same time.

   1: public class AuthenticateModule : NancyModule
   2: {
   3:     public AuthenticateModule() : base("/api")
   4:     {
   5:         Post["/AuthenticateUser"] = parameters =>
   6:             {
   7:                 var bind = this.Bind<LoginRequest>();
   8:  
   9:                 //do something with request.Username and request.Password.
  10:  
  11:                 var response = new Response
  12:                     {
  13:                         StatusCode = HttpStatusCode.OK
  14:                     };
  15:  
  16:                 response.AddCookie("valid", bind.Username, DateTime.Now.AddMinutes(5));
  17:                 return response;
  18:             };
  19:         Get["/LogOff"] = parameters =>
  20:             {
  21:                 var response = new Response
  22:                     {
  23:                         StatusCode = HttpStatusCode.OK
  24:                     };
  25:  
  26:                 //clear the cookie
  27:                 response.AddCookie("valid", null, DateTime.Now.AddYears(-5));
  28:                 return response;
  29:             };
  30:     }
  31: }
Authentication Bootstrapper

If you noticed in the prior modules there was a call to this.RequiresAuthentication().  This causes nancy to produce an Unauthroized http status returned to the browser since the user is attempting to access a protected resource.  This is also where our cookie comes into play as well that we set in the authentication module.  As you see in BeforeRequest we attempt to retrieve the cookie from the request and if one is found populate the current user on the context.  The other area to note is the related AfterRequest portion which detects if the status code is 401 and if it is, build up the appropriate json error response so the client can handle the error effectively.

   1: public class AuthenticationBootstrapper : DefaultNancyBootstrapper
   2:    {
   3:        protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
   4:        {
   5:            base.ApplicationStartup(container, pipelines);
   6:  
   7:            pipelines.BeforeRequest += (ctx) =>
   8:                {
   9:                    string cookie;
  10:  
  11:                    ctx.Request.Cookies.TryGetValue("valid", out cookie);
  12:  
  13:                    //do something to populate the user identity
  14:                    if (!string.IsNullOrEmpty(cookie))
  15:                    {
  16:                        ctx.CurrentUser = new DemoUserIdentity
  17:                            {
  18:                                UserName = cookie,
  19:                                Claims = BuildClaims(cookie)
  20:                            };
  21:                    }
  22:  
  23:                    return null;
  24:                };
  25:  
  26:            pipelines.AfterRequest += (ctx) =>
  27:                {
  28:                    // If status code comes back as Unauthorized then
  29:                    // forward the user to the login page
  30:                    if (ctx.Response.StatusCode == HttpStatusCode.Unauthorized)
  31:                    {
  32:                        var j = new JsonResponse<ApiError>(new ApiError {ErrorCode = 401, ErrorMessage = "user is not authenticated", ErrorName = "unauthorized"}, new DefaultJsonSerializer())
  33:                            {
  34:                                StatusCode = HttpStatusCode.Unauthorized
  35:                            };
  36:  
  37:                        ctx.Response = j;
  38:                    }
  39:                };
  40:        }
  41:  
  42:        private static IEnumerable<string> BuildClaims(string userName)
  43:        {
  44:            var claims = new List<string>();
  45:  
  46:            // Only bob can have access to SuperSecure
  47:            if (String.Equals(userName, "bob", StringComparison.InvariantCultureIgnoreCase))
  48:            {
  49:                claims.Add("SuperSecure");
  50:            }
  51:  
  52:            return claims;
  53:        }
  54:    }
 
After using WebAPI for a few projects Nancy is really growing on me. The fact the route matching and the appropriate verb is right in your class it is very easy to setup complex routes. You do not need to mess with attributes or hunting down how your route with match your controller via the Route config.

 

TypeScript

Since we are dealing with a JavaScript heavy application utilizing AngularJS it sure would be nice to have some good intellisense and some insight on the objects and methods.  Because of this need we can use TypeScript  which allows our typescript files to be compiled down to plain javascript.  Why would we want to do this?  Well this simple example can explain things more than words.

   1: var thisIsTrueOrFalse;
   2: thisIsTrueOrFalse = true;
   3: console.log(thisIsTrueOrFalse);
   4: thisIsTrueOrFalse = 10;
   5: console.log(thisIsTrueOrFalse);​
The snippet above is perfectly valid JavaScript but doing something like the above could cause strange behavior in your JavaScript application.  Now if you do the same thing in TypeScript it will look like the following.
   1: var thisIsTrueOrFalse : bool;
   2: thisIsTrueOrFalse = true;
   3: console.log(thisIsTrueOrFalse);
   4: thisIsTrueOrFalse = 10;
   5: console.log(thisIsTrueOrFalse);

All we did was simply add a type declaration to the variable thisIsTrueOrFalse which tells the TypeScript compiler that this variable is of type bool and should only accept either true or false values.  When we attempt to compile this script TypeScript throws an error that you can not convert number to bool.  This gave us the error checking in JavaScript that helps identify possible bugs early.  One of the amazing things about TypeScript is that JavaScript is TypeScript so you can use TypeScript files and annotate the types were appropriate.  This allows you to gain this power with little bit of work.  To take some of the work out of creating the type script definition files for various js libraries there is a github repository for this particular need. https://github.com/borisyankov/DefinitelyTyped.  I will talk more about the TypeScript syntax when we get to our next section AngularJs

AngularJs

When creating an AngularJs application you have roughly six parts.

directives.ts/directives.js

This creates any directives that are registered for your application.  An example of a directive would to take custom html elements and output appropriate markup.

filters.ts/filters.js

This creates any filters that are registered for your application.  An example of a filter is the currency filter which takes input and displays it in a nice currency format 1 would become $1.00.

services.ts/services.js

Services are simply services that you use in your application that contain business logic so your controllers do not become bloated.  In our example we create one service that allows us to interact with our rest API.  Instead of using the low level $http object we use the $resource object to create a new $resource class.  Using this class is sort of automagical since all you need to supply is the url of the endpoint, any parameters located in the url and that is about it.  In the url ‘api/checkingaccounts/:id’ the parameter in the url is the id.  Also you will notice that we specify the queryDescriptor for our resource this allows you to setup defaults for a particular method call.  For the query method we wish to supply a blank id when query is called and that the method of the http verb is a get.

TypeScript

To give us a bit of typing and compile time check we annotated the $resrouce variable with ng.resoruces.IResourceService and also the queryDescriptor to the type of ng.resource.IActionDescriptor.  This gives us some confidence that our objects are correct and declared with the correct types/structure.

   1: /// <reference path="../lib/angular/angular-1.0.d.ts" />
   2: /// <reference path="../lib/angular/angular-resource-1.0.d.ts" />
   3: /// <reference path="models.ts" />
   4:  
   5: angular.module('myApp.services', ['ngResource'])
   6:     .factory('CheckingAccount', function ($resource: ng.resource.IResourceService) {
   7:  
   8:         var queryDescriptor: ng.resource.IActionDescriptor;
   9:  
  10:         queryDescriptor = {
  11:             method: 'GET',
  12:             params: {
  13:                 id: '',
  14:  
  15:             },
  16:             isArray: true
  17:         };
  18:  
  19:         var share = <checkingAccountResource> $resource('api/checkingaccounts/:id', {}, {
  20:             query : queryDescriptor
  21:  
  22:         });
  23:         return share;
  24:     });
  25:  

controllers.ts/controllers.js

Since we have a few controllers in our application we will only show one.  First thing our controller depends on a $scope and a CheckingAccount.  The scope is where all our data and events are glued to.  In our details controller we have a CheckingAccount and one function called changeDescription.  You can see that we use the service created above CheckingAccount and issue the .get() method to retrieve one resource from the rest api.  Also in the function we call $update which then issues a put request back to our api with the updated description.  (Do not forget to supply the id in {} object otherwise you will always get a method not allowed from the put request)

TypeScript

Since we want our $scope to be properly typed we need to create a new interface that extends the type ng.IScope.  This allows us to add additional properties and functions on our scope to yet again get that compile time checking that everything should be ok.  We also type the CheckingAccount dependency to a ng.resoruce.IResoruceClient so we get the nice intellisense when issuing the query and $update method.  If you notice I had to case the result of .get() to <any> so we can properly assign it to our $scope.  This was needed since the return type of get() did not match up with the type on our $scope.

   1: interface ICheckDetailsControllerScope extends ng.IScope {
   2:     CheckingAccount: CheckingAccount;
   3:     changeDescription(e: Event);
   4: }
   5:  
   6: function CheckDetailsController($scope: ICheckDetailsControllerScope, CheckingAccount: ng.resource.IResourceClass, $routeParams: ICheckDetailsRouteParams) {
   7:  
   8:     $scope.CheckingAccount = <any>CheckingAccount.get({ id: $routeParams.id });
   9:     
  10:     $scope.changeDescription = function (e) {
  11:         $scope.CheckingAccount.$update({
  12:             id: $scope.CheckingAccount.Id
  13:         });
  14:         e.preventDefault();
  15:     };
  16: }

app.ts/app.js

This is the main module for your application which depends on filters, services, and directives.  This is also where you can configure your application for instance a routeProvider and a httpProvider resposneInterceptor.  The routeProvider says what view should be displayed when the hash tag changes in the url.  The responseInterceptor allows for a particular method to execute whenever any http action occurs via the $http dependency.  In this case we use it to intercept  when a 401 unauthorized status code is returned.

TypeScript

the $routeProvider is typed to the interface of ng.IRouteProviderProvider.  Doing this allows us to have nice inteillisense and type checking when we issue the $routeProvider.when() method call.

   1: /// <reference path="../lib/angular/angular-1.0.d.ts" />
   2: /// <reference path="../lib/angular/angular-resource-1.0.d.ts" />
   3: /// <reference path="../Scripts/bootstrap-2.1.d.ts" />
   4: /// <reference path="controllers.ts" />
   5:  
   6:  
   7: // Declare app level module which depends on filters, and services
   8: angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives']).
   9:  
  10:   config(['$routeProvider', function($routeProvider : ng.IRouteProviderProvider) {
  11:     $routeProvider.when('/balances', {templateUrl: 'partials/balances.html', controller: BalancesController});
  12:     $routeProvider.when('/transfers', {templateUrl: 'partials/transfers.html', controller: TransfersController});
  13:     $routeProvider.when('/balances/detail/:id', {templateUrl: 'partials/details.html', controller: CheckDetailsController});
  14:     $routeProvider.otherwise({redirectTo: '/balances'});
  15:   }]).config(function ($httpProvider) {
  16:       function exampleInterceptor($q, $log) {
  17:           function success(response) {
  18:               return response;
  19:           }
  20:           function error(response) {
  21:               
  22:               if (response.status === 401) {
  23:                   var deferred = $q.defer();
  24:                   $('#myModal').modal();
  25:                   return deferred.promise;
  26:               }
  27:  
  28:               return $q.reject(response);
  29:           }
  30:  
  31:           return function (promise:ng.IPromise) {
  32:               return promise.then(success, error);
  33:           }
  34:       };
  35:         $httpProvider.responseInterceptors.push(exampleInterceptor);
  36:    });;

The Views

If you have an MVC background the views are jus that views/templates for the data that is attached to your controller’s scope.  AngularJs relies on custom attributes to iterate over collections, bind click events, bind to an item on the scope, and to write out values to the html template. 

Notes of interest

  • when using ng-click to bind to a function make sure to call it like a function chanceDescription(), binding to changeDescription will do nothing and you will smack your head against the table. 
  • If you need access to the event when the click happens to prevent the default action you can access via the $event variable.  Angular does not prevent the default action
  • ng-model binds the text from the scope property to the input box.  When the input changes so does the bound scope property
  • If you wish to display text simply use {{}} (similar to <%=%> in asp.net).  If you wish to apply a filter to the item use the |
  • ng-repeat simply iterates of an array and repeats that section of the template for each item in the array. (data-ng-repeat and ng-repeat are the same depending if you want to run your views through a validator so no errors are thrown)
   1: <table>
   2:     <tr>
   3:         <td>
   4:             <label>Description</label>
   5:         </td>
   6:         <td>
   7:             <input type="text" ng-model="CheckingAccount.Description" />
   8:             <a href="#" ng-click="changeDescription($event)" class="btn btn-mini">Change Description</a>
   9:         </td>
  10:     </tr>
  11:     <tr>
  12:         <td>
  13:             <label>Balance</label>
  14:         </td>
  15:         <td>{{CheckingAccount.Balance | currency}}
  16:         </td>
  17:     </tr>
  18: </table>
  19:  
  20: <table class="table table-hover">
  21:     <thead>
  22:         <tr>
  23:             <th>Date
  24:             </th>
  25:             <th>Comment
  26:             </th>
  27:             <th>Amount
  28:             </th>
  29:         </tr>
  30:     </thead>
  31:     <tbody data-ng-repeat="transaction in CheckingAccount.RecentTransactions">
  32:         <tr>
  33:             <td>{{transaction.OccuredOn | date:'MM/dd/yyyy @ h:mma'}}
  34:             </td>
  35:             <td>
  36:              {{transaction.Comment}}
  37:             </td>
  38:             <td>{{transaction.Amount | currency}}
  39:             </td>
  40:         </tr>
  41:     </tbody>
  42:     <tfoot>
  43:     </tfoot>
  44: </table>

Well that is the post lots of information that probably will be hard to digest on first read.  I will say I do like the way this feels dealing with static html files, a few javascript libraries and a restful api feels like the way to go for future development.  It gives you so much power to create a responsive web application that can be insanely fast since we are no longer relying on the server to render pages and produce results everything is done on the client.

Boot Strap

We used bootstrap briefly to apply some nice styles to the demo application and the login modal.  Nothing particular to note here yet but more will follow to allow for responsive layouts.

You can view the code out on github if anyone is interested in seeing the final project.

  1. rowbot reblogged this from markcoleman
  2. markcoleman posted this
Blog comments powered by Disqus