Skip to content

Commit 2cdf34a

Browse files
committed
fix(material/progress-bar): unable to change value through property setter
Fixes the progress bar not updating when its value is changed through the setter. Normally we don't really handle cases like this, but I decided to do it in this one, because: 1. We were already paying the payload price for the setter anyway so adding the `markForCheck` call won't be too expensive. 2. The progress bar is a bit of a special case where it might make sense not to go through the view to change a value. E.g. for something like a file upload where everything is being done in memory. Fixes #18676.
1 parent 03485cd commit 2cdf34a

File tree

4 files changed

+94
-7
lines changed

4 files changed

+94
-7
lines changed

src/material-experimental/mdc-progress-bar/progress-bar.spec.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,45 @@ describe('MDC-based MatProgressBar', () => {
175175
expect(progressElement.componentInstance.mode).toBe('buffer');
176176
expect(progressElement.componentInstance.color).toBe('warn');
177177
});
178+
179+
it('should update the DOM transform when the value has changed', () => {
180+
const fixture = createComponent(BasicProgressBar);
181+
fixture.detectChanges();
182+
183+
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
184+
const progressComponent = progressElement.componentInstance;
185+
const primaryBar = progressElement.nativeElement.querySelector(
186+
'.mdc-linear-progress__primary-bar',
187+
);
188+
189+
expect(primaryBar.style.transform).toBe('scaleX(0)');
190+
191+
progressComponent.value = 40;
192+
fixture.detectChanges();
193+
194+
expect(primaryBar.style.transform).toBe('scaleX(0.4)');
195+
});
196+
197+
it('should update the DOM transform when the bufferValue has changed', () => {
198+
const fixture = createComponent(BasicProgressBar);
199+
fixture.detectChanges();
200+
201+
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
202+
const progressComponent = progressElement.componentInstance;
203+
const bufferBar = progressElement.nativeElement.querySelector(
204+
'.mdc-linear-progress__buffer-bar',
205+
);
206+
207+
progressComponent.mode = 'buffer';
208+
fixture.detectChanges();
209+
210+
expect(bufferBar.style.flexBasis).toBe('0%');
211+
212+
progressComponent.bufferValue = 40;
213+
fixture.detectChanges();
214+
215+
expect(bufferBar.style.flexBasis).toBe('40%');
216+
});
178217
});
179218

180219
describe('animation trigger on determinate setting', () => {

src/material/progress-bar/progress-bar.spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,41 @@ describe('MatProgressBar', () => {
218218
expect(progressElement.componentInstance.mode).toBe('buffer');
219219
expect(progressElement.componentInstance.color).toBe('warn');
220220
});
221+
222+
it('should update the DOM transform when the value has changed', () => {
223+
const fixture = createComponent(BasicProgressBar);
224+
fixture.detectChanges();
225+
226+
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
227+
const progressComponent = progressElement.componentInstance;
228+
const primaryBar = progressElement.nativeElement.querySelector('.mat-progress-bar-primary');
229+
230+
expect(primaryBar.style.transform).toBe('scale3d(0, 1, 1)');
231+
232+
progressComponent.value = 40;
233+
fixture.detectChanges();
234+
235+
expect(primaryBar.style.transform).toBe('scale3d(0.4, 1, 1)');
236+
});
237+
238+
it('should update the DOM transform when the bufferValue has changed', () => {
239+
const fixture = createComponent(BasicProgressBar);
240+
fixture.detectChanges();
241+
242+
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
243+
const progressComponent = progressElement.componentInstance;
244+
const bufferBar = progressElement.nativeElement.querySelector('.mat-progress-bar-buffer');
245+
246+
progressComponent.mode = 'buffer';
247+
fixture.detectChanges();
248+
249+
expect(bufferBar.style.transform).toBeFalsy();
250+
251+
progressComponent.bufferValue = 40;
252+
fixture.detectChanges();
253+
254+
expect(bufferBar.style.transform).toBe('scale3d(0.4, 1, 1)');
255+
});
221256
});
222257

