Skip to content

Commit aebbd8a

Browse files
crisbetojelbourn
authored andcommitted
fix(progress-spinner): not working with server-side rendering (#4020)
* Switches the progress spinner from using `querySelector` to `ViewChild`. * Moves all of the remaining DOM manipulation to the renderer. * Skips animations if `requestAnimationFrame` is not available. * Simplifies some unnecessarily complex logic for adding/removing the theme class. Fixes #3988.
1 parent 32b7426 commit aebbd8a

File tree

2 files changed

+20
-32
lines changed

2 files changed

+20
-32
lines changed

src/lib/progress-spinner/progress-spinner.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
-->
66
<svg viewBox="0 0 100 100"
77
preserveAspectRatio="xMidYMid meet">
8-
<path></path>
8+
<path #path></path>
99
</svg>

src/lib/progress-spinner/progress-spinner.ts

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import {
66
Input,
77
ElementRef,
88
NgZone,
9-
Renderer, Directive
9+
Renderer2,
10+
Directive,
11+
ViewChild,
1012
} from '@angular/core';
1113

1214

@@ -24,6 +26,8 @@ const startIndeterminate = 3;
2426
const endIndeterminate = 80;
2527
/* Maximum angle for the arc. The angle can't be exactly 360, because the arc becomes hidden. */
2628
const MAX_ANGLE = 359.99 / 100;
29+
/** Whether the user's browser supports requestAnimationFrame. */
30+
const HAS_RAF = typeof requestAnimationFrame !== 'undefined';
2731

2832
export type ProgressSpinnerMode = 'determinate' | 'indeterminate';
2933

@@ -67,7 +71,7 @@ export class MdProgressSpinner implements OnDestroy {
6771
private _interdeterminateInterval: number;
6872

6973
/** The SVG <path> node that is used to draw the circle. */
70-
private _path: SVGPathElement;
74+
@ViewChild('path') private _path: ElementRef;
7175

7276
private _mode: ProgressSpinnerMode = 'determinate';
7377
private _value: number;
@@ -107,7 +111,11 @@ export class MdProgressSpinner implements OnDestroy {
107111
@Input()
108112
get color(): string { return this._color; }
109113
set color(value: string) {
110-
this._updateColor(value);
114+
if (value) {
115+
this._renderer.removeClass(this._elementRef.nativeElement, `mat-${this._color}`);
116+
this._renderer.addClass(this._elementRef.nativeElement, `mat-${value}`);
117+
this._color = value;
118+
}
111119
}
112120

113121
/** Value of the progress circle. It is bound to the host as the attribute aria-valuenow. */
@@ -152,8 +160,7 @@ export class MdProgressSpinner implements OnDestroy {
152160
constructor(
153161
private _ngZone: NgZone,
154162
private _elementRef: ElementRef,
155-
private _renderer: Renderer
156-
) {}
163+
private _renderer: Renderer2) { }
157164

158165

159166
/**
@@ -178,7 +185,10 @@ export class MdProgressSpinner implements OnDestroy {
178185
this._renderArc(animateTo, rotation);
179186
} else {
180187
let animation = () => {
181-
let elapsedTime = Math.max(0, Math.min(Date.now() - startTime, duration));
188+
// If there is no requestAnimationFrame, skip ahead to the end of the animation.
189+
let elapsedTime = HAS_RAF ?
190+
Math.max(0, Math.min(Date.now() - startTime, duration)) :
191+
duration;
182192

183193
this._renderArc(
184194
ease(elapsedTime, animateFrom, changeInValue, duration),
@@ -237,30 +247,8 @@ export class MdProgressSpinner implements OnDestroy {
237247
* DOM attribute on the `<path>`.
238248
*/
239249
private _renderArc(currentValue: number, rotation = 0) {
240-
// Caches the path reference so it doesn't have to be looked up every time.
241-
let path = this._path = this._path || this._elementRef.nativeElement.querySelector('path');
242-
243-
// Ensure that the path was found. This may not be the case if the
244-
// animation function fires too early.
245-
if (path) {
246-
path.setAttribute('d', getSvgArc(currentValue, rotation));
247-
}
248-
}
249-
250-
/**
251-
* Updates the color of the progress-spinner by adding the new palette class to the element
252-
* and removing the old one.
253-
*/
254-
private _updateColor(newColor: string) {
255-
this._setElementColor(this._color, false);
256-
this._setElementColor(newColor, true);
257-
this._color = newColor;
258-
}
259-
260-
/** Sets the given palette class on the component element. */
261-
private _setElementColor(color: string, isAdd: boolean) {
262-
if (color != null && color != '') {
263-
this._renderer.setElementClass(this._elementRef.nativeElement, `mat-${color}`, isAdd);
250+
if (this._path) {
251+
this._renderer.setAttribute(this._path.nativeElement, 'd', getSvgArc(currentValue, rotation));
264252
}
265253
}
266254
}
@@ -285,7 +273,7 @@ export class MdProgressSpinner implements OnDestroy {
285273
})
286274
export class MdSpinner extends MdProgressSpinner implements OnDestroy {
287275

288-
constructor(elementRef: ElementRef, ngZone: NgZone, renderer: Renderer) {
276+
constructor(elementRef: ElementRef, ngZone: NgZone, renderer: Renderer2) {
289277
super(ngZone, elementRef, renderer);
290278
this.mode = 'indeterminate';
291279
}

0 commit comments

Comments
 (0)