Skip to content

Commit b011b45

Browse files
crisbetoandrewseguin
authored andcommitted
feat: remove hard dependency on @angular/http (#3792)
* Makes `@angular/http` an optional dependency since it is only being used by the icon module. An error will be thrown only if the user runs into something that requires the `HttpModule`. * Moves the various icon error classes into `icon-errors.ts`. * Moves the icon registry provider next to the registry itself, instead of in the same file as the icon directive. Fixes #2616.
1 parent 2481ee3 commit b011b45

File tree

7 files changed

+114
-63
lines changed

7 files changed

+114
-63
lines changed

src/lib/icon/fake-svgs.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import {
2-
Response,
3-
ResponseOptions} from '@angular/http';
1+
import {Response, ResponseOptions} from '@angular/http';
42

53
/**
64
* Fake URLs and associated SVG documents used by tests.

src/lib/icon/icon-errors.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import {MdError} from '../core';
2+
3+
/**
4+
* Exception thrown when attempting to load an icon with a name that cannot be found.
5+
* @docs-private
6+
*/
7+
export class MdIconNameNotFoundError extends MdError {
8+
constructor(iconName: string) {
9+
super(`Unable to find icon with the name "${iconName}"`);
10+
}
11+
}
12+
13+
/**
14+
* Exception thrown when attempting to load SVG content that does not contain the expected
15+
* <svg> tag.
16+
* @docs-private
17+
*/
18+
export class MdIconSvgTagNotFoundError extends MdError {
19+
constructor() {
20+
super('<svg> tag not found');
21+
}
22+
}
23+
24+
/**
25+
* Exception thrown when the consumer attempts to use `<md-icon>` without including @angular/http.
26+
* @docs-private
27+
*/
28+
export class MdIconNoHttpProviderError extends MdError {
29+
constructor() {
30+
super('Could not find Http provider for use with Angular Material icons. ' +
31+
'Please include the HttpModule from @angular/http in your app imports.');
32+
}
33+
}
34+
35+
/**
36+
* Exception thrown when an invalid icon name is passed to an md-icon component.
37+
* @docs-private
38+
*/
39+
export class MdIconInvalidNameError extends MdError {
40+
constructor(iconName: string) {
41+
super(`Invalid icon name: "${iconName}"`);
42+
}
43+
}

src/lib/icon/icon-registry.ts

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
import {Injectable, SecurityContext} from '@angular/core';
1+
import {Injectable, SecurityContext, Optional, SkipSelf} from '@angular/core';
22
import {SafeResourceUrl, DomSanitizer} from '@angular/platform-browser';
33
import {Http} from '@angular/http';
4-
import {MdError} from '../core';
54
import {Observable} from 'rxjs/Observable';
5+
import {
6+
MdIconNameNotFoundError,
7+
MdIconSvgTagNotFoundError,
8+
MdIconNoHttpProviderError,
9+
} from './icon-errors';
610
import 'rxjs/add/observable/forkJoin';
711
import 'rxjs/add/observable/of';
812
import 'rxjs/add/operator/map';
@@ -14,27 +18,6 @@ import 'rxjs/add/operator/catch';
1418
import 'rxjs/add/observable/throw';
1519

1620

17-
/**
18-
* Exception thrown when attempting to load an icon with a name that cannot be found.
19-
* @docs-private
20-
*/
21-
export class MdIconNameNotFoundError extends MdError {
22-
constructor(iconName: string) {
23-
super(`Unable to find icon with the name "${iconName}"`);
24-
}
25-
}
26-
27-
/**
28-
* Exception thrown when attempting to load SVG content that does not contain the expected
29-
* <svg> tag.
30-
* @docs-private
31-
*/
32-
export class MdIconSvgTagNotFoundError extends MdError {
33-
constructor() {
34-
super('<svg> tag not found');
35-
}
36-
}
37-
3821
/**
3922
* Configuration for an icon, including the URL and possibly the cached SVG element.
4023
* @docs-private
@@ -44,9 +27,6 @@ class SvgIconConfig {
4427
constructor(public url: SafeResourceUrl) { }
4528
}
4629

47-
/** Returns the cache key to use for an icon namespace and name. */
48-
const iconKey = (namespace: string, name: string) => namespace + ':' + name;
49-
5030
/**
5131
* Service to register and display icons used by the <md-icon> component.
5232
* - Registers icon URLs by namespace and name.
@@ -83,7 +63,7 @@ export class MdIconRegistry {
8363
*/
8464
private _defaultFontSetClass = 'material-icons';
8565

86-
constructor(private _http: Http, private _sanitizer: DomSanitizer) {}
66+
constructor(@Optional() private _http: Http, private _sanitizer: DomSanitizer) {}
8767

8868
/**
8969
* Registers an icon by URL in the default namespace.
@@ -386,7 +366,11 @@ export class MdIconRegistry {
386366
* cached, so future calls with the same URL may not cause another HTTP request.
387367
*/
388368
private _fetchUrl(safeUrl: SafeResourceUrl): Observable<string> {
389-
let url = this._sanitizer.sanitize(SecurityContext.RESOURCE_URL, safeUrl);
369+
if (!this._http) {
370+
throw new MdIconNoHttpProviderError();
371+
}
372+
373+
const url = this._sanitizer.sanitize(SecurityContext.RESOURCE_URL, safeUrl);
390374

391375
// Store in-progress fetches to avoid sending a duplicate request for a URL when there is
392376
// already a request in progress for that URL. It's necessary to call share() on the
@@ -408,8 +392,24 @@ export class MdIconRegistry {
408392
}
409393
}
410394

395+
export function ICON_REGISTRY_PROVIDER_FACTORY(
396+
parentRegistry: MdIconRegistry, http: Http, sanitizer: DomSanitizer) {
397+
return parentRegistry || new MdIconRegistry(http, sanitizer);
398+
}
399+
400+
export const ICON_REGISTRY_PROVIDER = {
401+
// If there is already an MdIconRegistry available, use that. Otherwise, provide a new one.
402+
provide: MdIconRegistry,
403+
deps: [[new Optional(), new SkipSelf(), MdIconRegistry], [new Optional(), Http], DomSanitizer],
404+
useFactory: ICON_REGISTRY_PROVIDER_FACTORY
405+
};
411406

412407
/** Clones an SVGElement while preserving type information. */
413408
function cloneSvg(svg: SVGElement): SVGElement {
414409
return svg.cloneNode(true) as SVGElement;
415410
}
411+
412+
/** Returns the cache key to use for an icon namespace and name. */
413+
function iconKey(namespace: string, name: string) {
414+
return namespace + ':' + name;
415+
}

src/lib/icon/icon.spec.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import {inject, async, TestBed} from '@angular/core/testing';
22
import {SafeResourceUrl, DomSanitizer} from '@angular/platform-browser';
3-
import {XHRBackend} from '@angular/http';
3+
import {HttpModule, XHRBackend} from '@angular/http';
44
import {MockBackend} from '@angular/http/testing';
55
import {Component} from '@angular/core';
66
import {MdIconModule} from './index';
77
import {MdIconRegistry} from './icon-registry';
8+
import {MdIconNoHttpProviderError} from './icon-errors';
89
import {getFakeSvgHttpResponse} from './fake-svgs';
910

1011

@@ -36,7 +37,7 @@ describe('MdIcon', () => {
3637

3738
beforeEach(async(() => {
3839
TestBed.configureTestingModule({
39-
imports: [MdIconModule],
40+
imports: [HttpModule, MdIconModule],
4041
declarations: [
4142
MdIconColorTestApp,
4243
MdIconLigatureTestApp,
@@ -394,6 +395,37 @@ describe('MdIcon', () => {
394395
});
395396

396397

398+
describe('MdIcon without HttpModule', () => {
399+
let mdIconRegistry: MdIconRegistry;
400+
let sanitizer: DomSanitizer;
401+
402+
beforeEach(async(() => {
403+
TestBed.configureTestingModule({
404+
imports: [MdIconModule],
405+
declarations: [MdIconFromSvgNameTestApp],
406+
});
407+
408+
TestBed.compileComponents();
409+
}));
410+
411+
beforeEach(inject([MdIconRegistry, DomSanitizer], (mir: MdIconRegistry, ds: DomSanitizer) => {
412+
mdIconRegistry = mir;
413+
sanitizer = ds;
414+
}));
415+
416+
it('should throw an error when trying to load a remote icon', async() => {
417+
expect(() => {
418+
mdIconRegistry.addSvgIcon('fido', sanitizer.bypassSecurityTrustResourceUrl('dog.svg'));
419+
420+
let fixture = TestBed.createComponent(MdIconFromSvgNameTestApp);
421+
422+
fixture.componentInstance.iconName = 'fido';
423+
fixture.detectChanges();
424+
}).toThrowError(MdIconNoHttpProviderError);
425+
});
426+
});
427+
428+
397429
/** Test components that contain an MdIcon. */
398430
@Component({template: `<md-icon>{{iconName}}</md-icon>`})
399431
class MdIconLigatureTestApp {

src/lib/icon/icon.ts

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,10 @@ import {
99
SimpleChange,
1010
ViewEncapsulation,
1111
AfterViewChecked,
12-
Optional,
13-
SkipSelf,
1412
} from '@angular/core';
15-
import {Http} from '@angular/http';
16-
import {DomSanitizer} from '@angular/platform-browser';
17-
import {MdError} from '../core';
18-
import {MdIconRegistry, MdIconNameNotFoundError} from './icon-registry';
13+
import {MdIconRegistry} from './icon-registry';
14+
import {MdIconNameNotFoundError, MdIconInvalidNameError} from './icon-errors';
1915

20-
/** Exception thrown when an invalid icon name is passed to an md-icon component. */
21-
export class MdIconInvalidNameError extends MdError {
22-
constructor(iconName: string) {
23-
super(`Invalid icon name: "${iconName}"`);
24-
}
25-
}
2616

2717
/**
2818
* Component to display an icon. It can be used in the following ways:
@@ -250,15 +240,3 @@ export class MdIcon implements OnChanges, OnInit, AfterViewChecked {
250240
}
251241
}
252242
}
253-
254-
export function ICON_REGISTRY_PROVIDER_FACTORY(
255-
parentRegistry: MdIconRegistry, http: Http, sanitizer: DomSanitizer) {
256-
return parentRegistry || new MdIconRegistry(http, sanitizer);
257-
}
258-
259-
export const ICON_REGISTRY_PROVIDER = {
260-
// If there is already an MdIconRegistry available, use that. Otherwise, provide a new one.
261-
provide: MdIconRegistry,
262-
deps: [[new Optional(), new SkipSelf(), MdIconRegistry], Http, DomSanitizer],
263-
useFactory: ICON_REGISTRY_PROVIDER_FACTORY,
264-
};

src/lib/icon/index.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import {NgModule} from '@angular/core';
2-
import {HttpModule} from '@angular/http';
32
import {MdCommonModule} from '../core';
4-
import {MdIcon, ICON_REGISTRY_PROVIDER} from './icon';
3+
import {MdIcon} from './icon';
4+
import {ICON_REGISTRY_PROVIDER} from './icon-registry';
55

66

77
@NgModule({
8-
imports: [HttpModule, MdCommonModule],
8+
imports: [MdCommonModule],
99
exports: [MdIcon, MdCommonModule],
1010
declarations: [MdIcon],
1111
providers: [ICON_REGISTRY_PROVIDER],
@@ -14,4 +14,5 @@ export class MdIconModule {}
1414

1515

1616
export * from './icon';
17-
export {MdIconRegistry} from './icon-registry';
17+
export * from './icon-errors';
18+
export * from './icon-registry';

src/lib/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
"homepage": "https://github.com/angular/material2#readme",
2424
"peerDependencies": {
2525
"@angular/core": "^4.0.0",
26-
"@angular/common": "^4.0.0",
27-
"@angular/http": "^4.0.0"
26+
"@angular/common": "^4.0.0"
2827
}
2928
}

0 commit comments

Comments
 (0)