Tag Archive for: angularjs

Find It! App – Success at SUN ‘n FUN

The Find It! mobile app for SUN ‘n FUN 2016 #SNF2016 was a big success with over 3000 downloads and great ratings and reviews. FindIt! received 4.7 stars in the Google Play Store and a perfect 5.0 stars in the Apple App Store…

AngularJS: Communicating between Scopes

One of the many features of the AngularJS JavaScript framework is scope.  Scope is a way to encapsulate data relevant to a particular piece of functionality and keep it separate from functionality not directly related to it.  While this separation is desirable, from it rises the need to communication and pass data between scopes.

From what I can tell, there are six ways to communicate between scopes in AngularJS:

  1. Global Variables
  2. Parent/Child direct communication
  3. Cookies
  4. Shared Properties Services
  5. Binding
  6. Events

Global Variables

Using global variables is a very simple way to pass data between scopes.  Use of global variables is generally perceived to be a bad idea, however, because these variables can easily, and accidentally, be overwritten.  Additionally, this practice leads to spaghetti code.

Parent/Child or Sibling Communication

Direct communication between parent and child scopes or sibling scopes is also a possibility but this is usually a bad idea as well.  For one, communication in this manner causes tight coupling.  Additionally, the mechanism for communication between scopes in this manner is clunky.  Child scopes are implemented as linked lists.  This means that each parent has a link to the first and last child scope, and those child scopes have a link to their next sibling and previous sibling.  While there could be certain situations where this kind of communication is desirable, generally there is a better way.

Cookies

Use of cookies works well but should generally be used for their intended purpose which is to persist data locally.  If cookies are abused, you run into the same problem as global variables.  Also, cookies only store string data so attempting to communicate any non-string data would have the overhead of serialization.

Shared Properties Services

Shared properties services are a good way to share and cache data between scopes.  These are objects that are injected into controllers and directives using mechanisms provided by AngularJS which are intended to contain data common to each.  While communication can be achieved in this manner using watchers on a specified shared property, generally this is most useful for caching purposes.

To define a shared properties serivce:


angular.module('Derby.services').service('sharedProperties', [function () {
    return {
        selectedDivisionId: 0,
        //...Define other default shared properties here
    };
}]);

To use a shared properties service, inject it into a controller or directive and use it as an object:


angular.module('RaceTrackerApp.heats', []).controller('HeatsController', ['$scope', 'sharedProperties',
    function ($scope, sharedProperties) {
        //You must add sharedProperties to $scope in order to watch it
        $scope.sharedProperties = sharedProperties;

        //Watch the selectedDivisionId for changes
        $scope.$watch('sharedProperties.selectedDivisionId', 
            //This function will be executed when selectedDivisionId changes 
            //from anywhere
            function(newValue,oldValue){
                //Do something
            }
        );

Binding

Binding in AngularJS is a built-in way to easily pass data between parent and child scopes through HTML.  To facilitate communication, you can add a watcher on a bound variable.  When this variable changes, the watcher will fire allowing you to take some additional action.  The downside to binding is that it is difficult to achieve outside of HTML and therefore if the two scopes in which you want to communicate are not connected via HTML then binding should probably not be used.

To use binding, define a variable in your parent scope as such:


angular.module('RaceTrackerApp.heats', []).controller('HeatsController', ['$scope',
    function ($scope) {
        $scope.heats = [];

And use the variable in HTML as such:


<div ng-repeat="heat in heats">

Events

The final method of communicating between scopes is using events.  There are two kinds of events in AngularJS:

  • Broadcast – Event bubbles downward to all decentant scopes
  • Emit – Events bubble upwards to all ancestor scopes

Besides the fact that each event type bubbles in a different direction, these both work the same.  Each can pass in additional payload data.  Also, each can be watched for using $on.

To dispatch an event:


//heatCardClicked is the event name and heatDriverYear is the payload
$scope.$emit('heatCardClicked', heatDriverYear);

$broadcast works in the same way but bubbles in a different direction. To watch for an event:


//Watches for the heatCardClicked event.  Expects a heatDriverYear payload
$scope.$on('heatCardClicked', 
    function(event, heatDriverYear){
        //Do stuff after the heat card is clicked
    }
);

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"];