223258
describe('animation trigger on determinate setting', () => {

src/material/progress-bar/progress-bar.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
Output,
2424
ViewChild,
2525
ViewEncapsulation,
26+
ChangeDetectorRef,
2627
} from '@angular/core';
2728
import {CanColor, mixinColor, ThemePalette} from '@angular/material/core';
2829
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';
@@ -135,16 +136,20 @@ export class MatProgressBar
135136
@Optional()
136137
@Inject(MAT_PROGRESS_BAR_DEFAULT_OPTIONS)
137138
defaults?: MatProgressBarDefaultOptions,
139+
/**
140+
* @deprecated `_changeDetectorRef` parameter to be made required.
141+
* @breaking-change 11.0.0
142+
*/
143+
private _changeDetectorRef?: ChangeDetectorRef,
138144
) {
139145
super(elementRef);
140146

141147
// We need to prefix the SVG reference with the current path, otherwise they won't work
142148
// in Safari if the page has a `<base>` tag. Note that we need quotes inside the `url()`,
143-
144-
// because named route URLs can contain parentheses (see #12338). Also we don't use since
145-
// we can't tell the difference between whether
146-
// the consumer is using the hash location strategy or not, because `Location` normalizes
147-
// both `/#/foo/bar` and `/foo/bar` to the same thing.
149+
// because named route URLs can contain parentheses (see #12338). Also we don't use `Location`
150+
// since we can't tell the difference between whether the consumer is using the hash location
151+
// strategy or not, because `Location` normalizes both `/#/foo/bar` and `/foo/bar` to
152+
// the same thing.
148153
const path = location ? location.getPathname().split('#')[0] : '';
149154
this._rectangleFillValue = `url('${path}#${this.progressbarId}')`;
150155
this._isNoopAnimation = _animationMode === 'NoopAnimations';
@@ -168,6 +173,9 @@ export class MatProgressBar
168173
}
169174
set value(v: NumberInput) {
170175
this._value = clamp(coerceNumberProperty(v) || 0);
176+
177+
// @breaking-change 11.0.0 Remove null check for _changeDetectorRef.
178+
this._changeDetectorRef?.markForCheck();
171179
}
172180
private _value: number = 0;
173181

@@ -178,6 +186,9 @@ export class MatProgressBar
178186
}
179187
set bufferValue(v: number) {
180188
this._bufferValue = clamp(v || 0);
189+
190+
// @breaking-change 11.0.0 Remove null check for _changeDetectorRef.
191+
this._changeDetectorRef?.markForCheck();
181192
}
182193
private _bufferValue: number = 0;
183194

tools/public_api_guard/material/progress-bar.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import { _AbstractConstructor } from '@angular/material/core';
88
import { AfterViewInit } from '@angular/core';
99
import { CanColor } from '@angular/material/core';
10+
import { ChangeDetectorRef } from '@angular/core';
1011
import { _Constructor } from '@angular/material/core';
1112
import { ElementRef } from '@angular/core';
1213
import { EventEmitter } from '@angular/core';
@@ -31,7 +32,8 @@ export function MAT_PROGRESS_BAR_LOCATION_FACTORY(): MatProgressBarLocation;
3132
// @public
3233
export class MatProgressBar extends _MatProgressBarBase implements CanColor, AfterViewInit, OnDestroy {
3334
constructor(elementRef: ElementRef, _ngZone: NgZone, _animationMode?: string | undefined,
34-
location?: MatProgressBarLocation, defaults?: MatProgressBarDefaultOptions);
35+
location?: MatProgressBarLocation, defaults?: MatProgressBarDefaultOptions,
36+
_changeDetectorRef?: ChangeDetectorRef | undefined);
3537
readonly animationEnd: EventEmitter<ProgressAnimationEnd>;
3638
// (undocumented)
3739
_animationMode?: string | undefined;
@@ -58,7 +60,7 @@ export class MatProgressBar extends _MatProgressBarBase implements CanColor, Aft
5860
// (undocumented)
5961
static ɵcmp: i0.ɵɵComponentDeclaration<MatProgressBar, "mat-progress-bar", ["matProgressBar"], { "color": "color"; "value": "value"; "bufferValue": "bufferValue"; "mode": "mode"; }, { "animationEnd": "animationEnd"; }, never, never>;
6062
// (undocumented)
61-
static ɵfac: i0.ɵɵFactoryDeclaration<MatProgressBar, [null, null, { optional: true; }, { optional: true; }, { optional: true; }]>;
63+
static ɵfac: i0.ɵɵFactoryDeclaration<MatProgressBar, [null, null, { optional: true; }, { optional: true; }, { optional: true; }, null]>;
6264
}
6365

6466
// @public

0 commit comments

Comments
 (0)