Created
June 10, 2016 16:25
-
-
Save cmoore4/75192bf59396ad888195489c1ca26da1 to your computer and use it in GitHub Desktop.
A method, using Angular's promise notify functionality, to immediately restore previous page contents on renavigating to the same page, while simultaneously getting new data and updating the cache.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// These two resources helped me out: | |
// http://www.webdeveasy.com/interceptors-in-angularjs-and-useful-examples/ | |
// https://code.angularjs.org/1.4.0/docs/api/ng/service/$q | |
module.factory('httpCacher', ['localStorageService', '$q', '$timeout', '$injector', function(localStorageService, $q, $timeout, $injector) { | |
var cachePrefix = 'ng_http_cache_'; | |
var cacheExpire = 1000 * 60 * 60 * 8; // 8 Hour Expirey | |
// http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/ | |
// This will be the key, along with cachePrefix above for our localstorage key:value pair, | |
// hashing the request object | |
String.prototype.hashCode = function() { | |
var hash = 0; | |
if (this.length === 0) return hash; | |
for (var i = 0; i < this.length; i++) { | |
var char = this.charCodeAt(i); | |
hash = ((hash << 5) - hash) + char; | |
hash = hash & hash; // Convert to 32bit integer | |
} | |
return hash; | |
}; | |
var cache = { | |
get: function(key) { | |
var val = localStorageService.get(key); | |
if (val) { | |
return val; | |
} else { | |
return {}; | |
} | |
}, | |
set: function(key, value, config) { | |
// Clear existing cache expirations for this key | |
var expires = window.expires || {}; | |
if (expires[key]) { | |
clearTimeout(expires[key]); | |
} | |
// Set a new cache expiration for the key | |
var expire = setTimeout(function() { | |
localStorageService.remove(key); | |
}, cacheExpire); | |
expires[key] = expire; | |
return localStorageService.set( | |
key, { | |
data: value, | |
time: new Date().getTime(), | |
request: config | |
} | |
); | |
} | |
}; | |
var cacheInjector = { | |
request: function(request) { | |
// Only get cache idempotent actions | |
if (['GET', 'HEAD', 'OPTIONS'].indexOf(request.method) === -1) { | |
return request; | |
} | |
var key = cachePrefix + JSON.stringify(request).hashCode(); | |
var val = cache.get(key); | |
if (val.data) { | |
var $http = $injector.get('$http'), | |
defer = $q.defer(); | |
defer.notify(val); | |
$timeout(function() { | |
defer.notify(val); | |
defer.resolve(request); | |
}, 10, false); | |
return defer.promise; | |
} | |
return request; | |
}, | |
response: function(response) { | |
if (['GET', 'HEAD', 'OPTIONS'].indexOf(response.config.method) === -1) { | |
return response; | |
} | |
var key = cachePrefix + JSON.stringify(response.config).hashCode(); | |
var val = response.data; | |
cache.set(key, val, response.config); | |
return response; | |
} | |
}; | |
return cacheInjector; | |
}]); | |
module.config(['$httpProvider', function($httpProvider) { | |
$httpProvider.interceptors.push('httpCacher'); | |
}]); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Using this file as example: | |
// https://bitbucket.org/cgdev/acrew-app/src/22ceeae5c12dc2ca61d2cb3f5ef5f38d7289867c/src/views/company/company.js?at=master&fileviewer=file-view-default | |
'use strict'; | |
angular.module('Acrew.company', ['ngRoute', 'lbServices']) | |
.controller('companyController', ['$scope', '$rootScope', '$location', 'Company', 'Job', 'localStorageService', '$routeParams', | |
function($scope, $rootScope, $location, Company, Job, localStorageService, $routeParams) { | |
$scope.company = {}; | |
$scope.jobs = []; | |
$rootScope.pageTitle = 'Company'; | |
$rootScope.isLoading = true; | |
// This will be called by both cache hits and server responses | |
$scope.setCompany = function(company) { | |
$scope.company = company; | |
localStorageService.set('company', company); | |
}; | |
Company.findOne({ | |
filter: { | |
where: { | |
id: $routeParams.id | |
} | |
} | |
}).$promise.then( | |
// Success callback | |
function(data) { | |
console.log('[company#Company.find] Success'); | |
$scope.setCompany(data); | |
}, | |
// Eror callback | |
function(err) { | |
console.error('[company#Company.find] Error', err); | |
}, | |
// The third promise callback argument executes everytime $q.notify is called. | |
// We send back the cached value on notify as the request is sent to the server. | |
function(cache) { | |
$scope.setCompany(cache.data); | |
// We turn off the main loading indicator, and load a smaller "updating" spinner in the corner | |
$rootScope.isLoading = false; | |
$rootScope.backgroundLoading = true; | |
// Finally *always* executes after success or failure, and here we make sure we always toggle off the | |
// loading indicators | |
}).finally(function() { | |
$rootScope.isLoading = false; | |
$rootScope.backgroundLoading = false; | |
}); | |
// ... cut remaining code for demo purposes | |
} | |
]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment