Skip to content

Commit b89aa76

Browse files
committed
fix(progress-spinner): not redrawing when changing modes
* Fixes the progress spinner not redrawing when the mode is changed dynamically from `indeterminate` to `determinate`. * Some minor cleanup in the progress spinner. Fixes #3648.
1 parent 3ad6ff0 commit b89aa76

File tree

4 files changed

+51
-22
lines changed

4 files changed

+51
-22
lines changed

src/demo-app/progress-spinner/progress-spinner-demo.html

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ <h1>Determinate</h1>
44
<p>Value: {{progressValue}}</p>
55
<button md-raised-button (click)="step(10)">Increase</button>
66
<button md-raised-button (click)="step(-10)">Decrease</button>
7+
<md-checkbox [(ngModel)]="modeToggle">Is determinate</md-checkbox>
78
</div>
89

910
<div class="demo-progress-spinner">
10-
<md-progress-spinner mode="determinate" [value]="progressValue" color="primary"></md-progress-spinner>
11-
<md-progress-spinner [value]="progressValue" color="accent"></md-progress-spinner>
11+
<md-progress-spinner [mode]="modeToggle ? 'indeterminate' : 'determinate'"
12+
[value]="progressValue" color="primary"></md-progress-spinner>
13+
<md-progress-spinner [mode]="modeToggle ? 'indeterminate' : 'determinate'"
14+
[value]="progressValue" color="accent"></md-progress-spinner>
1215
</div>
1316

1417
<h1>Indeterminate</h1>
@@ -24,3 +27,4 @@ <h1>Indeterminate</h1>
2427
<md-progress-spinner mode="indeterminate" [color]="color"></md-progress-spinner>
2528
<md-spinner [color]="color"></md-spinner>
2629
</div>
30+

