Skip to content

Commit fd3bac6

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 4caf039 commit fd3bac6

File tree

4 files changed

+96
-9
lines changed

4 files changed

+96
-9
lines changed

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,43 @@ describe('MDC-based MatProgressBar', () => {
152152
.toBe(false, 'Expect aria-valuenow to be cleared in query mode.');
153153
});
154154

155+
it('should update the DOM transform when the value has changed', () => {
156+
const fixture = createComponent(BasicProgressBar);
157+
fixture.detectChanges();
158+
159+
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
160+
const progressComponent = progressElement.componentInstance;
161+
const primaryBar = progressElement.nativeElement
162+
.querySelector('.mdc-linear-progress__primary-bar');
163+
164+
expect(primaryBar.style.transform).toBe('scaleX(0)');
165+
166+
progressComponent.value = 40;
167+
fixture.detectChanges();
168+
169+
expect(primaryBar.style.transform).toBe('scaleX(0.4)');
170+
});
171+
172+
it('should update the DOM transform when the bufferValue has changed', () => {
173+
const fixture = createComponent(BasicProgressBar);
174+
fixture.detectChanges();
175+
176+
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
177+
const progressComponent = progressElement.componentInstance;
178+
const bufferBar = progressElement.nativeElement
179+
.querySelector('.mdc-linear-progress__buffer-bar');
180+
181+
progressComponent.mode = 'buffer';
182+
fixture.detectChanges();
183+
184+
expect(bufferBar.style.flexBasis).toBe('0%');
185+
186+
progressComponent.bufferValue = 40;
187+
fixture.detectChanges();
188+
189+
expect(bufferBar.style.flexBasis).toBe('40%');
190+
});
191+
155192
});
156193

157194
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
@@ -196,6 +196,41 @@ describe('MatProgressBar', () => {
196196
.toBe(false, 'Expect aria-valuenow to be cleared in query mode.');
197197
});
198198

199+
it('should update the DOM transform when the value has changed', () => {
200+
const fixture = createComponent(BasicProgressBar);
201+
fixture.detectChanges();
202+
203+
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
204+
const progressComponent = progressElement.componentInstance;
205+
const primaryBar = progressElement.nativeElement.querySelector('.mat-progress-bar-primary');
206+
207+
expect(primaryBar.style.transform).toBe('scale3d(0, 1, 1)');
208+
209+
progressComponent.value = 40;
210+
fixture.detectChanges();
211+
212+
expect(primaryBar.style.transform).toBe('scale3d(0.4, 1, 1)');
213+
});
214+
215+
it('should update the DOM transform when the bufferValue has changed', () => {
216+
const fixture = createComponent(BasicProgressBar);
217+
fixture.detectChanges();
218+
219+
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
220+
const progressComponent = progressElement.componentInstance;
221+
const bufferBar = progressElement.nativeElement.querySelector('.mat-progress-bar-buffer');
222+
223+
progressComponent.mode = 'buffer';
224+
fixture.detectChanges();
225+
226+
expect(bufferBar.style.transform).toBeFalsy();
227+
228+
progressComponent.bufferValue = 40;
229+
fixture.detectChanges();
230+
231+
expect(bufferBar.style.transform).toBe('scale3d(0.4, 1, 1)');
232+
});
233+
199234
});
200235

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

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

Lines changed: 21 additions & 7 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} from '@angular/material/core';
2829
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';
@@ -111,16 +112,21 @@ export class MatProgressBar extends _MatProgressBarBase implements CanColor,
111112
* @deprecated `location` parameter to be made required.
112113
* @breaking-change 8.0.0
113114
*/
114-
@Optional() @Inject(MAT_PROGRESS_BAR_LOCATION) location?: MatProgressBarLocation) {
115+
@Optional() @Inject(MAT_PROGRESS_BAR_LOCATION) location?: MatProgressBarLocation,
116+
117+
/**
118+
* @deprecated `_changeDetectorRef` parameter to be made required.
119+
* @breaking-change 11.0.0
120+
*/
121+
private _changeDetectorRef?: ChangeDetectorRef) {
115122
super(elementRef);
116123

117124
// We need to prefix the SVG reference with the current path, otherwise they won't work
118125
// in Safari if the page has a `<base>` tag. Note that we need quotes inside the `url()`,
119-
120-
// because named route URLs can contain parentheses (see #12338). Also we don't use since
121-
// we can't tell the difference between whether
122-
// the consumer is using the hash location strategy or not, because `Location` normalizes
123-
// both `/#/foo/bar` and `/foo/bar` to the same thing.
126+
// because named route URLs can contain parentheses (see #12338). Also we don't use `Location`
127+
// since we can't tell the difference between whether the consumer is using the hash location
128+
// strategy or not, because `Location` normalizes both `/#/foo/bar` and `/foo/bar` to
129+
// the same thing.
124130
const path = location ? location.getPathname().split('#')[0] : '';
125131
this._rectangleFillValue = `url('${path}#${this.progressbarId}')`;
126132
this._isNoopAnimation = _animationMode === 'NoopAnimations';
@@ -134,13 +140,21 @@ export class MatProgressBar extends _MatProgressBarBase implements CanColor,
134140
get value(): number { return this._value; }
135141
set value(v: number) {
136142
this._value = clamp(coerceNumberProperty(v) || 0);
143+
144+
// @breaking-change 11.0.0 Remove null check for _changeDetectorRef.
145+
this._changeDetectorRef?.markForCheck();
137146
}
138147
private _value: number = 0;
139148

140149
/** Buffer value of the progress bar. Defaults to zero. */
141150
@Input()
142151
get bufferValue(): number { return this._bufferValue; }
143-
set bufferValue(v: number) { this._bufferValue = clamp(v || 0); }
152+
set bufferValue(v: number) {
153+
this._bufferValue = clamp(v || 0);
154+
155+
// @breaking-change 11.0.0 Remove null check for _changeDetectorRef.
156+
this._changeDetectorRef?.markForCheck();
157+
}
144158
private _bufferValue: number = 0;
145159

146160
@ViewChild('primaryValueBar') _primaryValueBar: ElementRef;

tools/public_api_guard/material/progress-bar.d.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ export declare class MatProgressBar extends _MatProgressBarBase implements CanCo
1515
get value(): number;
1616
set value(v: number);
1717
constructor(elementRef: ElementRef, _ngZone: NgZone, _animationMode?: string | undefined,
18-
location?: MatProgressBarLocation);
18+
location?: MatProgressBarLocation,
19+
_changeDetectorRef?: ChangeDetectorRef | undefined);
1920
_bufferTransform(): {
2021
transform: string;
2122
} | null;
@@ -26,7 +27,7 @@ export declare class MatProgressBar extends _MatProgressBarBase implements CanCo
2627
ngOnDestroy(): void;
2728
static ngAcceptInputType_value: NumberInput;
2829
static ɵcmp: i0.ɵɵComponentDeclaration<MatProgressBar, "mat-progress-bar", ["matProgressBar"], { "color": "color"; "value": "value"; "bufferValue": "bufferValue"; "mode": "mode"; }, { "animationEnd": "animationEnd"; }, never, never>;
29-
static ɵfac: i0.ɵɵFactoryDeclaration<MatProgressBar, [null, null, { optional: true; }, { optional: true; }]>;
30+
static ɵfac: i0.ɵɵFactoryDeclaration<MatProgressBar, [null, null, { optional: true; }, { optional: true; }, null]>;
3031
}
3132

3233
export interface MatProgressBarLocation {

0 commit comments

Comments
 (0)