Unit Testing a Service that Makes an API Call Using $httpbackend

Friday, August 22, 2014

Introduction

This is part of a series on Unit Testing in AngularJS. You can see my others posts:

Creating a Single Page Application will inevitably require making an API call to a service at some point, and AngularJs provides two mechanisms to do accomplish this in its framework. The one instance is using the ngResource module, and the other approach is using the $http service. Personally, I am sort of torn as to what approach to use but I am finding my self gravitating more towards using the $http service just because it is more customizable. I think ngResource shines if you have a predictable set of RESTFUL API calls and you just want to make calls and get responses back, but if you want so more control of things like exception management, or provide a chain of promises, I find the $http service to be more straight forward in setting up.

$httpBackend

Another nice thing the $http service is that is comes with the corresponding $httpBackend service which can be used for mocking calls so your unit test have no external dependencies. After all, that is whole point of a unit test is that it is self contained and not reliant on any outside factors.


The Service

So in this example I have a service that makes an API call to get the friends for a current user:

 

This service has a GET call and a POST call and I want to write a test that will test a mock of the POST call.


The Test Setup

So lets look at the unit test and talk about what is going in this code.

So the first thing that is done is that we need to define our service and also define the $httpBackend service that will be mocking our calls.

I am also defining a mocked response that will be returned one the service is called.

In the beforeEach section, the service and $httpBackend are injected into our environment before each test.

And then after each call we want to ensure that the calls were expected to be made were actually made and that there were no exceptions thrown on those calls.

The Unit Test

Here is the first line of code from the test

$httpBackend.expectPOST('/api/friends/refresh').respond(refreshResponse);

 

This tells the $httpBackend service to expect a POST call to be made to a service and that it will return the refreshResponse object that was defined at the top of the page.

The next lines of code are the actual execution of the service.

var deferredResponse =  friendsApiService.refreshUsers();

var users;

deferredResponse.then(function(response){

    users = response.data;

});

Since we want to preserve the asynchronous nature of the call while at the same time be able to test the response of the call we will then need to flush the response. This will explicitly flush any pending request so that when we can evaluate the response it returns within the unit test. If we did not do this, since our call is asynchronous, our test would finish before the call was actually completed.

Once the flush is complete, we now can evaluate our response to see of our test is working.

 

 

Unit Testing a Simple Controller in AngularJs

Tuesday, August 12, 2014

Introduction

This is part of a series of post related to how to set up and execute unit testing in AngularJs.

In this post, I will show an example of unit testing a simple controller.

In this case I have a started building a controller, and one of the first things I want to test is a collapsible panel inside my view. The way the panel works is when at first the user initially goes to view that has a list of items, at the top of the view there is a button that opens and closes a panel that contains a search text box so the user can filter the items on the page.


The Controller Code

So to start out, my new controller looks like this:

 

The $scope initially sets the isSearchCollapsed property to true, in which the view uses to indicate the panel is closed.

Here is the Jade view that shows or hides the panel.

 

Using the Angular UI directive, the panel slides open and closed based on the value of the isSearchCollapsed property. The value of that property is change every time the toggleSearchCollapsed is executed.


The Test Code

To test the controller code, I need to be able to replicate the controller and its scope in the test environment and Angular gives us a way to do that with two built in functions. Lets look at the code

First thing I need to do is to replicate the controller's scope so I can then attach it to the replicated controller. The $rootScope object has a function called $new which will do that for me. This function basically is used to create child scopes from the base scope.

The next thing I need to do is replicate the controller assigning our newly created scope to that controller. Angular uses the $controller service internally to instantiate controllers, and we can use it to instantiate our controller in our test environment.

Once that is set up we are ready to start testing, and in this case, I have three tests.

  • Starting from scratch my first test is there just to make sure I have successfully replicated the controller and it's scope.
  • The second test, makes sure the isSearchCollapsed property is initially set to false.
  • The third test, validates that when the toggleSearchCollapsed is triggered that the value of the isSearchCollapsed is changed. Since my second test passed and I know the initial value will be true, when I call the function and test the value again it should be false.

Unit Testing a Simple Service in AngularJs

Monday, July 14, 2014

Introduction

When testing in AngularJs, I have found that once you have everything setup correctly, unit testing with Jasmine is not all that complicated. However, when I first started testing in AngularJs, getting things setup right was a bit hit or miss for me. At the time, the documentation for testing wasn't all that definitive so it took me a little longer to grasp the concepts than probably should have been needed.

