Skip to content
This repository was archived by the owner on Mar 13, 2018. It is now read-only.

Commit 75da881

Browse files
author
John Messerly
committed
Merge pull request #27 from rictic/progress
Add progress and loading attributes; includes docs, demo, and test.
2 parents f50e457 + 887f67c commit 75da881

File tree

4 files changed

+248
-4
lines changed

4 files changed

+248
-4
lines changed

core-ajax.html

+61-4
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
@homepage github.io
3333
-->
3434
<link rel="import" href="core-xhr.html">
35-
<polymer-element name="core-ajax" hidden attributes="url handleAs auto params response error method headers body contentType withCredentials">
35+
<polymer-element name="core-ajax" hidden attributes="url handleAs auto params response error method headers body contentType withCredentials progress loading">
3636
<script>
3737

3838
Polymer('core-ajax', {
@@ -106,7 +106,7 @@
106106
params: '',
107107

108108
/**
109-
* The response for the most recently made request, or null if it hasn't
109+
* The response for the current request, or null if it hasn't
110110
* completed yet or the request resulted in error.
111111
*
112112
* @attribute response
@@ -116,7 +116,7 @@
116116
response: null,
117117

118118
/**
119-
* The error for the most recently made request, or null if it hasn't
119+
* The error for the current request, or null if it hasn't
120120
* completed yet or the request resulted in success.
121121
*
122122
* @attribute error
@@ -125,6 +125,24 @@
125125
*/
126126
error: null,
127127

128+
/**
129+
* Whether the current request is currently loading.
130+
*
131+
* @attribute loading
132+
* @type boolean
133+
* @default false
134+
*/
135+
loading: false,
136+
137+
/**
138+
* The progress of the current request.
139+
*
140+
* @attribute progress
141+
* @type {loaded: number, total: number, lengthComputable: boolean}
142+
* @default {}
143+
*/
144+
progress: null,
145+
128146
/**
129147
* The HTTP method to use such as 'GET', 'POST', 'PUT', or 'DELETE'.
130148
* Default is 'GET'.
@@ -199,6 +217,10 @@
199217
*/
200218
xhrArgs: null,
201219

220+
created: function() {
221+
this.progress = {};
222+
},
223+
202224
ready: function() {
203225
this.xhr = document.createElement('core-xhr');
204226
},
@@ -233,7 +255,26 @@
233255
this.fire('core-error', {response: response, xhr: xhr});
234256
},
235257

258+
processProgress: function(progress, xhr) {
259+
if (xhr !== this.activeRequest) {
260+
return;
261+
}
262+
// We create a proxy object here because these fields
263+
// on the progress event are readonly properties, which
264+
// causes problems in common use cases (e.g. binding to
265+
// <paper-progress> attributes).
266+
var progressProxy = {
267+
lengthComputable: progress.lengthComputable,
268+
loaded: progress.loaded,
269+
total: progress.total
270+
}
271+
this.progress = progressProxy;
272+
},
273+
236274
complete: function(xhr) {
275+
if (xhr === this.activeRequest) {
276+
this.loading = false;
277+
}
237278
this.fire('core-complete', {response: xhr.status, xhr: xhr});
238279
},
239280

@@ -334,8 +375,24 @@
334375
args.url = this.url;
335376
args.method = this.method;
336377

337-
this.response = this.error = null;
378+
this.response = this.error = this.progress = null;
338379
this.activeRequest = args.url && this.xhr.request(args);
380+
if (this.activeRequest) {
381+
this.loading = true;
382+
var activeRequest = this.activeRequest;
383+
// IE < 10 doesn't support progress events.
384+
if ('onprogress' in activeRequest) {
385+
this.activeRequest.addEventListener(
386+
'progress',
387+
function(progress) {
388+
this.processProgress(progress, activeRequest);
389+
}.bind(this), false);
390+
} else {
391+
this.progress = {
392+
lengthComputable: false,
393+
}
394+
}
395+
}
339396
return this.activeRequest;
340397
}
341398

demo-progress.html

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<script src="../platform/platform.js" debug></script>
5+
<meta charset="utf-8">
6+
<title>Race condition</title>
7+
</head>
8+
<body>
9+
<link rel="import" href="core-ajax.html">
10+
<link rel="import" href="../paper-progress/paper-progress.html">
11+
<polymer-element name="progress-test">
12+
<template>
13+
<core-ajax
14+
id="ajax" auto
15+
url="http://httpbin.org/drip"
16+
params="{{ {numbytes: numbytes, duration:5} }}"
17+
response="{{response}}"
18+
progress="{{progress}}"
19+
loading="{{loading}}"
20+
></core-ajax>
21+
22+
<!--
23+
Ordinarily you'd gate on progress.lengthComputable, but we know the
24+
length in this case (and httpbin sadly doesn't return a
25+
Content-Length header for this requesthere).
26+
27+
https://github.com/kennethreitz/httpbin/pull/160
28+
-->
29+
<div>
30+
<button on-click="{{restart}}">Restart</button>
31+
<template if="{{loading}}">
32+
Loading...
33+
</template>
34+
<template if="{{!loading}}">
35+
Loaded!
36+
</template>
37+
</div>
38+
<template if="{{loading && progress.loaded}}">
39+
<paper-progress
40+
value="{{progress.loaded}}"
41+
min="0"
42+
max="{{numbytes}}">
43+
</paper-progress><br>
44+
</template>
45+
</template>
46+
<script>
47+
Polymer('progress-test', {
48+
loading: true,
49+
i: 0,
50+
numbytes: 1000,
51+
pretty: function(i) {
52+
return JSON.stringify(i, null, 2);
53+
},
54+
restart: function() {
55+
this.$.ajax.abort();
56+
this.$.ajax.go();
57+
}
58+
});
59+
</script>
60+
61+
</polymer-element>
62+
63+
<progress-test></progress-test>
64+
</body>
65+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<!doctype html>
2+
<!--
3+
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
4+
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
5+
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6+
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
7+
Code distributed by Google as part of the polymer project is also
8+
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
9+
-->
10+
<html>
11+
<head>
12+
<title>core-ajax-response-and-error</title>
13+
14+
<script src="../../../platform/platform.js"></script>
15+
<script src="../../../polymer-test-tools/chai/chai.js"></script>
16+
<script src="../../../polymer-test-tools/htmltest.js"></script>
17+
18+
<link rel="import" href="../../core-ajax.html">
19+
<link rel="import" href="../../../core-range/core-range.html">
20+
21+
</head>
22+
<body>
23+
24+
<polymer-element name="race-condition">
25+
<template>
26+
<!-- TODO: change this from /headers to /drip if we can get httpbin to
27+
send a Content-Length with their /drip response.
28+
The delay is needed so that we can make assertions about
29+
the intermediate state in browsers that are slow to
30+
notice attribute changes.
31+
32+
https://github.com/kennethreitz/httpbin/pull/160
33+
-->
34+
<core-ajax
35+
auto url="http://httpbin.org/delay/1"
36+
loading="{{loading}}" progress="{{progress}}"
37+
on-core-response="{{responseReceived}}"></core-ajax>
38+
<core-range
39+
id="range"
40+
value="{{progress.loaded}}"
41+
min="0"
42+
max="{{progress.total}}"
43+
ratio="{{ratio}}">
44+
</core-range>
45+
</template>
46+
<script>
47+
Polymer({
48+
numbytes: 1000,
49+
loading: null,
50+
loadingWasTrue: false,
51+
loadingFinished: false,
52+
previousLoaded: null,
53+
domReady: function() {
54+
// This will fail the test if it neither passes nor fails in time.
55+
this.finalTimeout = setTimeout(function() {
56+
chai.assert.fail('', '', 'Test timed out.');
57+
}, 3000);
58+
},
59+
loadingChanged: function() {
60+
this.assertConsistent();
61+
},
62+
progressChanged: function() {
63+
this.assertConsistent();
64+
},
65+
responseReceived: function() {
66+
// Shortly after the response is received we should settle into
67+
// the final consistent state for the element.
68+
setTimeout(function() {
69+
chai.assert.equal(this.loading, false,
70+
'Should no longer be loading');
71+
chai.assert.equal(
72+
this.loadingWasTrue, true,
73+
'For a little while, we should have been loading.');
74+
if (this.progress && this.progress.lengthComputable) {
75+
chai.assert.equal(this.progress.total, this.progress.loaded);
76+
}
77+
this.assertConsistent();
78+
clearTimeout(this.finalTimeout);
79+
done();
80+
}.bind(this), 300);
81+
},
82+
assertConsistent: function() {
83+
if (this.progress && this.progress.lengthComputable) {
84+
chai.assert.isNumber(this.progress.total);
85+
chai.assert.isNumber(this.progress.loaded);
86+
chai.assert.isNumber(this.ratio);
87+
chai.assert(this.progress.loaded >= 0,
88+
'Bytes loaded should not be negative.');
89+
if (this.progress.loaded > 0) {
90+
this.$.range.update();
91+
chai.assert(this.ratio > 0,
92+
'If bytes loaded is >0, % loaded must be >0.');
93+
}
94+
chai.assert(this.progress.loaded <= this.progress.total,
95+
'Bytes loaded should be less than or equal the total.');
96+
if (this.previousLoaded !== null) {
97+
chai.assert(this.progress.loaded >= this.previousLoaded,
98+
'Bytes loaded should increase monotonically.');
99+
}
100+
this.previousLoaded = this.progress.loaded;
101+
console.log(this.progress.loaded + ' of ' + this.progress.total);
102+
}
103+
if (this.loading === true) {
104+
this.loadingWasTrue = true;
105+
}
106+
if (this.loadingWasTrue && this.loading === false) {
107+
this.loadingFinished = true;
108+
}
109+
if (this.loadingFinished) {
110+
chai.assert.equal(
111+
this.loading, false,
112+
'Once loaded, the request should stay that way');
113+
}
114+
}
115+
});
116+
</script>
117+
</polymer-element>
118+
119+
<race-condition></race-condition>
120+
</body>
121+
</html>

tests/js/htmltests.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
htmlSuite('core-ajax', function() {
22
htmlTest('html/core-ajax.html');
33
htmlTest('html/core-ajax-response-and-error.html');
4+
htmlTest('html/core-ajax-progress-and-loading.html');
45
});

0 commit comments

Comments
 (0)