Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit d497822

Browse files
feat($http): support handling additional XHR events
Closes #14367 Closes #11547 Closes #1934
1 parent 50cdede commit d497822

File tree

5 files changed

+73
-24
lines changed

5 files changed

+73
-24
lines changed

src/ng/http.js

+27-3
Original file line numberDiff line numberDiff line change
@@ -801,8 +801,12 @@ function $HttpProvider() {
801801
* - **headers** – `{Object}` – Map of strings or functions which return strings representing
802802
* HTTP headers to send to the server. If the return value of a function is null, the
803803
* header will not be sent. Functions accept a config object as an argument.
804-
* - **events** - `{Object}` - Event listeners to be bound to the XMLHttpRequest object.
805-
* To bind events to the XMLHttpRequest upload object, nest it under the upload key.
804+
* - **eventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest object.
805+
* To bind events to the XMLHttpRequest upload object, use `uploadEventHandlers`.
806+
* The handler will be called in the context of a `$apply` block.
807+
* - **uploadEventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest upload
808+
* object. To bind events to the XMLHttpRequest object, use `eventHandlers`.
809+
* The handler will be called in the context of a `$apply` block.
806810
* - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
807811
* - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
808812
* - **transformRequest** –
@@ -1261,11 +1265,31 @@ function $HttpProvider() {
12611265
}
12621266

12631267
$httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
1264-
config.withCredentials, config.responseType, config.events);
1268+
config.withCredentials, config.responseType,
1269+
createApplyHandlers(config.eventHandlers),
1270+
createApplyHandlers(config.uploadEventHandlers));
12651271
}
12661272

12671273
return promise;
12681274

1275+
function createApplyHandlers(eventHandlers) {
1276+
if (eventHandlers) {
1277+
var applyHandlers = {};
1278+
forEach(eventHandlers, function(eventHandler, key) {
1279+
applyHandlers[key] = function() {
1280+
if (useApplyAsync) {
1281+
$rootScope.$applyAsync(eventHandler);
1282+
} else if ($rootScope.$$phase) {
1283+
eventHandler();
1284+
} else {
1285+
$rootScope.$apply(eventHandler);
1286+
}
1287+
};
1288+
});
1289+
return applyHandlers;
1290+
}
1291+
}
1292+
12691293

12701294
/**
12711295
* Callback registered to $httpBackend():

src/ng/httpBackend.js

+7-13
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ function $HttpBackendProvider() {
5454

5555
function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
5656
// TODO(vojta): fix the signature
57-
return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers) {
57+
return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
5858
$browser.$$incOutstandingRequestCount();
5959
url = url || $browser.url();
6060

@@ -114,19 +114,13 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
114114
xhr.onerror = requestError;
115115
xhr.onabort = requestError;
116116

117-
if (eventHandlers) {
118-
forEach(eventHandlers, function(value, key) {
119-
if (key !== 'upload') {
120-
xhr.addEventListener(key, value);
121-
}
122-
});
117+
forEach(eventHandlers, function(value, key) {
118+
xhr.addEventListener(key, value);
119+
});
123120

124-
if (eventHandlers.upload) {
125-
forEach(eventHandlers.upload, function(value, key) {
126-
xhr.upload.addEventListener(key, value);
127-
});
128-
}
129-
}
121+
forEach(uploadEventHandlers, function(value, key) {
122+
xhr.upload.addEventListener(key, value);
123+
});
130124

131125
if (withCredentials) {
132126
xhr.withCredentials = true;

src/ngMock/angular-mocks.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -1322,12 +1322,15 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
13221322
}
13231323

13241324
// TODO(vojta): change params to: method, url, data, headers, callback
1325-
function $httpBackend(method, url, data, callback, headers, timeout, withCredentials, responseType) {
1325+
function $httpBackend(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
13261326

13271327
var xhr = new MockXhr(),
13281328
expectation = expectations[0],
13291329
wasExpected = false;
13301330

1331+
xhr.$$events = eventHandlers;
1332+
xhr.upload.$$events = uploadEventHandlers;
1333+
13311334
function prettyPrint(data) {
13321335
return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
13331336
? data
@@ -2011,6 +2014,9 @@ function MockXhr() {
20112014

20122015
this.abort = angular.noop;
20132016

2017+
// This section simulates the events on a real XHR object (and the upload object)
2018+
// When we are testing $httpBackend (inside the angular project) we make partial use of this
2019+
// but store the events directly ourselves on `$$events`, instead of going through the `addEventListener`
20142020
this.$$events = {};
20152021
this.addEventListener = function(name, listener) {
20162022
if (angular.isUndefined(this.$$events[name])) this.$$events[name] = [];

test/ng/httpBackendSpec.js

+2-6
Original file line numberDiff line numberDiff line change
@@ -244,12 +244,8 @@ describe('$httpBackend', function() {
244244
it('should set up event listeners', function() {
245245
var progressFn = function() {};
246246
var uploadProgressFn = function() {};
247-
$backend('GET', '/url', null, callback, {}, null, null, null, {
248-
progress: progressFn,
249-
upload: {
250-
progress: uploadProgressFn
251-
}
252-
});
247+
$backend('GET', '/url', null, callback, {}, null, null, null,
248+
{progress: progressFn}, {progress: uploadProgressFn});
253249
xhr = MockXhr.$$lastInstance;
254250
expect(xhr.$$events.progress[0]).toBe(progressFn);
255251
expect(xhr.upload.$$events.progress[0]).toBe(uploadProgressFn);

test/ng/httpSpec.js

+30-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22

3+
/* global MockXhr: false */
4+
35
describe('$http', function() {
46

57
var callback, mockedCookies;
@@ -1019,7 +1021,7 @@ describe('$http', function() {
10191021
});
10201022

10211023

1022-
describe('scope.$apply', function() {
1024+
describe('callbacks', function() {
10231025

10241026
it('should $apply after success callback', function() {
10251027
$httpBackend.when('GET').respond(200);
@@ -1047,6 +1049,33 @@ describe('$http', function() {
10471049

10481050
$exceptionHandler.errors = [];
10491051
}));
1052+
1053+
1054+
it('should pass the event handlers through to the backend', function() {
1055+
var progressFn = jasmine.createSpy('progressFn');
1056+
var uploadProgressFn = jasmine.createSpy('uploadProgressFn');
1057+
$httpBackend.when('GET').respond(200);
1058+
$http({
1059+
method: 'GET',
1060+
url: '/some',
1061+
eventHandlers: {progress: progressFn},
1062+
uploadEventHandlers: {progress: uploadProgressFn}
1063+
});
1064+
$rootScope.$apply();
1065+
var mockXHR = MockXhr.$$lastInstance;
1066+
expect(mockXHR.$$events.progress).toEqual(jasmine.any(Function));
1067+
expect(mockXHR.upload.$$events.progress).toEqual(jasmine.any(Function));
1068+
1069+
spyOn($rootScope, '$digest');
1070+
1071+
mockXHR.$$events.progress();
1072+
expect(progressFn).toHaveBeenCalledOnce();
1073+
expect($rootScope.$digest).toHaveBeenCalledTimes(1);
1074+
1075+
mockXHR.upload.$$events.progress();
1076+
expect(uploadProgressFn).toHaveBeenCalledOnce();
1077+
expect($rootScope.$digest).toHaveBeenCalledTimes(2);
1078+
});
10501079
});
10511080

10521081

0 commit comments

Comments
 (0)