So in this post, I am going to show how I test a simple service that takes an array of directory paths as a string and parses it out into an array of arrays. Its a simple test for a service that has no dependencies and only one function.

I am basically building on my previous post where I showed how to setup a unit testing environment using NodeJs, GruntJs, KarmaJs, and Jasmine.


The Service

So in this example, I am taking an AngularJs factory that returns one function. This function should take an array of paths and convert them into an array of array of directories.

The whole point of encapsulating this code is that not only is it testable, but is also mockable, and it is reusable.


The Unit Test

So here is the test:

The way I setup a unit test for the module, is I divide the test module into three different sections.

  1. The first section I include any variables that will be used by all the tests. This includes things like the scope, mocks, services, controllers etc. On a side note, if my mock data becomes too big, I separate them out into a different file and place the file in a folder called mocks and then reference them from there. My Karma.conf.js file then will need a reference to this folder.
  2. The next section is the most critical section and that is the section that sets everything up before each test runs.
  3. The last section is the section that handles all the tests.

Let me focus on the second section as that for me has always been the trickiest to get right.

BeforeEach

Jasmine comes with two global variables (beforeEach and afterEach), and as the names describe, the beforeEach runs before each test, while the afterEach runs after each test.

Here I am only using the beforeEach to set up the tests.

Module

The module command is used to specify what AngularJs module you are going to test. You need not worry about any other dependent modules, at this point, the module routine is only specific to the module you currently want to test, even if you have other dependent modules.

Inject

The inject function wraps objects into an injectable function and is where I am initializing the service. In this case I am only referencing my service that I want to test since I don't have any other dependencies.

Notice the underscores in the parameters.These underscores are actually ignored by the injector but allow us differentiate between what is being injected and the module level variable that we want to use for all out tests so they can be assigned accordingly.

The Actual Tests

Finally, the last part of the file contains the actual tests themselves.

If I am using a TDD approach, the first thing I do is make sure the service exists. This is represented by the test, "should be defined as a service". Once this test passes, I am then ready to move on to my next test.

I then test to make sure the method I am actually testing in the service exists. This test is represented by the test, "should have a function called getReportList". Again, once this test passes, I can then move to me next tests.

Then my last two tests, test that the function is actually returning what I am expecting it to return.

Conclusion

Hope that helps. In later posts I will show some different unit tests, such as testing a controller, testing a service that has a $HttpBackend dependency, and testing a controller that has a service dependency. In those posts I hope to cover things like mocks, spies, and the $HttpBackend service as well. 

Angular Unit Testing - Getting setup with Jasmine, Karma, and Grunt

Tuesday, June 24, 2014

As a developer coming from an ASP.Net, C# background, I had come to appreciate the ability to be able to unit test my code. I find that I am not all that great a programmer, and often times the unit tests I write have brought to surface a bug that I would have introduced had I not had a unit test in place to discover the bug in the first place.

In the past, I had not given much thought to do JavaScript unit tests. I had just assumed that it wasn't really possible to do them without a lot of jumping through hoops. However, with AngularJs, unit testing isn't all that difficult. I have found that once I have gotten over the initial hurdle of getting everything setup, unit testing JavaScript code has become quite useful.

I find the feedback loop is much quicker with the unit tests I write, because I can see write away that the code is working or not without having to do much of anything.

So with being said, lets look and see how to setup the unit testing framework.

NodeJs

For my unit tests, I use Jasmine, Karma, Grunt which all sit on top of the NodeJs runtime, so for that you will first need to install NodeJS.

NodeJs comes with package installer called NPM. Similar to Gems in Ruby, or Nuget in Visual Studio, NPM will install all the packages needed to start unit testing.

Note, if you are starting a project from scratch, I would recommend installing Yoeman. Yoeman is a scaffolding package that will install a starter project with everything already configured.  For example, when I have a new AngularJs project, I use the Yoeman Angular Fullstack generator, and then go back in and finegle the generated code to my own preferences.

For this post I am assuming that the project and tests already exists. So I am going to only go though all the steps to to get up and running with unit testing on an existing project.

Also, I am running Windows 8.1, so if you are on a different OS like Linux or Apple OS, or even a different version of windows, these steps might differ slightly. I'm not really sure, but I had noticed that other posts were slightly different than what I had to do to get started.

Creating the package.json File

The first thing to do is create a package.json file. NPM uses this file to keep track of what you have installed and what versions of the packages you are using. The easiest way to create this file is to initialize NPM from the command line.

npm init

Doing this generates a file that will look like this.

 

You could also just manually create the file yourself. Either way, this file is required.

Installing the packages

You can install each package separately from the command line but an easier way to get everything installed is to get a copy of the packages.json file with all the packages you need and just type the following from the command line:

npm install

This command will take what ever you have in the package.json file and install it in your project if it isn't already installed.

 

My package.json file looks like this:

The packages under the "devDependencies" node tells NPM that these are packages are only for use in the development environment and are not to be copied to production.

You might also want to install Grunt, Karma and maybe even Yoeman globally so you don't have to keep installing it for every project. You can do that with the following commands.

npm install -g grunt-cli

npm install -g karma-cli

npm install -g yoeman

The "cli" suffix is the package that allows grunt and karma to called directly from the command line in Windows. The -g parameter tells NPM you want to install the package globally.

Initializing the karma.config.js File

Karma is a test runner developed by the AngularJs team. It's basically the engine that goes through all the unit test and runs them, and then provides the results of the executed unit tests. Once karma is installed, you will need a karma.config.js file to so karma knows things where the JavaScript files are located, what testing framework you want to use, etc.

You can generate a karma.config.js file by running the following command:

karma init

When you run this you will be asked a bunch of questions:

  • Which unit testing framework do you use?
  • Do you want to use Require.js?
  • Do you want to capture any browsers automatically?
  • What is the location of your source and test files?
  • Should any of the files included by the previous patterns be excluded?
  • Do you want karma to watch all the files and run the tests on change?

I typically accept all the defaults, and, if needed, go back and change the configuration later. My current file looks like this:

If you have separated your JavaScript framework files from your own custom JavaScript files, you will need to add what ever framework files you are using that are included in the tests. For example, I have included angular and angular-mocks for angular specific testing and I also have a reference to lodash.js.

Configuring Grunt

Gruntjs is a task runner that lets you run several tasks at once. For this demo, I am only running karma, but I could add other tasks like JsHint to check my JavaScript syntax, or Uglify to minimize files. Grunt needs a gruntfile.js file installed which tells grunt what tasks to run and how to run them.

My grunt file is pretty simple since I am only running one task. It looks like this:

   

Now I'm ready

Now that I have setup my Grunt file, and I have told Karma to watch my JavaScript files. Every time I make a change to JavaScript code and change it, Karma will run the test and provide me the results in the command window. I simply run "grunt" from the command line.

Running "karma:unit" (karma) task
[2014-06-24 11:14:00.421] [INFO] karma - Karma v0.12.16 server started at http://localhost:9876/
[2014-06-24 11:14:00.569] [INFO] launcher - Starting browser Chrome
[2014-06-24 11:14:04.015] [INFO] Chrome 35.0.1916 (Windows) - Connected on socket aX-CPdsznQq6Oi75cNhf with id 3608593
Chrome 35.0.1916 (Windows): Executed 19 of 19 SUCCESS (0.047 secs / 0.043 secs)


Conclusion

I think in future posts, I will discuss a little how I go about writing unit tests, but for now I'll leave you with this in the hope that at least gets you started in writing AngularJs unit tests.

If you have any questions or feedback, shoot me a comment I would glad to hear what you think?

 

JavaScript, Hoisting, Firefox, If/then Statements

Friday, March 21, 2014

Quick intro about JavaScript and Scoping

In most C and Java type languages a block (that is any curly brace) defines the scope of a variable or a function. However in JavaScript, scoping is at the function level. So when I define a variable inside a block (if/then statement for example) it can be used throughout the entire function it is in, including outside its defined block.

JavaScript will also take that function that is defined anywhere in a function and set its definition at the top of the function behind the scenes. This is called hoisting, and basically it allows you to call a function that is defined below the code you are calling it from.

 

So for example:

 

This works in all browsers because JavaScript hoisted the inner function to the top of the outer function behind the scenes.

If/Then statements and Firefox

The problem is in Firefox if you define a function inside an if/then statement the function no longer gets hoisted. This only happens in Firefox.

 

So in the following example, the following code works in IE and Chrome but is broken in Firefox.

 

In Firefox I get an error because it did not hoist the callHoistedFunction therefore it was not defined at the point it was being used.

ReferenceError: callHoistedFunction is not defined
var hello = callHoistedFunction();
 

So the moral of the story is never define a function inside a “if/then” or “try/catch” statement. As a matter of practice, you should probably just define all variables and functions at the top of the outer function as that is where it gets hoisted to anyways.

 

This is from Douglas Crockford’s book the “JavaScript, The Good Parts”. Pg 102.

 

In most languages, it is generally best to declare variables at the site of the first use. That turns out to be a bad practice in JavaScript, because it does not have block scope. It is better to declare all variables at the top of each function.

 

Here is his video on his book in case you were also interested.

http://www.youtube.com/watch?v=hQVTIJBZook

 

Hope this helps,

AngularJS - Using a Service to Communicate Between Two Different Controllers

Monday, January 6, 2014

Introduction

AngularJS, being an MVC type of framework allows you to have a controller that is responsible for marshaling data to and from a view on your web page.

Well, what if you have a multiple controllers on a page that are completely independent from one another, but in certain cases you want those controllers to be able to communicate back and forth. I say completely independent, because you could nest the controllers, making a parent-child relationship, and then you could use the $rootScope object to communicate with each other.

How $rootScope Works

Every AngularJS application has exactly one $rootScope. All other scopes inside the application are children of the $rootScope. So in the case of nested controllers, the child controller's scope object is a descendent of the parent scope which is ultimately a descendent of the application $rootScope. So, for example, if you try to evaluate {{message.text}} on a child scope. and If that property does not exist on the child scope, AngularJS will then evaluate the parent scope for that property, and then ultimately it will evaluate the $rootScope.

So, in the case of controllers that have a parent-child relationship, you can use the existing inheritance between the controller to facilitate the communication.

Using Factories and Services to Facilitate Communication Across Independent Controllers

In the case your controllers are independent from each other, then you will need a service that will link the two controllers. In my example, I am using a factory which essentially a service that returns an object.

My example is just a web page with two text boxes that chat with each other. Each text box is in a different controller, but each controller has the list of message from both text boxes.

Here is the HTML.

    <div ng-controller="Controller1">
      <h1>Controller 1</h1>
      <ul ng-repeat="msg1 in messages1 track by msg1.id">
        <li>{{msg1.text}}</li>
      </ul>
      <p>
        Controller 1 Message: <input type="text" ng-model="text">
        <button ng-click="postMessage()">Post</button>
      </p>
    </div>
    <p></p>
    <div ng-controller="Controller2">
      <h1>Controller 2</h1>
      <ul ng-repeat="msg2 in messages2 track by msg2.id">
        <li>{{msg2.text}}</li>
      </ul>
      <p>
        Controller 1 Message: <input type="text" ng-model="text">
        <button ng-click="postMessage()">Post</button>
      </p>
    </div>

Each ng-controller section above has a copy of the same messages that is sent out by the service that manages the message list.

So to link the two controllers above, I have added a Angular Factory that will be injected into each controller. Here is the code.

app.factory('messageService', function ($rootScope) {
    var messenger = {
        messages: [],
        identity: 0,
        addMessage: function (text, caller) {
 
            this.identity += 1;
            var id = this.identity,
                message = {
                    text: caller + text,
                    id: id
                };
 
            this.messages.push(message);
            $rootScope.$broadcast('messageAdded');
        }
    };
 
    return messenger;
});

In this service, I am injecting the $rootScope, which as available throughout the Angular application. I have a function called addMessage which, when called by a controller, adds a message to the collection of messages and then broadcasts that the message was added using the $broadcast function. Also, keep in mind that a factory in Angular is a singleton object, so there is only one occurrence of its state in the current Angular application, and any other object that access its state will get what ever values it hold at that current time.

Looking at the code for the controllers, you will see that each of the controllers injects the service and calls the addMessage function when a user enters a text message and clicks the Post button.

app.controller('Controller1', function ($scope, messageService) {
    $scope.messages1 = messageService.messages;
    $scope.post = {
        text: ''
    };
 
    $scope.postMessage = function () {
        console.log($scope.post);
        messageService.addMessage($scope.text, "controller 1");
    };
 
    $scope.$on('messageAdded', function () {
        $scope.messages1 = messageService.messages;
    });
});

Each controller also has $on listener which is listening for the messageAdded broadcast. When this broadcast is detected by the controller, it fires the function to update the message collection retrieving the updated message list from the service.

