From 386233761c999cf5f66dc7e5de980ffe3b97e992 Mon Sep 17 00:00:00 2001 From: Jeremy Mowery Date: Tue, 3 Sep 2019 21:31:50 +0000 Subject: [PATCH] Use the ErrorHandler to log errors in MatIcon. Currently a console.log is used. This change switches to using ErrorHandler. ErrorHandler is better because it allows the user of MatIcon to define how Errors should be handled instead of relying on the console. Update API goldens --- src/material/icon/icon.spec.ts | 27 ++++++++++++++- src/material/icon/icon.ts | 42 ++++++++++++++--------- tools/public_api_guard/material/icon.d.ts | 2 +- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/material/icon/icon.spec.ts b/src/material/icon/icon.spec.ts index 9fc58199afa9..54ff5a5efaf1 100644 --- a/src/material/icon/icon.spec.ts +++ b/src/material/icon/icon.spec.ts @@ -278,10 +278,35 @@ describe('MatIcon', () => { http.expectOne('farm-set-1.svg').error(new ErrorEvent('Network error')); fixture.detectChanges(); - expect(errorHandler.handleError).toHaveBeenCalledTimes(1); + // Called twice once for the HTTP request failing and once for the icon + // then not being able to be found. + expect(errorHandler.handleError).toHaveBeenCalledTimes(2); expect(errorHandler.handleError.calls.argsFor(0)[0].message).toEqual( 'Loading icon set URL: farm-set-1.svg failed: Http failure response ' + 'for farm-set-1.svg: 0 '); + expect(errorHandler.handleError.calls.argsFor(1)[0].message) + .toEqual( + `Error retrieving icon ${testComponent.iconName}! ` + + 'Unable to find icon with the name "pig"'); + }); + + it('should delegate an error getting an SVG icon to the ErrorHandler', () => { + iconRegistry.addSvgIconSetInNamespace('farm', trustUrl('farm-set-1.svg')); + + const fixture = TestBed.createComponent(IconFromSvgName); + const testComponent = fixture.componentInstance; + + testComponent.iconName = 'farm:DNE'; + fixture.detectChanges(); + http.expectOne('farm-set-1.svg').flush(FAKE_SVGS.farmSet1); + fixture.detectChanges(); + + // The HTTP request succeeded but the icon was not found so we logged. + expect(errorHandler.handleError).toHaveBeenCalledTimes(1); + expect(errorHandler.handleError.calls.argsFor(0)[0].message) + .toEqual( + `Error retrieving icon ${testComponent.iconName}! ` + + 'Unable to find icon with the name "DNE"'); }); it('should extract icon from SVG icon set', () => { diff --git a/src/material/icon/icon.ts b/src/material/icon/icon.ts index f996381107f4..167ff107d698 100644 --- a/src/material/icon/icon.ts +++ b/src/material/icon/icon.ts @@ -6,27 +6,29 @@ * found in the LICENSE file at https://angular.io/license */ -import {take} from 'rxjs/operators'; +import {coerceBooleanProperty} from '@angular/cdk/coercion'; +import {DOCUMENT} from '@angular/common'; import { + AfterViewChecked, Attribute, ChangeDetectionStrategy, Component, ElementRef, + ErrorHandler, + inject, + Inject, + InjectionToken, Input, OnChanges, + OnDestroy, OnInit, + Optional, SimpleChanges, ViewEncapsulation, - Optional, - InjectionToken, - inject, - Inject, - OnDestroy, - AfterViewChecked, } from '@angular/core'; -import {DOCUMENT} from '@angular/common'; import {CanColor, CanColorCtor, mixinColor} from '@angular/material/core'; -import {coerceBooleanProperty} from '@angular/cdk/coercion'; +import {take} from 'rxjs/operators'; + import {MatIconRegistry} from './icon-registry'; @@ -178,14 +180,15 @@ export class MatIcon extends _MatIconMixinBase implements OnChanges, OnInit, Aft private _elementsWithExternalReferences?: Map; constructor( - elementRef: ElementRef, - private _iconRegistry: MatIconRegistry, + elementRef: ElementRef, private _iconRegistry: MatIconRegistry, @Attribute('aria-hidden') ariaHidden: string, /** * @deprecated `location` parameter to be made required. * @breaking-change 8.0.0 */ - @Optional() @Inject(MAT_ICON_LOCATION) private _location?: MatIconLocation) { + @Optional() @Inject(MAT_ICON_LOCATION) private _location?: MatIconLocation, + // @breaking-change 9.0.0 _errorHandler parameter to be made required + @Optional() private readonly _errorHandler?: ErrorHandler) { super(elementRef); // If the user has not explicitly set aria-hidden, mark the icon as hidden, as this is @@ -228,10 +231,17 @@ export class MatIcon extends _MatIconMixinBase implements OnChanges, OnInit, Aft if (this.svgIcon) { const [namespace, iconName] = this._splitIconName(this.svgIcon); - this._iconRegistry.getNamedSvgIcon(iconName, namespace).pipe(take(1)).subscribe( - svg => this._setSvgElement(svg), - (err: Error) => console.log(`Error retrieving icon: ${err.message}`) - ); + this._iconRegistry.getNamedSvgIcon(iconName, namespace) + .pipe(take(1)) + .subscribe(svg => this._setSvgElement(svg), (err: Error) => { + const errorMessage = `Error retrieving icon ${namespace}:${iconName}! ${err.message}`; + // @breaking-change 9.0.0 _errorHandler parameter to be made required. + if (this._errorHandler) { + this._errorHandler.handleError(new Error(errorMessage)); + } else { + console.error(errorMessage); + } + }); } else if (svgIconChanges.previousValue) { this._clearSvgElement(); } diff --git a/tools/public_api_guard/material/icon.d.ts b/tools/public_api_guard/material/icon.d.ts index 60743fb8e9d1..666e5f50f32b 100644 --- a/tools/public_api_guard/material/icon.d.ts +++ b/tools/public_api_guard/material/icon.d.ts @@ -28,7 +28,7 @@ export declare class MatIcon extends _MatIconMixinBase implements OnChanges, OnI inline: boolean; svgIcon: string; constructor(elementRef: ElementRef, _iconRegistry: MatIconRegistry, ariaHidden: string, - _location?: MatIconLocation | undefined); + _location?: MatIconLocation | undefined, _errorHandler?: ErrorHandler | undefined); ngAfterViewChecked(): void; ngOnChanges(changes: SimpleChanges): void; ngOnDestroy(): void;