Skip to content

Commit ca30f42

Browse files
arturovtandrewseguin
authored andcommitted
perf(material/progress-bar): do not run change detection if there are no animationEnd listeners (#24673)
(cherry picked from commit b1d20f9)
1 parent b83d225 commit ca30f42

File tree

4 files changed

+90
-20
lines changed

4 files changed

+90
-20
lines changed

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

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {TestBed, ComponentFixture} from '@angular/core/testing';
2-
import {Component, DebugElement, Provider, Type} from '@angular/core';
2+
import {ApplicationRef, Component, DebugElement, Provider, Type} from '@angular/core';
33
import {By} from '@angular/platform-browser';
44
import {dispatchFakeEvent} from '../../cdk/testing/private';
55
import {MatProgressBarModule, MAT_PROGRESS_BAR_DEFAULT_OPTIONS} from './index';
@@ -231,14 +231,16 @@ describe('MDC-based MatProgressBar', () => {
231231

232232
it('should trigger output event on primary value bar animation end', () => {
233233
fixture.detectChanges();
234-
spyOn(progressComponent.animationEnd, 'next');
234+
235+
const animationEndSpy = jasmine.createSpy();
236+
progressComponent.animationEnd.subscribe(animationEndSpy);
235237

236238
progressComponent.value = 40;
237-
expect(progressComponent.animationEnd.next).not.toHaveBeenCalled();
239+
expect(animationEndSpy).not.toHaveBeenCalled();
238240

239241
// On animation end, output should be emitted.
240242
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend');
241-
expect(progressComponent.animationEnd.next).toHaveBeenCalledWith({value: 40});
243+
expect(animationEndSpy).toHaveBeenCalledWith({value: 40});
242244
});
243245
});
244246

@@ -267,27 +269,56 @@ describe('MDC-based MatProgressBar', () => {
267269

268270
it('should trigger output event on primary value bar animation end', () => {
269271
fixture.detectChanges();
270-
spyOn(progressComponent.animationEnd, 'next');
272+
273+
const animationEndSpy = jasmine.createSpy();
274+
progressComponent.animationEnd.subscribe(animationEndSpy);
271275

272276
progressComponent.value = 40;
273-
expect(progressComponent.animationEnd.next).not.toHaveBeenCalled();
277+
expect(animationEndSpy).not.toHaveBeenCalled();
274278

275279
// On animation end, output should be emitted.
276280
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend');
277-
expect(progressComponent.animationEnd.next).toHaveBeenCalledWith({value: 40});
281+
expect(animationEndSpy).toHaveBeenCalledWith({value: 40});
278282
});
279283

280284
it('should trigger output event with value not bufferValue', () => {
281285
fixture.detectChanges();
282-
spyOn(progressComponent.animationEnd, 'next');
286+
287+
const animationEndSpy = jasmine.createSpy();
288+
progressComponent.animationEnd.subscribe(animationEndSpy);
283289

284290
progressComponent.value = 40;
285291
progressComponent.bufferValue = 70;
286-
expect(progressComponent.animationEnd.next).not.toHaveBeenCalled();
292+
expect(animationEndSpy).not.toHaveBeenCalled();
287293

288294
// On animation end, output should be emitted.
289295
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend');
290-
expect(progressComponent.animationEnd.next).toHaveBeenCalledWith({value: 40});
296+
expect(animationEndSpy).toHaveBeenCalledWith({value: 40});
297+
});
298+
299+
it('should not run change detection if there are no `animationEnd` observers', () => {
300+
fixture.detectChanges();
301+
302+
const animationEndSpy = jasmine.createSpy();
303+
const appRef = TestBed.inject(ApplicationRef);
304+
spyOn(appRef, 'tick');
305+
306+
progressComponent.value = 30;
307+
progressComponent.bufferValue = 60;
308+
// On animation end, output should be emitted.
309+
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend');
310+
311+
expect(appRef.tick).not.toHaveBeenCalled();
312+
313+
progressComponent.animationEnd.subscribe(animationEndSpy);
314+
315+
progressComponent.value = 40;
316+
progressComponent.bufferValue = 70;
317+
// On animation end, output should be emitted.
318+
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend');
319+
320+
expect(appRef.tick).toHaveBeenCalled();
321+
expect(animationEndSpy).toHaveBeenCalledWith({value: 40});
291322
});
292323
});
293324
});

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ export class MatProgressBar
227227
)
228228
.pipe(filter((e: TransitionEvent) => e.target === this._primaryBar))
229229
.subscribe(() => {
230+
if (this.animationEnd.observers.length === 0) {
231+
return;
232+
}
233+
230234
if (this.mode === 'determinate' || this.mode === 'buffer') {
231235
this._ngZone.run(() => this.animationEnd.next({value: this.value}));
232236
}

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

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {TestBed, ComponentFixture} from '@angular/core/testing';
2-
import {Component, DebugElement, Provider, Type} from '@angular/core';
2+
import {ApplicationRef, Component, DebugElement, Provider, Type} from '@angular/core';
33
import {By} from '@angular/platform-browser';
44
import {dispatchFakeEvent} from '../../cdk/testing/private';
55
import {MatProgressBarModule, MAT_PROGRESS_BAR_LOCATION} from './index';
@@ -270,14 +270,16 @@ describe('MatProgressBar', () => {
270270

271271
it('should trigger output event on primary value bar animation end', () => {
272272
fixture.detectChanges();
273-
spyOn(progressComponent.animationEnd, 'next');
273+
274+
const animationEndSpy = jasmine.createSpy();
275+
progressComponent.animationEnd.subscribe(animationEndSpy);
274276

275277
progressComponent.value = 40;
276-
expect(progressComponent.animationEnd.next).not.toHaveBeenCalled();
278+
expect(animationEndSpy).not.toHaveBeenCalled();
277279

278280
// On animation end, output should be emitted.
279281
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend');
280-
expect(progressComponent.animationEnd.next).toHaveBeenCalledWith({value: 40});
282+
expect(animationEndSpy).toHaveBeenCalledWith({value: 40});
281283
});
282284
});
283285

@@ -306,27 +308,56 @@ describe('MatProgressBar', () => {
306308

307309
it('should trigger output event on primary value bar animation end', () => {
308310
fixture.detectChanges();
309-
spyOn(progressComponent.animationEnd, 'next');
311+
312+
const animationEndSpy = jasmine.createSpy();
313+
progressComponent.animationEnd.subscribe(animationEndSpy);
310314

311315
progressComponent.value = 40;
312-
expect(progressComponent.animationEnd.next).not.toHaveBeenCalled();
316+
expect(animationEndSpy).not.toHaveBeenCalled();
313317

314318
// On animation end, output should be emitted.
315319
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend');
316-
expect(progressComponent.animationEnd.next).toHaveBeenCalledWith({value: 40});
320+
expect(animationEndSpy).toHaveBeenCalledWith({value: 40});
317321
});
318322

319323
it('should trigger output event with value not bufferValue', () => {
320324
fixture.detectChanges();
321-
spyOn(progressComponent.animationEnd, 'next');
325+
326+
const animationEndSpy = jasmine.createSpy();
327+
progressComponent.animationEnd.subscribe(animationEndSpy);
322328

323329
progressComponent.value = 40;
324330
progressComponent.bufferValue = 70;
325-
expect(progressComponent.animationEnd.next).not.toHaveBeenCalled();
331+
expect(animationEndSpy).not.toHaveBeenCalled();
326332

327333
// On animation end, output should be emitted.
328334
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend');
329-
expect(progressComponent.animationEnd.next).toHaveBeenCalledWith({value: 40});
335+
expect(animationEndSpy).toHaveBeenCalledWith({value: 40});
336+
});
337+
338+
it('should not run change detection if there are no `animationEnd` observers', () => {
339+
fixture.detectChanges();
340+
341+
const animationEndSpy = jasmine.createSpy();
342+
const appRef = TestBed.inject(ApplicationRef);
343+
spyOn(appRef, 'tick');
344+
345+
progressComponent.value = 30;
346+
progressComponent.bufferValue = 60;
347+
// On animation end, output should be emitted.
348+
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend');
349+
350+
expect(appRef.tick).not.toHaveBeenCalled();
351+
352+
progressComponent.animationEnd.subscribe(animationEndSpy);
353+
354+
progressComponent.value = 40;
355+
progressComponent.bufferValue = 70;
356+
// On animation end, output should be emitted.
357+
dispatchFakeEvent(primaryValueBar.nativeElement, 'transitionend');
358+
359+
expect(appRef.tick).toHaveBeenCalled();
360+
expect(animationEndSpy).toHaveBeenCalledWith({value: 40});
330361
});
331362
});
332363
});

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ export class MatProgressBar
250250
)
251251
.pipe(filter((e: TransitionEvent) => e.target === element))
252252
.subscribe(() => {
253+
if (this.animationEnd.observers.length === 0) {
254+
return;
255+
}
256+
253257
if (this.mode === 'determinate' || this.mode === 'buffer') {
254258
this._ngZone.run(() => this.animationEnd.next({value: this.value}));
255259
}

0 commit comments

Comments
 (0)