src/demo-app/progress-spinner/progress-spinner-demo.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import {Component} from '@angular/core';
88
styleUrls: ['progress-spinner-demo.css'],
99
})
1010
export class ProgressSpinnerDemo {
11-
progressValue: number = 40;
11+
progressValue: number = 60;
1212
color: string = 'primary';
13+
modeToggle: boolean = false;
1314

1415
step(val: number) {
1516
this.progressValue = Math.max(0, Math.min(100, val + this.progressValue));

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

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ describe('MdProgressSpinner', () => {
5050
it('should set the value to undefined when the mode is set to indeterminate', () => {
5151
let fixture = TestBed.createComponent(ProgressSpinnerWithValueAndBoundMode);
5252
let progressElement = fixture.debugElement.query(By.css('md-progress-spinner'));
53-
fixture.debugElement.componentInstance.mode = 'determinate';
53+
fixture.componentInstance.mode = 'determinate';
5454
fixture.detectChanges();
5555

5656
expect(progressElement.componentInstance.value).toBe(50);
57-
fixture.debugElement.componentInstance.mode = 'indeterminate';
57+
fixture.componentInstance.mode = 'indeterminate';
5858
fixture.detectChanges();
5959
expect(progressElement.componentInstance.value).toBe(undefined);
6060
});
@@ -89,7 +89,7 @@ describe('MdProgressSpinner', () => {
8989
let progressElement = fixture.debugElement.query(By.css('md-progress-spinner'));
9090
expect(progressElement.componentInstance.interdeterminateInterval).toBeTruthy();
9191

92-
fixture.debugElement.componentInstance.isHidden = true;
92+
fixture.componentInstance.isHidden = true;
9393
fixture.detectChanges();
9494
expect(progressElement.componentInstance.interdeterminateInterval).toBeFalsy();
9595
});
@@ -102,7 +102,7 @@ describe('MdProgressSpinner', () => {
102102

103103
expect(progressElement.componentInstance.interdeterminateInterval).toBeTruthy();
104104

105-
fixture.debugElement.componentInstance.isHidden = true;
105+
fixture.componentInstance.isHidden = true;
106106
fixture.detectChanges();
107107

108108
expect(progressElement.componentInstance.interdeterminateInterval).toBeFalsy();
@@ -116,7 +116,7 @@ describe('MdProgressSpinner', () => {
116116

117117
expect(progressElement.nativeElement.classList).toContain('mat-primary');
118118

119-
fixture.debugElement.componentInstance.color = 'accent';
119+
fixture.componentInstance.color = 'accent';
120120
fixture.detectChanges();
121121

122122
expect(progressElement.nativeElement.classList).toContain('mat-accent');
@@ -131,13 +131,30 @@ describe('MdProgressSpinner', () => {
131131

132132
expect(progressElement.nativeElement.classList).toContain('mat-primary');
133133

134-
fixture.debugElement.componentInstance.color = 'accent';
134+
fixture.componentInstance.color = 'accent';
135135
fixture.detectChanges();
136136

137137
expect(progressElement.nativeElement.classList).toContain('mat-accent');
138138
expect(progressElement.nativeElement.classList).not.toContain('mat-primary');
139139
});
140140

141+
it('should re-render the circle when switching from indeterminate to determinate mode', () => {
142+
let fixture = TestBed.createComponent(ProgressSpinnerWithValueAndBoundMode);
143+
let progressElement = fixture.debugElement.query(By.css('md-progress-spinner')).nativeElement;
144+
145+
fixture.componentInstance.mode = 'indeterminate';
146+
fixture.detectChanges();
147+
148+
let path = progressElement.querySelector('path');
149+
let oldDimesions = path.getAttribute('d');
150+
151+
fixture.componentInstance.mode = 'determinate';
152+
fixture.detectChanges();
153+
154+
expect(path.getAttribute('d')).not
155+
.toBe(oldDimesions, 'Expected circle dimensions to have changed.');
156+
});
157+
141158
});
142159

143160

@@ -148,14 +165,14 @@ class BasicProgressSpinner { }
148165
class IndeterminateProgressSpinner { }
149166

150167
@Component({template: '<md-progress-spinner value="50" [mode]="mode"></md-progress-spinner>'})
151-
class ProgressSpinnerWithValueAndBoundMode { }
168+
class ProgressSpinnerWithValueAndBoundMode { mode = 'indeterminate'; }
152169

153170
@Component({template: `
154171
<md-progress-spinner mode="indeterminate" *ngIf="!isHidden"></md-progress-spinner>`})
155-
class IndeterminateProgressSpinnerWithNgIf { }
172+
class IndeterminateProgressSpinnerWithNgIf { isHidden = false; }
156173

157174
@Component({template: `<md-spinner *ngIf="!isHidden"></md-spinner>`})
158-
class SpinnerWithNgIf { }
175+
class SpinnerWithNgIf { isHidden = false; }
159176

160177
@Component({template: `<md-spinner [color]="color"></md-spinner>`})
161178
class SpinnerWithColor { color: string = 'primary'; }

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

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ export class MdProgressSpinner implements OnDestroy {
134134
set value(v: number) {
135135
if (v != null && this.mode == 'determinate') {
136136
let newValue = clamp(v);
137-
this._animateCircle((this.value || 0), newValue, linearEase, DURATION_DETERMINATE, 0);
137+
this._animateCircle(this.value || 0, newValue);
138138
this._value = newValue;
139139
}
140140
}
@@ -150,13 +150,13 @@ export class MdProgressSpinner implements OnDestroy {
150150
get mode() {
151151
return this._mode;
152152
}
153-
set mode(m: ProgressSpinnerMode) {
154-
if (m == 'indeterminate') {
153+
set mode(mode: ProgressSpinnerMode) {
154+
if (mode === 'indeterminate') {
155155
this._startIndeterminateAnimation();
156156
} else {
157-
this._cleanupIndeterminateAnimation();
157+
this._cleanupIndeterminateAnimation(true);
158158
}
159-
this._mode = m;
159+
this._mode = mode;
160160
}
161161

162162
constructor(
@@ -176,8 +176,8 @@ export class MdProgressSpinner implements OnDestroy {
176176
* @param rotation The starting angle of the circle fill, with 0° represented at the top center
177177
* of the circle.
178178
*/
179-
private _animateCircle(animateFrom: number, animateTo: number, ease: EasingFn,
180-
duration: number, rotation: number) {
179+
private _animateCircle(animateFrom: number, animateTo: number, ease: EasingFn = linearEase,
180+
duration = DURATION_DETERMINATE, rotation = 0) {
181181

182182
let id = ++this._lastAnimationId;
183183
let startTime = Date.now();
@@ -237,16 +237,23 @@ export class MdProgressSpinner implements OnDestroy {
237237

238238
/**
239239
* Removes interval, ending the animation.
240+
* @param redraw Whether to redraw the circle after the animation is cleared.
240241
*/
241-
private _cleanupIndeterminateAnimation() {
242-
this.interdeterminateInterval = null;
242+
private _cleanupIndeterminateAnimation(redraw = false) {
243+
if (this.interdeterminateInterval) {
244+
this.interdeterminateInterval = null;
245+
246+
if (redraw) {
247+
this._animateCircle(0, this._value);
248+
}
249+
}
243250
}
244251

245252
/**
246253
* Renders the arc onto the SVG element. Proxies `getArc` while setting the proper
247254
* DOM attribute on the `<path>`.
248255
*/
249-
private _renderArc(currentValue: number, rotation: number) {
256+
private _renderArc(currentValue: number, rotation = 0) {
250257
// Caches the path reference so it doesn't have to be looked up every time.
251258
let path = this._path = this._path || this._elementRef.nativeElement.querySelector('path');
252259

0 commit comments

Comments
 (0)