[Coding] Web API, AngularJS, and Authentication – Too Many Resources (Part 2)

Posted by Khatharsis on August 16, 2014

This post is the continuation of my proof-of-concept project with AngularJS, Web API, and FOrms Authentication. The previous post covered the back end (database/entity model and Web API). This post will cover the front end (AngularJS).


HTML
My index.html contained two divs. The first div displayed the result of calling GetUsers() with a button to trigger the call. Results would be displayed in a list using a ng-repeat. The second div handled the login form and logout link. I learned a few things about AngularJS here.

The first is in a repeater, it does not handle duplicate entries very well. I don’t know why, but attempting to do a ng-repeat="user in users" will result in an error message. The workaround is simply appending ” by $index” to the directive value: ng-repeat="user in users track by $index". Another tip, “track by” should go at the very end. Meaning, if any filters are used, they go in the middle, e.g., ng-repeat="user in users | orderBy: 'name' track by $index".

The other interesting problem I ran across was figuring out how to handle when a 401 error (forbidden) is returned instead of a list of users. Introducing logic to the view seemed like a bad idea (i.e., if users is not null, then display users, else display the error message), so my JS code is a little wordy.

Strangely, I did have some display logic when it came to the second div. If the user IsAuthenticated(), then display the logout link. Else, display the login form. I did this through ng-show:


<div data-ng-show="!authenticated">
	Username: <input type="text" data-ng-model="authentication.username" required />
	<br />
	Password: <input type="password" data-ng-model="authentication.password" required />
	<br />
	<button type="submit">Login</button>
</div>
<br /><br />
{{text}}
<br /><br />
<div data-ng-show="authenticated">
	<a href="" data-ng-click="logOutClick()">Sign Out</a>
</div>

My grasp on what is acceptable and good practice is still very tenuous here. This bit of logic was also a nice-to-have (e.g., useful UX cue), but remains a topic to research further.

JavaScript
Now, we come to the glue. More AngularJS lessons were learned here, including a rather interesting way of structuring my API factory into one object instead of multiple objects corresponding to the multiple controllers. It looks something like the following:


APP.factory('apiFactory', ['$http', function($http) {
	var apiFactory = {};
	apiFactory.user = { urlBase: 'api/user' };
	apiFactory.auth = { urlBase: 'api/auth' };
	
	apiFactory.user.getUsers = function() { return $http.get(this.urlBase); };
	
	apiFactory.auth.signIn = function(username, password) {
		var url = this.urlBase + '/SignIn/';
		return $http.post(url);
	};
	
	// similar methods for signOut and isAuthenticated
	
	return apiFactory;
}]);

This looks a little cleaner than having multiple factories for a single API, but that is also an option if there is more configuration required.

I found out that when handling the promise that gets returned, if an error is thrown, it won’t register in promise.error(). Instead, you have to set up an interceptor factory and register it with an interceptors array. It sounds daunting, but it’s not so bad:

// Interceptor to catch 401 error
APP.factory('httpInterceptor', ['$q', function httpInterceptor($q) {
    var httpInterceptor = {};

    httpInterceptor.success = function (response) {
        return response;
    };

    httpInterceptor.error = function (response) {
        if (response.status == 401) {
            return $q.reject(response);
        }
    };

    return httpInterceptor;
}]);

// Require an interceptor to catch 401 response
APP.config(function ($httpProvider) {
    $httpProvider.interceptors.push('httpInterceptor');
})


$q is a promise/deferred implementation and is provided by the AngularJS library (similar to $http for AJAX calls). Intercepting the 401 result properly sends the error condition to my controller, which then properly handles the result, e.g.,


APP.controller('userController', ['$scope', 'apiFactory', 
    function ($scope, apiFactory) {
    var api = apiFactory.user;
    $scope.users = [];
    $scope.error = '';

    $scope.generateClick = function () {
        api.getUsers()
            .success(function (users) {
                $scope.users = users;
                $scope.error = '';
            })
            .error(function (err) {
                $scope.users = [];
                $scope.error = err;
            });
    };
}]);

This is my user controller with a users array property and an error string property. Recall my earlier issue with logic in the view concerning getting users – if I am authenticated (success), I get a user list, if not, I get an error. The appropriate values are then set in success() and error(). I feel it’s a bit messy because it can quickly get out of hand, but for a small project it is okay.

And. That’s it! After doing all of this, I had a good set of existing code I could copy-paste from for a different AngularJS project, but I still had more to learn (routing and templates). Every time I think I have a handle on the basics, I find that I really don’t. I need more meaningful projects to practice AngularJS on.