make xhr.cache optionally synchronous

- add `sync` flag xhr.cache
- change ng:include to use the sync flag
- change ng:view to use the sync flag

The end result is that there are fewer repaints in the browser,
which means less "blinking" that user sees.
This commit is contained in:
Igor Minar 2011-03-30 09:35:59 -07:00
parent 9985104dc0
commit c06c5a36b1
4 changed files with 29 additions and 10 deletions

View file

@ -20,10 +20,11 @@
* @param {boolean=} [verifyCache=false] If `true` then a result is immediately returned from cache * @param {boolean=} [verifyCache=false] If `true` then a result is immediately returned from cache
* (if present) while a request is sent to the server for a fresh response that will update the * (if present) while a request is sent to the server for a fresh response that will update the
* cached entry. The `callback` function will be called when the response is received. * cached entry. The `callback` function will be called when the response is received.
* @param {boolean=} [sync=false] in case of cache hit execute `callback` synchronously.
*/ */
angularServiceInject('$xhr.cache', function($xhr, $defer, $log){ angularServiceInject('$xhr.cache', function($xhr, $defer, $log){
var inflight = {}, self = this; var inflight = {}, self = this;
function cache(method, url, post, callback, verifyCache){ function cache(method, url, post, callback, verifyCache, sync){
if (isFunction(post)) { if (isFunction(post)) {
callback = post; callback = post;
post = null; post = null;
@ -31,7 +32,13 @@ angularServiceInject('$xhr.cache', function($xhr, $defer, $log){
if (method == 'GET') { if (method == 'GET') {
var data, dataCached; var data, dataCached;
if (dataCached = cache.data[url]) { if (dataCached = cache.data[url]) {
$defer(function() { callback(200, copy(dataCached.value)); });
if (sync) {
callback(200, copy(dataCached.value));
} else {
$defer(function() { callback(200, copy(dataCached.value)); });
}
if (!verifyCache) if (!verifyCache)
return; return;
} }

View file

@ -673,12 +673,12 @@ angularWidget('ng:include', function(element){
useScope = this.$eval(scopeExp); useScope = this.$eval(scopeExp);
if (src) { if (src) {
xhr('GET', src, function(code, response){ xhr('GET', src, null, function(code, response){
element.html(response); element.html(response);
childScope = useScope || createScope(scope); childScope = useScope || createScope(scope);
compiler.compile(element)(childScope); compiler.compile(element)(childScope);
scope.$eval(onloadExp); scope.$eval(onloadExp);
}); }, false, true);
} else { } else {
childScope = null; childScope = null;
element.html(''); element.html('');
@ -1066,10 +1066,10 @@ angularWidget('ng:view', function(element) {
} }
if (src) { if (src) {
$xhr('GET', src, function(code, response){ $xhr('GET', src, null, function(code, response){
element.html(response); element.html(response);
compiler.compile(element)(childScope); compiler.compile(element)(childScope);
}); }, false, true);
} else { } else {
element.html(''); element.html('');
} }

View file

@ -107,6 +107,22 @@ describe('$xhr.cache', function() {
}); });
it('should call callback synchronously when sync flag is on', function() {
$browserXhr.expectGET('/url').respond('+');
cache('GET', '/url', null, callback, false, true);
expect(log).toEqual(''); //callback hasn't executed
$browserXhr.flush();
expect(log).toEqual('"+";'); //callback has executed
cache('GET', '/url', null, callback, false, true);
expect(log).toEqual('"+";"+";'); //callback has executed
$browser.defer.flush();
expect(log).toEqual('"+";"+";'); //callback was not called again any more
});
it('should call eval after callbacks for both cache hit and cache miss execute', function() { it('should call eval after callbacks for both cache hit and cache miss execute', function() {
var eval = this.spyOn(scope, '$eval').andCallThrough(); var eval = this.spyOn(scope, '$eval').andCallThrough();

View file

@ -610,7 +610,6 @@ describe("widget", function(){
scope.url = 'myUrl'; scope.url = 'myUrl';
scope.$service('$xhr.cache').data.myUrl = {value:'{{name}}'}; scope.$service('$xhr.cache').data.myUrl = {value:'{{name}}'};
scope.$eval(); scope.$eval();
scope.$service('$browser').defer.flush();
expect(element.text()).toEqual('misko'); expect(element.text()).toEqual('misko');
dealoc(scope); dealoc(scope);
}); });
@ -623,7 +622,6 @@ describe("widget", function(){
scope.url = 'myUrl'; scope.url = 'myUrl';
scope.$service('$xhr.cache').data.myUrl = {value:'{{name}}'}; scope.$service('$xhr.cache').data.myUrl = {value:'{{name}}'};
scope.$eval(); scope.$eval();
scope.$service('$browser').defer.flush();
expect(element.text()).toEqual('igor'); expect(element.text()).toEqual('igor');
@ -640,7 +638,6 @@ describe("widget", function(){
scope.url = 'myUrl'; scope.url = 'myUrl';
scope.$service('$xhr.cache').data.myUrl = {value:'{{c=c+1}}'}; scope.$service('$xhr.cache').data.myUrl = {value:'{{c=c+1}}'};
scope.$eval(); scope.$eval();
scope.$service('$browser').defer.flush();
// this one should really be just '1', but due to lack of real events things are not working // this one should really be just '1', but due to lack of real events things are not working
// properly. see discussion at: http://is.gd/ighKk // properly. see discussion at: http://is.gd/ighKk
@ -657,7 +654,6 @@ describe("widget", function(){
scope.url = 'myUrl'; scope.url = 'myUrl';
scope.$service('$xhr.cache').data.myUrl = {value:'my partial'}; scope.$service('$xhr.cache').data.myUrl = {value:'my partial'};
scope.$eval(); scope.$eval();
scope.$service('$browser').defer.flush();
expect(element.text()).toEqual('my partial'); expect(element.text()).toEqual('my partial');
expect(scope.loaded).toBe(true); expect(scope.loaded).toBe(true);
dealoc(element); dealoc(element);