I am finding this approach useful when the application you are working on gets a bit more complex and you have a lot of controllers on a page responsible for different things. In certain cases you may want to update a portion of the page that is managed by a controller, when a user clicks on another part of page managed by a different controller.

Here is the Plunker example

http://plnkr.co/edit/UA28cXfF0I8r3M54DyYa?p=preview

 

Old School MVP for ASP.Net Web Froms

Saturday, November 23, 2013

Introduction

Sometimes you have to work with a legacy site because, let's face it, it works and it's just not worth the effort to rewrite the whole thing via the newer technologies.

But still, if you need to add a web page and you still want to be able to separate the presentation logic from the domain logic, the Model View Presenter Pattern is the way to go.

I am not going to go into much detail about the pattern itself, there is a great article about it up on MSDN, but I would like to show you a great implementation of it that reduces the amount of code you will need to write to get it going.

The Base View

The first thing to do, is to set up the views, which will be interfaces the web page will use to represent the data on the page.

public interface IView
{
     
}

Yeah, not much here, just an empty interface, but this interface will be inherited from all the other views, making them all recognizable.

The Base Presenter

The base presenter class gets the base view injected into it. By doing this, the presenter will have a way to map the model to the web page.

public abstract class Presenter<TView>
{
    public TView View { getset; }
 
    public abstract void OnViewInitialized();
 
    public abstract void OnViewLoaded();
 
}

The Base Page class all Web Pages Will Inherit From

Now that we have the presenter and the view, we can create a base web page class that will inherit from the System.Web.UI.Page class. Using this class as the base, the web page will automatically inject the presenter and the view onto the page (you can also do this for a UserControl base class).

public abstract class MvpBasePage<TPresenterTView> : Page where TPresenter : Presenter<TView>
    {
 
        protected Presenter<TView> Presenter { getset; }
 
        protected MvpBasePage()
        {
            if (!(this is TView))
                throw new InvalidOperationException("Must impliment the generic type TView");
            try
            {
                Presenter = ObjectFactory.GetInstance<TPresenter>();
                Presenter.View = (TView)((object)this);
            }
            catch
            {
                Trace.Write(ObjectFactory.WhatDoIHave());
                throw;
            }
 
            Debug.WriteLine(ObjectFactory.WhatDoIHave());
        }
    }

I am using StructureMap to register the presenter classes, but any Inversion of Controll container should work here as well.

The Default Page Presenter and View

Now that I have all my base classes ready I can then start using them to create web pages that will conform to the Model View Presenter pattern. So for my default page, I am going to impliment a small version of my trusty sample application, which is a project tracker. You can see an almost full version that uses AngularJs on GutHub, but in this case I am just going mock a client list, which is the first page.

So my client list view is just going to have two properties on it: a content title, and a list of clients (Note: you can also have functions and events on the view for more complicated scenarios).

public interface IClientsView : IView
{
    string ContentTitle { getset; }
    List<Client> Clients {  set; }
}

My presenter for the clients just calls a service that is mocking a list of clients.

public class ClientsPresenter : Presenter<IClientsView>
  {
      private readonly IClientsService _clientsService;
 
      public ClientsPresenter(IClientsService clientsService)
      {
          _clientsService = clientsService;
      }
 
      public override void OnViewInitialized()
      {
          View.ContentTitle = "Client List";
          List<Client> clients = _clientsService.GetAll();
          View.Clients = clients;
      }
 
      public override void OnViewLoaded()
      {
      }
  }

Look Ma! Hardly any code behind!

Now with all this in place my Code Behind file only has the responsibility of marshaling data to and from the view, making it very clean.

public partial class _Default : MvpBasePage<ClientsPresenterIClientsView>IClientsView
    {
        private List<Client> _clients;
 
        public string ContentTitle { getset; }
 
        public List<Client> Clients
        {
            set
            {
                clientsRepeater.DataSource = value;
                clientsRepeater.DataBind();
            }
        }
 
        protected void Page_Load(object senderEventArgs e)
        {
            Presenter.OnViewInitialized();
            if (Page.IsCallback == false)
                Presenter.OnViewLoaded();
        }
    }

And lastly, here is the actual web form

