Skip to content

Commit e28e8b2

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 881edec commit e28e8b2

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

+37
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,43 @@ describe('MDC-based MatProgressBar', () => {
167167
expect(progressElement.componentInstance.color).toBe('warn');
168168
});
169169

170+
it('should update the DOM transform when the value has changed', () => {
171+
const fixture = createComponent(BasicProgressBar);
172+
fixture.detectChanges();
173+
174+
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
175+
const progressComponent = progressElement.componentInstance;
176+
const primaryBar = progressElement.nativeElement
177+
.querySelector('.mdc-linear-progress__primary-bar');
178+
179+
expect(primaryBar.style.transform).toBe('scaleX(0)');
180+
181+
progressComponent.value = 40;
182+
fixture.detectChanges();
183+
184+
expect(primaryBar.style.transform).toBe('scaleX(0.4)');
185+
});
186+
187+
it('should update the DOM transform when the bufferValue has changed', () => {
188+
const fixture = createComponent(BasicProgressBar);
189+
fixture.detectChanges();
190+
191+
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
192+
const progressComponent = progressElement.componentInstance;
193+
const bufferBar = progressElement.nativeElement
194+
.querySelector('.mdc-linear-progress__buffer-bar');
195+
196+
progressComponent.mode = 'buffer';
197+
fixture.detectChanges();
198+
199+
expect(bufferBar.style.flexBasis).toBe('0%');
200+
201+
progressComponent.bufferValue = 40;
202+
fixture.detectChanges();
203+
204+
expect(bufferBar.style.flexBasis).toBe('40%');
205+
});
206+
170207
});
171208

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

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

+35
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,41 @@ describe('MatProgressBar', () => {
210210
expect(progressElement.componentInstance.color).toBe('warn');
211211
});
212212

213+
it('should update the DOM transform when the value has changed', () => {
214+
const fixture = createComponent(BasicProgressBar);
215+
fixture.detectChanges();
216+
217+
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
218+
const progressComponent = progressElement.componentInstance;
219+
const primaryBar = progressElement.nativeElement.querySelector('.mat-progress-bar-primary');
220+
221+
expect(primaryBar.style.transform).toBe('scale3d(0, 1, 1)');
222+
223+
progressComponent.value = 40;
224+
fixture.detectChanges();
225+
226+
expect(primaryBar.style.transform).toBe('scale3d(0.4, 1, 1)');
227+
});
228+
229+
it('should update the DOM transform when the bufferValue has changed', () => {
230+
const fixture = createComponent(BasicProgressBar);
231+
fixture.detectChanges();
232+
233+
const progressElement = fixture.debugElement.query(By.css('mat-progress-bar'))!;
234+
const progressComponent = progressElement.componentInstance;
235+
const bufferBar = progressElement.nativeElement.querySelector('.mat-progress-bar-buffer');
236+
237+
progressComponent.mode = 'buffer';
238+
fixture.detectChanges();
239+
240+
expect(bufferBar.style.transform).toBeFalsy();
241+
242+
progressComponent.bufferValue = 40;
243+
fixture.detectChanges();
244+
245+
expect(bufferBar.style.transform).toBe('scale3d(0.4, 1, 1)');
246+
});
247+
213248
});
214249

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

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

+20-7
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';
@@ -127,16 +128,20 @@ export class MatProgressBar extends _MatProgressBarBase implements CanColor,
127128
*/
128129
@Optional() @Inject(MAT_PROGRESS_BAR_LOCATION) location?: MatProgressBarLocation,
129130
@Optional() @Inject(MAT_PROGRESS_BAR_DEFAULT_OPTIONS)
130-
defaults?: MatProgressBarDefaultOptions) {
131+
defaults?: MatProgressBarDefaultOptions,
132+
/**
133+
* @deprecated `_changeDetectorRef` parameter to be made required.
134+
* @breaking-change 11.0.0
135+
*/
136+
private _changeDetectorRef?: ChangeDetectorRef) {
131137
super(elementRef);
132138

133139
// We need to prefix the SVG reference with the current path, otherwise they won't work
134140
// in Safari if the page has a `<base>` tag. Note that we need quotes inside the `url()`,
135-
136-
// because named route URLs can contain parentheses (see #12338). Also we don't use since
137-
// we can't tell the difference between whether
138-
// the consumer is using the hash location strategy or not, because `Location` normalizes
139-
// both `/#/foo/bar` and `/foo/bar` to the same thing.
141+
// because named route URLs can contain parentheses (see #12338). Also we don't use `Location`
142+
// since we can't tell the difference between whether the consumer is using the hash location
143+
// strategy or not, because `Location` normalizes both `/#/foo/bar` and `/foo/bar` to
144+
// the same thing.
140145
const path = location ? location.getPathname().split('#')[0] : '';
141146
this._rectangleFillValue = `url('${path}#${this.progressbarId}')`;
142147
this._isNoopAnimation = _animationMode === 'NoopAnimations';
@@ -158,13 +163,21 @@ export class MatProgressBar extends _MatProgressBarBase implements CanColor,
158163
get value(): number { return this._value; }
159164
set value(v: number) {
160165
this._value = clamp(coerceNumberProperty(v) || 0);
166+
167+
// @breaking-change 11.0.0 Remove null check for _changeDetectorRef.
168+
this._changeDetectorRef?.markForCheck();
161169
}
162170
private _value: number = 0;
163171

164172
/** Buffer value of the progress bar. Defaults to zero. */
165173
@Input()
166174
get bufferValue(): number { return this._bufferValue; }
167-
set bufferValue(v: number) { this._bufferValue = clamp(v || 0); }
175+
set bufferValue(v: number) {
176+
this._bufferValue = clamp(v || 0);
177+
178+
// @breaking-change 11.0.0 Remove null check for _changeDetectorRef.
179+
this._changeDetectorRef?.markForCheck();
180+
}
168181
private _bufferValue: number = 0;
169182

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

tools/public_api_guard/material/progress-bar.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import { AbstractConstructor } from '@angular/material/core/common-behaviors/constructor';
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/common-behaviors/constructor';
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;
@@ -60,7 +62,7 @@ export class MatProgressBar extends _MatProgressBarBase implements CanColor, Aft
6062
// (undocumented)
6163
static ɵcmp: i0.ɵɵComponentDeclaration<MatProgressBar, "mat-progress-bar", ["matProgressBar"], { "color": "color"; "value": "value"; "bufferValue": "bufferValue"; "mode": "mode"; }, { "animationEnd": "animationEnd"; }, never, never>;
6264
// (undocumented)
63-
static ɵfac: i0.ɵɵFactoryDeclaration<MatProgressBar, [null, null, { optional: true; }, { optional: true; }, { optional: true; }]>;
65+
static ɵfac: i0.ɵɵFactoryDeclaration<MatProgressBar, [null, null, { optional: true; }, { optional: true; }, { optional: true; }, null]>;
6466
}
6567

6668
// @public

0 commit comments

Comments
 (0)