Angular async calls  in 8 iterations.

Objective: create a demo async call in Angular 1 with all the bells and whistles. All iterations in code here.

Iteration 1

JSONPlaceholder is a handy fake REST service. An Angular module is created with a dataController controller which requests a fake body and title from the JSONPlaceholder REST service via the $http service. Since the call is asynchronous initially only a promise (you will eventually get the request data, just be patient)  is returned. When the promise is fulfilled the data can be transferred to the $scope object.

 

var app = angular.module('app', []);
app.controller('dataController', ['$scope', '$http', dataController]);

function dataController($scope, $http) {
	$scope.body = 'waiting....';
	$http.get('https://jsonplaceholder.typicode.com/POSTS/1')
			.then(function (response) {
		$scope.body = response.data.body;
		$scope.title = response.data.title;
	});
}

Iteration 2

Iteration 2 introduces the concept of directives. A custom marker called “dummy-data” is added the a new div tag. This marker does not have a value like an attribute (e.g.  class=’menu’) and IDE’s will complain a lot about them.

app.directive('dummyData', dummyData);

function dummyData() {
	return {
		template: '{{title}} {{body}}'
	};
}

On compilation the marker dummy-data will trigger the dummyData directive which in this implementation simply injects an HTML template into the DOM. The parent controller holds the scope and the scope is therefore available to the directive.

Iteration 3

In iteration 3 caching of the async call is tackled.  It deploys the default $cacheFactory service and the scope response is cached with a put(0 method and retrieved with a get() method.

var app = angular.module('app', []);
app.controller('dataController', ['$scope', '$http', 'superCache',  
dataController]);
app.directive('dummyData', dummyData);
app.factory('superCache', ['$cacheFactory', superCache]);

function dataController($scope, $http,superCache) {
    $scope.body = 'waiting....';
    article = superCache.get('article');
    if (article) {
        $scope.body = article.body;
        $scope.title = article.title;
    } else {
        $http.get('https://jsonplaceholder.typicode.com/POSTS/1').then(function (response) {
            $scope.body = response.data.body;
            $scope.title = response.data.title;
            superCache.put('article', response.data);                       
        });
    }
}
          
function superCache($cacheFactory){
    return $cacheFactory('super-cache');
}

Unfortunately is is not obvious that de data are just stored in the local memory that is within the same request. For true caching the data should be stored in localstorage or sessionstorage.

Iteration 4

Better caching is provided with the external angular-cache submodule. This submodule is injected into the app and then the CacheFactory service is available for the controller.

var app = angular.module('app', ['angular-cache']);
app.controller('dataController', ['$scope', '$http', 'CacheFactory', 
 dataController]);
app.directive('dummyData', dummyData);

function dataController($scope, $http,CacheFactory) {
    
    CacheFactory('dataCache', {
        storageMode: 'localStorage',
        maxAge: 15 * 60 * 1000, 
        cacheFlushInterval: 60 * 60 * 1000, 
        deleteOnExpire: 'aggressive' 
    });
    
    $scope.body = 'waiting....';                
        $http.get('https://jsonplaceholder.typicode.com/POSTS/1',
        {cache: CacheFactory.get('dataCache')
    } ).then(function (response) {
            $scope.body = response.data.body;
            $scope.title = response.data.title;
            
        });
}

Things like the storage mode  and expiration time can be configured.

Iteration 5

The next iteration separates data collection from the controller, making the code more re-usable. This is done by creating a jsonData factory method:

app.factory('jsonData', jsonData);
            
function dataController($scope, $http,jsonData) {
    
   jsonData.getPlaceholder(function (response) {
        $scope.body = response.data.body;
        $scope.title = response.data.title;
    });
}

function jsonData($http, CacheFactory) {

    CacheFactory('dataCache', {
        storageMode: 'localStorage',
        maxAge: 15 * 60 * 1000,
        cacheFlushInterval: 60 * 60 * 1000,
        deleteOnExpire: 'aggressive'
    });

    return {
        getPlaceholder: function (callback) {
            $http.get('https://jsonplaceholder.typicode.com/POSTS/1',
 {cache: CacheFactory.get('dataCache')
            }).then(callback);
        }
    };
};

Instead of a factory a service can also be used but in Angular both a identical.

Iteration 6

In the previous iteration a callback function was used to add the response to the scope but is it possible for the getPlaceholder method to just return the response? Not just like that, it is only possible to return the deferred promise by invoking the enigmatic $q service.

function dataController($scope, $http,jsonData) {
    promise = jsonData.getPlaceholder();               
    promise.then(function (response) {
        $scope.body = response.data.body;
        $scope.title = response.data.title;
    });
}

function jsonData($http, $q,  CacheFactory) {
     ........    
     var deferred = $q.defer();
     
    return {
        getPlaceholder: function () {
            $http.get('https://jsonplaceholder.typicode.com/POSTS/1', 
            {cache: CacheFactory.get('dataCache')
            }).then(function(response){
                deferred.resolve(response);                                                      
            } );
            return deferred.promise;
        }
    };
};

Iteration 7

Adding configuration to a project is a good practice. For this constant() is a default module method:

app.constant('dataCacheConfig', {
    storageMode: 'memory',
    maxAge: 15 * 60 * 1000,
    cacheFlushInterval: 60 * 60 * 1000,
    deleteOnExpire: 'aggressive'
});           
.....
CacheFactory('dataCache',dataCacheConfig);

Iteration 8

Of course when the API server returns an error nothing is displayed, But how to handle such errors? In addition to the success callback the $http then() method has an error callback as second argument. The http response also has the http status code.

Links

Advertisements