<asp:Content runat="server" ID="FeaturedContent" ContentPlaceHolderID="FeaturedContent">
    <section class="featured">
        <div class="content-wrapper">
            <hgroup class="title">
                <h1><%: ContentTitle %></h1>
            </hgroup>
            <section>
                <asp:Repeater runat="server" ID="clientsRepeater">
                   <HeaderTemplate>
                        <table>
                        <thead>
                            <tr>
                                <th>Client Name</th>
                                <th>Contact Name</th>
                            </tr>
                        </thead>
                        <tbody>
                            
                        
                   </HeaderTemplate>
                    <ItemTemplate>
                        <tr>
                            <td><%#Eval("ClientName"%></td>
                            <td><%#Eval("ContactName"%></td>
                        </tr>
                    </ItemTemplate>
                    <FooterTemplate>
                        </tbody>
                    </table>
                    </FooterTemplate>
                </asp:Repeater>
            </section>
        </div>
    </section>
</asp:Content>

That's pretty much it.

The great thing here is if you need to work in legacy applications, you can start this pattern at any point. I have added new pages with this pattern into legacy application that do not follow any pattern without much effort.

I put all the code up on GutHub, in case you would like to take a look.

 

Validation Gotchas to Watch For When Using Angular

Thursday, July 11, 2013

Introduction

Validating a form in Angular is rather straight forward and it is pretty unobtrusive when it comes to the amount of code you have to right, but I thought I would quickly share some gotchas that I experienced when validating a form.

<form name="taskForm" ng-submit="save()" class='form-horizontal' novalidate>

The Form Tag and Controller

First thing to note is that if you want your submit function to fire so you can validate the form in your controller, you need to add the novalidate attribute to your form tag. Otherwise, the browser's built in validating handler will kick and prevent the form submission from firing.

