AngularJS and Cookies (Continued)

As my esteemed colleague Robbie Pell has pointed out in a recent post, AngularJS provides an easy mechanism for setting and retrieving cookies but this mechanism comes with several notable limitations.  Robbie’s post outlines the fact that AngularJS provides no way to set an expiration date on a cookie which causes the cookie to expire when the browser session ends.

I have uncovered an additional limitation.  It appears that AngularJS also does not provide a way to specify which subdirectories of a domain the cookie applies to.  This causes the cookie to, by default, only be applied to the subdirectory in which it is set.  For example, if I set a cookie from a page at http://dev1.derby.reurgency.net/derby/racetracker/index.html using AngularJS the cookie will not be visible at http://dev1.derby.reurgency.net/derby/api/ causing all API calls which require that specific cookie to fail.

The solution to this is to, once again, set the cookies manually outside of AngularJS.  I have built on Robbie’s example below:

var expirationDate = new Date();
var myValue;
var path = "/";

expirationDate.setDate(expirationDate.getDate() + 30);
document.cookie = "MyToken" + "=" + myValue + "; path=" + path + "; expires=" + expirationDate.toUTCString();

In the example above, the cookie will be applied to all subdirectories within the current domain.  Alternatively, you can specify a subdirectory to apply the cookie to by altering the “path” variable.

Custom Exceptions and Angular Error Handling

This blog covers a straight forward example of handling errors via custom exceptions from the server and displaying the exception message on the client using an angular service.

Server:

Step 1:

Create Custom Exceptions

namespace Business.Exceptions
{
  public class ProblematicWebServiceException : Exception
  {
   public ProblematicWebServiceException()
   : base()
   {

   }
   public ProblematicWebServiceException(string message)
   : base(message)
   {

   }
   public ProblematicWebServiceException(string message, Exception innerException)
   : base(message, innerException)
   {

   }
  }
}

Step 2:

Wrap code that has the potential to fail in try/catch blocks and throw custom exception with a helpful error message. This is probably going to be in the Business layer depending on the architecture of the application.

try
{
   problematicService.GetSomeStuff();
}
catch (Exception)
{
   throw new Business.Exceptions.ProblematicWebServiceException("The Problematic Web Service is temporarily down.");
}

 

Step 3:

Catch exceptions as they bubble up to the application’s service layer and return appropriate Http Status Code(Service Unavailable 503 in this case):

catch (Business.Exceptions.ProblematicWebServiceException ex)
            {
                HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.ServiceUnavailable)
                {
                    Content = new StringContent(ex.Message)
                };
                throw new HttpResponseException(response);
            }

Client:

Step 4:

Create Shared Service to handle errors:


angular.module('Shared.Services.SharedDataService', [])
    .service('sharedData', ['$rootScope', '$cookies', '$http', '$location',  function ($rootScope, $cookies, $http, $location) {
   var handleFault = function (context,error) {

   if (error.status === 503 && error.data) {
                errorMessage = error.data;
            }

   return {message:errorMessage,details:errorDetails}
        };
}

Step 5:

Handle error using $resource’s error callback to display the error message to the user:

MyResource.get({ 'id': customerId },
        function (response) {
            $location.path('/customerDetails/' + customerId);
        },
        function (error) {
            $scope.isError = true;
            $scope.errorMessage = sharedData.handleFault("gettingProblematicServiceCustomer", error).message;
        });           

 

Validating Matching Text using an Angular Directive

This is a nice angular directive I found which can be used with AngularJS validation to make sure the text in two inputs match. The most common scenario for this would be for a form where a user must create a new password.

The directive:

.directive('compareText', [function () {
        return {
            require: 'ngModel',
            link: function (scope, elem, attrs, ctrl) {
                var firstTextBox = '#' + attrs.compareText;
                elem.add(firstTextBox).on('keyup', function () {
                    scope.$apply(function () {
                        var v = elem.val() === $(firstTextBox).val();
                        ctrl.$setValidity('textMatch', v);
                    });
                });
            }
        }
    }])

Implementation:

<form name="myForm">
   <input id="pw1" type="password" placeholder="* Password" name="password1" ng-model="model.Password1">
   <input id="pw2" type="password" placeholder="* Repeat Password" name="password2" compare-Text="pw1" ng-model="model.Password2">
   <div ng-show="myForm.password2.$error.textMatch">Must match Password</div>
</form>

 

This is a very simple and reusable directive that makes sure that the text in the inputs match using some jQuery.

Client and Server Validation using Regular Expressions

Here are some of the regular expressions being used in the Youngevity project. These are important to prevent SQL injection attacks. These validation expressions are being used on both the Code First models and the client side Angular validation.

These are a starting point for us to start tweaking until as a team we get these expressions to a point where we are comfortable. Then it would be nice to have these as our standard regular expressions that we use on all projects thus saving us time going forward.

These expressions are using whitelisting which is just simply specifying which characters are allowed as opposed to blacklisting which specifies which characters aren’t allowed. Whitelisting is considered a best practice when writing regular expressions.

Here is an example of how to use a regular expression on a C# model:

[RegularExpression(@"^[0-9a-zA-Z /.@()!#$%^&*?+_-]+$", ErrorMessage = "Please remove invalid characters")]
        public virtual string LastName { get; set; }

Implentation using AngularJS:

<input type="text" ng-model="customerRequest.LastName" name="LastName" placeholder="* Last Name" ng-pattern="/^[0-9a-zA-Z /.@()!#$%^&*?+_-]+$/">
<div ng-show="requestForm.LastName.$error.pattern">Please remove invalid characters</div>

Basic regular expression which can be used for many different fields:

^[0-9a-zA-Z /.@()!#$%^&*?+_-]+$

Regular expression for telephone #s(this is overkill if you are just going to stick the telephone # in the database but when you need to make sure that telephone #s contain a valid 10 or 11 digit North America # it is good to use)

^D?1?D?(d{3})D?D?(d{3})D?(d{4})$

Validating string is a Guid:

^{?[dA-Fa-f]{8}-[dA-Fa-f]{4}-[dA-Fa-f]{4}-[dA-Fa-f]{4}-[dA-Fa-f]{12}}?$

This once again is just a starting point for us to have a library of regular expressions we can quickly grab when needed. For most fields the basic regular expression shown above will work in most cases. For the basic regular expression used above I intentionally did not use shortcuts that allow you to not spell out each special character which is being allowed. I like the fact that you can quickly see what the allowed special characters are without having to lookup the shortcut via google.

AngularJS and Cookies

AngularJS has built in support for cookies. A cookie may be added by simply injecting $cookies into your service or controller and creating a cookie by:

$cookies.MyToken = value;

The value can then also be read from the cookies easily by:

var myToken = $cookies.MyToken;

This obviously is a nice feature which abstracts away much of the javascript usually written for creating and reading cookies.

The problem with the current way angular cookies are created is that all cookies by default are set to expire at the end of the browsing session and as of this post there isn’t a way to change that. The problem has been recognized and the creators of angular are working on allowing expiration to be set on cookies in a future update. For the time being we have stopped using $cookies and are using some simple javascript to create cookies but then use angular to read and delete cookies.

Here is some javascript on how to create a new cookie with an expiration of 30 days:

var expirationDate = new Date();
var myValue;

expirationDate.setDate(expirationDate.getDate() + 30);
document.cookie = "MyToken" + "=" + myValue + "; expires=" + expirationDate.toUTCString();

In order to delete an angular cookie simply use:

delete $cookies["MyToken"];