if ($scope.taskForm.$valid === falsereturn;

Notice the form name is automatically attached to scope so it can be validated.  Angular has some built evaluation function you can use to validate the form.

To keep the errors from showing until the user has submitted the form: I created an isSubmitted variable on the $scope object and setting it true when the user clicks the submit button.

$scope.isSubmitted = true;

The Input tag.

Another thing to note is that if you want to use the other custom attributes like email, min, max, etc, then you need to make sure your use the correct input type in your tag. If you don’t, those validation errors will not fire.

Here is a number example:

<div class="control-group" ng-class="{error: isSubmitted && (timesheetForm.rate.$error.required || timesheetForm.rate.$error.number || timesheetForm.rate.$error.min || timesheetForm.rate.$error.max)}">
                <label class="control-label" for="timesheet-rate">Actual Rate:</label>
                <div class="controls">
                    <input type="number" ng-model="timesheet.rate" class="input-mini" 
id="timesheet-rate" name="rate" required min="1" max="999"/>
                    <span class="help-inline" 
ng-show="isSubmitted && timesheetForm.rate.$error.required">Actual rate is required.</span>
                    <span class="help-inline" 
ng-show="isSubmitted && timesheetForm.rate.$error.number">Actual rate is not a valid number.</span>
                    <span class="help-inline" 
ng-show="isSubmitted && timesheetForm.rate.$error.min">Actual rate must be greater than 0.</span>
                    <span class="help-inline" 
ng-show="isSubmitted && timesheetForm.rate.$error.max">Actual rate must be less than 999.</span>
                </div>
            </div>

Here is an email example:

            <div class="control-group" ng-class="{error: isSubmitted && (clientForm.email.$error.required || clientForm.email.$error.email)}">
                <label class="control-label" for="email-address">Email Address:</label>
                <div class="controls">
                    <input type="email" ng-model="client.contactEmail" class="input-xlarge" id="email-address" name="email" required/>
                    <span class="help-inline" ng-show="isSubmitted  && clientForm.email.$error.required">Email is required.</span>
                    <span class="help-inline" ng-show="isSubmitted  && clientForm.email.$error.email">Not a valid email address.</span>
                </div>
            </div>

You Need a Name:

You need to include the name attribute in your input tag and the name of the tag cannot have a "-" in the name. Same thing here; the validation will work with the dash.

Styling Using ng-class

For styling errors in Twitter Bootstrap you have to provide an error class in the control-group div tag which is the parent of the input tag that is being validated. To do that with an input element that several validations you can evaluate several conditions at once.

Here I am using the ng-class directive to validate several different scenarios.

<div class="control-group" ng-class="{error: isSubmitted && (timesheetForm.hours.$error.required || timesheetForm.hours.$error.number || timesheetForm.hours.$error.min || timesheetForm.hours.$error.max)}">

Here is the entire form.

        <form name="timesheetForm" ng-submit="save()" class='form-horizontal' novalidate>
            
            <div class="control-group" ng-class="{error: isSubmitted && timesheetForm.timesheetName.$error.required}">
                <label class="control-label" for="timesheet-name">Timesheet Name:</label>
                <div class="controls">
                    <input type="text" ng-model="timesheet.timesheetName" 
class="input-xlarge" id="timesheet-name" name="timesheetName" ng-focus required/>
                    <span class="help-inline" 
ng-show="isSubmitted && timesheetForm.timesheetName.$error.required">Timesheet name is required.</span>
                </div>
            </div>
            <div class="control-group">
                <label class="control-label" for="timesheet-description">Description:</label>
                <div class="controls">
                    <textarea ng-model="timesheet.timesheetDescription" 
class="input-xlarge" id="timesheet-description"  cols="20" rows="10"></textarea>
                </div>
            </div>
            <div class="control-group" ng-class="{error: isSubmitted && timesheetForm.startDate.$error.required}">
                <label class="control-label" for="timesheet-start-date">Start Date:</label>
                <div class="controls">
                    <input type="text" jq-datepicker ng-model="$parent.timesheet.startDate" 
class="input-small" id="timesheet-start-date" name="startDate" required/>
                    <span class="help-inline" 
ng-show="isSubmitted && timesheetForm.startDate.$error.required">Start date is required.</span>
                </div>
            </div>
            <div class="control-group" ng-class="{error: isSubmitted && (timesheetForm.hours.$error.required || timesheetForm.hours.$error.number || timesheetForm.hours.$error.min || timesheetForm.hours.$error.max)}">
                <label class="control-label" for="timesheet-hours">Actual Hours:</label>
                <div class="controls">
                    <input type="number" ng-model="timesheet.actualHours" class="input-mini" id="timesheet-hours" name="hours" required min="1" max="999"/>
                    <span class="help-inline" ng-show="isSubmitted && timesheetForm.hours.$error.required">Actual hours are required.</span>
                    <span class="help-inline" ng-show="isSubmitted && timesheetForm.hours.$error.number">Actual hours is not a valid number.</span>
                    <span class="help-inline" ng-show="isSubmitted && timesheetForm.hours.$error.min">Actual hours must be greater than 0.</span>
                    <span class="help-inline" ng-show="isSubmitted && timesheetForm.hours.$error.max">Actual hours must be less than 999.</span>
                </div>
            </div>
            <div class="control-group" ng-class="{error: isSubmitted && (timesheetForm.rate.$error.required || timesheetForm.rate.$error.number || timesheetForm.rate.$error.min || timesheetForm.rate.$error.max)}">
                <label class="control-label" for="timesheet-rate">Actual Rate:</label>
                <div class="controls">
                    <input type="number" ng-model="timesheet.rate" class="input-mini" id="timesheet-rate" name="rate" required min="1" max="999"/>
                    <span class="help-inline" ng-show="isSubmitted && timesheetForm.rate.$error.required">Actual rate is required.</span>
                    <span class="help-inline" ng-show="isSubmitted && timesheetForm.rate.$error.number">Actual rate is not a valid number.</span>
                    <span class="help-inline" ng-show="isSubmitted && timesheetForm.rate.$error.min">Actual rate must be greater than 0.</span>
                    <span class="help-inline" ng-show="isSubmitted && timesheetForm.rate.$error.max">Actual rate must be less than 999.</span>
                </div>
            </div>
            <div class="control-group">
                <label class="control-label" for="timesheet-revision-number">Revision Number:</label>
                <div class="controls">
                    <input type="text" ng-model="timesheet.revisionNumber" class="input-small" id="timesheet-revision-number"/>
                </div>
            </div>
            <div class="control-group" ng-class="{error: isSubmitted && timesheetForm.status.$error.required}">
                <label class="control-label" for="timesheet-status">Status:</label>
                <select ng-model="timesheet.statusId" 
data-ng-options="t.statusId as t.statusName for t in statuses" id="timesheet-status" name="status" required></select>
                <span class="help-inline" ng-show="isSubmitted && timesheetForm.stats.$error.required">Status selection is required.</span>
            </div>
            <div class="control-group" ng-class="{error: isSubmitted && timesheetForm.user.$error.required}">
                <label class="control-label" for="timesheet-user">User:</label>
                <select ng-model="timesheet.userId" data-ng-options="m.userId as m.userName for m in members" id="timesheet-user" name="user" required></select>
                <span class="help-inline" ng-show="isSubmitted && timesheetForm.user.$error.required">Member selection is required.</span>
            </div>
            <div class="form-actions">
                <button class="btn btn-primary" >Save</button>
                <button class="btn" ng-click="cancel()" >Remove</button>
            </div>
        </form>

Hope that helps.

Multiple Column Sorting of a Table Using Angular

Thursday, June 20, 2013

This year I have been focusing a lot on Single Page Application (SPA) and the various tools that can be used to build these applications efficiently.
 
So far, I have checked out knockout.js, backbone.js and angular.js, but have been mainly focused on getting my head around angular.js. I have been hearing great things about ember.js too, so perhaps I'll tackle that next. For now, though, I am focusing on angular.js. 
 
So in this post, I thought I would share how to take advantage of angular's filtering functionality. Specifically: how to have a table with a sort button on each of the table columns. It's actually pretty easy.

Binding

One of the core features of angular.js is its ability to do "two-way binging". That is, once the page loads, Angular, binds any models on the its scope to the designated fields on the page. These fields can be a single record or a collection of records. This means properties I change on the scope will immediately change on the screen. Angular handles all that magic.

Filter

Angular has a filter function that allows you to do things like query your collection filtering the result on page. The filter function can also sort your collection instantaneously on the page. In my example of sorting, I can also change the sort the sort column dynamically and I can also reverse the sort dynamically by storing the sort values in the $scope. Note that the $scope is sort of like a ViewBag if you think of it in terms of ASP.Net ASP. It is holds properties that the page has access to and that you can change at anytime.

Template

Here is what my template looks like
<table class="table table-striped table-condensed">
        <thead>
            <tr>
                <th></th>
                <th>Client Name <button class="btn btn-mini btn-link" ng-click="sort('clientName')"><i  class="icon-sort"></i></button> </th>
                <th>Contact Name<button class="btn btn-mini btn-link" ng-click="sort('contactName')"><i  class="icon-sort"></i></button></th>
                <th>Email Address<button class="btn btn-mini btn-link" ng-click="sort('contactEmail')"><i  class="icon-sort"></i></button></th>
                <th></th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            <tr ng-repeat="client in clients | orderBy:orderProp:direction">
                <td><a ng-href="/#/clients/edit/{{client.clientId}}"><i class="icon-edit"></i></a></td>
                <td><a ng-href="/#/clients/view/{{client.clientId}}"> {{client.clientName}}</a></td>
                <td>{{client.contactName}}</td>
                <td>{{client.contactEmail}}</td>
                <td></td>
                <td><remove-button text="Remove Client" action="removeClient()"></remove-button></td>
            </tr>
        </tbody>
    </table>

I tell Angular to loop through the collection by using the ng-repeat command and then I can set the sorting options by using the filter command orderBy. I am then telling it what property in the scope to use for the sorting (orderProp) and which property will be used for ascending and descending direction (direction).

Also notice my heading column links. They have a ng-click command to call the function sort. The sort function takes a parameter for which column to sort and will either change the column sorting or the direction depending on what column was clicked.

Then in the controller class, I initialize those properties and create the sort function which checks to see if it was the same column that is set in the scope and if it is it simply changes the direction of the sort. If the column is different than what is in scope, then it resort based on the new column and resets the direction to ascending.

                    $scope.direction = true;
		    $scope.orderProp = "clientName";
		    $scope.sort = function(column) {
		        if ($scope.orderProp === column) {
		            $scope.direction = !$scope.direction;
		        } else {
		            $scope.orderProp = column;
		            $scope.direction = true;
		        }
		    };

Here is my Plunker Example.

 

My Daughter's Flute Evaluation

Saturday, May 4, 2013

My oldest daughter had her flute evaluation today. I can't recall doing anything that stressful when I was her age. Any how, as a proud pop, I couldn't resist and posted this on my website. She did really great.

They're many schools represented at this event, and as a matter of fact, my daughter's particular judge had traveled 5 hours to be at the competition. When we arrived at the particular school holding the competion, we settled in the cafeteria where kids from all over the region were practicing before their evaluation.  All I can say is, I am glad my daughter plays the flute and not trumpet like I did growing up, as trumpets can be quite loud when a bunch of them are together in one room plaing different music at the same time.