Skip to content

Commit 1207c42

Browse files
committed
refactor: switch to new approach without ReflectiveInjector
1 parent b13aff8 commit 1207c42

14 files changed

+146
-108
lines changed
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {Component} from '@angular/core';
2-
import {BlockScrollStrategy, ViewportRuler} from '@angular/material';
2+
import {ScrollStrategyOptions, ScrollStrategy} from '@angular/material';
33

44
@Component({
55
moduleId: module.id,
@@ -8,6 +8,6 @@ import {BlockScrollStrategy, ViewportRuler} from '@angular/material';
88
styleUrls: ['block-scroll-strategy-e2e.css'],
99
})
1010
export class BlockScrollStrategyE2E {
11-
constructor(private _viewportRuler: ViewportRuler) { }
12-
scrollStrategy = new BlockScrollStrategy(this._viewportRuler);
11+
constructor(private _scrollStrategyOptions: ScrollStrategyOptions) { }
12+
scrollStrategy: ScrollStrategy = this._scrollStrategyOptions.get('block');
1313
}

src/lib/core/overlay/overlay-ref.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ export class OverlayRef implements PortalHost {
2323
private _scrollStrategy: ScrollStrategy,
2424
private _ngZone: NgZone) {
2525

26-
_scrollStrategy.attach(this,
27-
typeof _state.scrollStrategy === 'string' ? null : _state.scrollStrategy.config);
26+
let scrollStrategyConfig = typeof _state.scrollStrategy === 'string' ?
27+
null :
28+
_state.scrollStrategy.config;
29+
30+
_scrollStrategy.attach(this, scrollStrategyConfig);
2831
}
2932

3033
/** The overlay's HTML element */

src/lib/core/overlay/overlay.spec.ts

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import {OverlayState} from './overlay-state';
88
import {OverlayRef} from './overlay-ref';
99
import {PositionStrategy} from './position/position-strategy';
1010
import {OverlayModule} from './overlay-directives';
11-
import {ScrollStrategy} from './scroll/scroll-strategy';
11+
import {ScrollStrategy, ScrollStrategyOptions, ScrollDispatcher} from './scroll/index';
12+
import {ViewportRuler} from './position/viewport-ruler';
1213

1314

1415
describe('Overlay', () => {
@@ -22,10 +23,20 @@ describe('Overlay', () => {
2223
TestBed.configureTestingModule({
2324
imports: [OverlayModule, PortalModule, OverlayTestModule],
2425
providers: [
25-
{provide: OverlayContainer, useFactory: () => {
26-
overlayContainerElement = document.createElement('div');
27-
return {getContainerElement: () => overlayContainerElement};
28-
}}
26+
ScrollDispatcher,
27+
ViewportRuler,
28+
{
29+
provide: ScrollStrategyOptions,
30+
useClass: ScrollStrategyOptionsOverride,
31+
deps: [ScrollDispatcher, ViewportRuler]
32+
},
33+
{
34+
provide: OverlayContainer,
35+
useFactory: () => {
36+
overlayContainerElement = document.createElement('div');
37+
return {getContainerElement: () => overlayContainerElement};
38+
}
39+
}
2940
]
3041
}).compileComponents();
3142
}));
@@ -337,51 +348,30 @@ describe('Overlay', () => {
337348
describe('scroll strategy', () => {
338349
let fakeScrollStrategy: FakeScrollStrategy;
339350
let config: OverlayState;
340-
341-
class FakeScrollStrategy implements ScrollStrategy {
342-
isEnabled = false;
343-
overlayRef: OverlayRef;
344-
345-
constructor() {
346-
fakeScrollStrategy = this;
347-
}
348-
349-
attach(overlayRef: OverlayRef) {
350-
this.overlayRef = overlayRef;
351-
}
352-
353-
enable() {
354-
this.isEnabled = true;
355-
}
356-
357-
disable() {
358-
this.isEnabled = false;
359-
}
360-
}
351+
let overlayRef: OverlayRef;
361352

362353
beforeEach(() => {
354+
});
355+
356+
beforeEach(inject([ScrollStrategyOptions], (scrollOptions: ScrollStrategyOptionsOverride) => {
363357
config = new OverlayState();
364-
overlay.registerScrollStrategy('fake', FakeScrollStrategy);
365358
config.scrollStrategy = 'fake';
366-
});
359+
overlayRef = overlay.create(config);
360+
fakeScrollStrategy =
361+
scrollOptions.instances[scrollOptions.instances.length - 1] as FakeScrollStrategy;
362+
}));
367363

368364
it('should attach the overlay ref to the scroll strategy', () => {
369-
let overlayRef = overlay.create(config);
370-
371365
expect(fakeScrollStrategy.overlayRef).toBe(overlayRef,
372366
'Expected scroll strategy to have been attached to the current overlay ref.');
373367
});
374368

375369
it('should enable the scroll strategy when the overlay is attached', () => {
376-
let overlayRef = overlay.create(config);
377-
378370
overlayRef.attach(componentPortal);
379371
expect(fakeScrollStrategy.isEnabled).toBe(true, 'Expected scroll strategy to be enabled.');
380372
});
381373

382374
it('should disable the scroll strategy once the overlay is detached', () => {
383-
let overlayRef = overlay.create(config);
384-
385375
overlayRef.attach(componentPortal);
386376
expect(fakeScrollStrategy.isEnabled).toBe(true, 'Expected scroll strategy to be enabled.');
387377

@@ -390,8 +380,6 @@ describe('Overlay', () => {
390380
});
391381

392382
it('should disable the scroll strategy when the overlay is destroyed', () => {
393-
let overlayRef = overlay.create(config);
394-
395383
overlayRef.dispose();
396384
expect(fakeScrollStrategy.isEnabled).toBe(false, 'Expected scroll strategy to be disabled.');
397385
});
@@ -469,3 +457,37 @@ class FakePositionStrategy implements PositionStrategy {
469457

470458
dispose() {}
471459
}
460+
461+
462+
class FakeScrollStrategy implements ScrollStrategy {
463+
isEnabled = false;
464+
overlayRef: OverlayRef;
465+
466+
attach(overlayRef: OverlayRef) {
467+
this.overlayRef = overlayRef;
468+
}
469+
470+
enable() {
471+
this.isEnabled = true;
472+
}
473+
474+
disable() {
475+
this.isEnabled = false;
476+
}
477+
}
478+
479+
480+
class ScrollStrategyOptionsOverride extends ScrollStrategyOptions {
481+
constructor(scrollDispatcher: ScrollDispatcher, viewportRuler: ViewportRuler) {
482+
super(scrollDispatcher, viewportRuler);
483+
}
484+
485+
// used for accessing the current instance in unit tests.
486+
public instances: ScrollStrategy[] = [];
487+
488+
get(strategy: string): ScrollStrategy {
489+
let instance = strategy === 'fake' ? new FakeScrollStrategy() : super.get(strategy);
490+
this.instances.push(instance);
491+
return instance;
492+
}
493+
}

src/lib/core/overlay/overlay.ts

Lines changed: 7 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,14 @@ import {
55
Injector,
66
NgZone,
77
Provider,
8-
ReflectiveInjector,
98
} from '@angular/core';
109
import {OverlayState} from './overlay-state';
1110
import {DomPortalHost} from '../portal/dom-portal-host';
1211
import {OverlayRef} from './overlay-ref';
1312
import {OverlayPositionBuilder} from './position/overlay-position-builder';
1413
import {VIEWPORT_RULER_PROVIDER} from './position/viewport-ruler';
1514
import {OverlayContainer, OVERLAY_CONTAINER_PROVIDER} from './overlay-container';
16-
import {ScrollStrategy} from './scroll/scroll-strategy';
17-
import {RepositionScrollStrategy} from './scroll/reposition-scroll-strategy';
18-
import {BlockScrollStrategy} from './scroll/block-scroll-strategy';
19-
import {CloseScrollStrategy} from './scroll/close-scroll-strategy';
20-
import {NoopScrollStrategy} from './scroll/noop-scroll-strategy';
15+
import {ScrollStrategy, ScrollStrategyOptions} from './scroll/index';
2116

2217

2318
/** Next overlay unique ID. */
@@ -37,19 +32,10 @@ let defaultState = new OverlayState();
3732
*/
3833
@Injectable()
3934
export class Overlay {
40-
// Create a child ReflectiveInjector, allowing us to instantiate scroll
41-
// strategies without going throught the injector cache.
42-
private _reflectiveInjector = ReflectiveInjector.resolveAndCreate([], this._injector);
43-
private _scrollStrategies = {
44-
reposition: RepositionScrollStrategy,
45-
block: BlockScrollStrategy,
46-
close: CloseScrollStrategy,
47-
noop: NoopScrollStrategy
48-
};
49-
5035
constructor(private _overlayContainer: OverlayContainer,
5136
private _componentFactoryResolver: ComponentFactoryResolver,
5237
private _positionBuilder: OverlayPositionBuilder,
38+
private _scrollStrategyOptions: ScrollStrategyOptions,
5339
private _appRef: ApplicationRef,
5440
private _injector: Injector,
5541
private _ngZone: NgZone) { }
@@ -71,17 +57,6 @@ export class Overlay {
7157
return this._positionBuilder;
7258
}
7359

74-
/**
75-
* Registers a scroll strategy to be available for use when creating an overlay.
76-
* @param name Name of the scroll strategy.
77-
* @param constructor Class to be used to instantiate the scroll strategy.
78-
*/
79-
registerScrollStrategy(name: string, constructor: Function): void {
80-
if (name && constructor) {
81-
this._scrollStrategies[name] = constructor;
82-
}
83-
}
84-
8560
/**
8661
* Creates the DOM element for an overlay and appends it to the overlay container.
8762
* @returns Newly-created pane element
@@ -111,29 +86,14 @@ export class Overlay {
11186
* @param state
11287
*/
11388
private _createOverlayRef(pane: HTMLElement, state: OverlayState): OverlayRef {
89+
let scrollStrategyName = typeof state.scrollStrategy === 'string' ?
90+
state.scrollStrategy :
91+
state.scrollStrategy.name;
92+
93+
let scrollStrategy = this._scrollStrategyOptions.get(scrollStrategyName);
11494
let portalHost = this._createPortalHost(pane);
115-
let scrollStrategy = this._createScrollStrategy(state);
11695
return new OverlayRef(portalHost, pane, state, scrollStrategy, this._ngZone);
11796
}
118-
119-
/**
120-
* Resolves the scroll strategy of an overlay state.
121-
* @param state State for which to resolve the scroll strategy.
122-
*/
123-
private _createScrollStrategy(state: OverlayState): ScrollStrategy {
124-
let strategyName = typeof state.scrollStrategy === 'string' ?
125-
state.scrollStrategy :
126-
state.scrollStrategy.name;
127-
128-
if (!this._scrollStrategies.hasOwnProperty(strategyName)) {
129-
throw new Error(`Unsupported scroll strategy "${strategyName}". The available scroll ` +
130-
`strategies are ${Object.keys(this._scrollStrategies).join(', ')}.`);
131-
}
132-
133-
// Note that we use `resolveAndInstantiate` which will instantiate
134-
// the scroll strategy without putting it in the injector cache.
135-
return this._reflectiveInjector.resolveAndInstantiate(this._scrollStrategies[strategyName]);
136-
}
13797
}
13898

13999
/** Providers for Overlay and its related injectables. */

src/lib/core/overlay/scroll/block-scroll-strategy.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
ComponentPortal,
55
OverlayModule,
66
PortalModule,
7-
BlockScrollStrategy,
87
Platform,
98
ViewportRuler,
109
OverlayState,

src/lib/core/overlay/scroll/block-scroll-strategy.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import {Injectable} from '@angular/core';
21
import {ScrollStrategy} from './scroll-strategy';
32
import {ViewportRuler} from '../position/viewport-ruler';
43

54
/**
65
* Strategy that will prevent the user from scrolling while the overlay is visible.
76
*/
8-
@Injectable()
97
export class BlockScrollStrategy implements ScrollStrategy {
108
private _previousHTMLStyles = { top: null, left: null };
119
private _previousScrollPosition: { top: number, left: number };

src/lib/core/overlay/scroll/close-scroll-strategy.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
OverlayModule,
1111
ScrollStrategy,
1212
ScrollDispatcher,
13-
CloseScrollStrategy,
1413
} from '../../core';
1514

1615

src/lib/core/overlay/scroll/close-scroll-strategy.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import {Injectable} from '@angular/core';
21
import {ScrollStrategy, getMdScrollStrategyAlreadyAttachedError} from './scroll-strategy';
32
import {OverlayRef} from '../overlay-ref';
43
import {Subscription} from 'rxjs/Subscription';
@@ -8,7 +7,6 @@ import {ScrollDispatcher} from './scroll-dispatcher';
87
/**
98
* Strategy that will close the overlay as soon as the user starts scrolling.
109
*/
11-
@Injectable()
1210
export class CloseScrollStrategy implements ScrollStrategy {
1311
private _scrollSubscription: Subscription|null = null;
1412
private _overlayRef: OverlayRef;

src/lib/core/overlay/scroll/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ import {NgModule} from '@angular/core';
22
import {SCROLL_DISPATCHER_PROVIDER} from './scroll-dispatcher';
33
import {Scrollable} from './scrollable';
44
import {PlatformModule} from '../../platform/index';
5+
import {ScrollStrategyOptions} from './scroll-strategy-options';
56

67
export {Scrollable} from './scrollable';
78
export {ScrollDispatcher} from './scroll-dispatcher';
89

910
// Export pre-defined scroll strategies and interface to build custom ones.
1011
export {ScrollStrategy} from './scroll-strategy';
12+
export {ScrollStrategyOptions} from './scroll-strategy-options';
1113
export {RepositionScrollStrategy} from './reposition-scroll-strategy';
1214
export {CloseScrollStrategy} from './close-scroll-strategy';
1315
export {NoopScrollStrategy} from './noop-scroll-strategy';
@@ -17,6 +19,6 @@ export {BlockScrollStrategy} from './block-scroll-strategy';
1719
imports: [PlatformModule],
1820
exports: [Scrollable],
1921
declarations: [Scrollable],
20-
providers: [SCROLL_DISPATCHER_PROVIDER],
22+
providers: [SCROLL_DISPATCHER_PROVIDER, ScrollStrategyOptions],
2123
})
2224
export class ScrollDispatchModule { }

src/lib/core/overlay/scroll/noop-scroll-strategy.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import {Injectable} from '@angular/core';
21
import {ScrollStrategy} from './scroll-strategy';
32

43
/**
54
* Scroll strategy that doesn't do anything.
65
*/
7-
@Injectable()
86
export class NoopScrollStrategy implements ScrollStrategy {
97
enable() { }
108
disable() { }

src/lib/core/overlay/scroll/reposition-scroll-strategy.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
OverlayModule,
1111
ScrollStrategy,
1212
ScrollDispatcher,
13-
RepositionScrollStrategy,
1413
} from '../../core';
1514

1615

src/lib/core/overlay/scroll/reposition-scroll-strategy.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import {Injectable} from '@angular/core';
21
import {Subscription} from 'rxjs/Subscription';
32
import {ScrollStrategy, getMdScrollStrategyAlreadyAttachedError} from './scroll-strategy';
43
import {OverlayRef} from '../overlay-ref';
@@ -14,7 +13,6 @@ export interface RepositionScrollStrategyConfig {
1413
/**
1514
* Strategy that will update the element position as the user is scrolling.
1615
*/
17-
@Injectable()
1816
export class RepositionScrollStrategy implements ScrollStrategy {
1917
private _scrollSubscription: Subscription|null = null;
2018
private _overlayRef: OverlayRef;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import {Injectable} from '@angular/core';
2+
import {ScrollStrategy} from './scroll-strategy';
3+
import {RepositionScrollStrategy} from './reposition-scroll-strategy';
4+
import {CloseScrollStrategy} from './close-scroll-strategy';
5+
import {NoopScrollStrategy} from './noop-scroll-strategy';
6+
import {BlockScrollStrategy} from './block-scroll-strategy';
7+
import {ScrollDispatcher} from './scroll-dispatcher';
8+
import {ViewportRuler} from '../position/viewport-ruler';
9+
10+
11+
/**
12+
* Factory that instantiates scroll strategies. Provides the built-in `reposition`, `close`,
13+
* `noop` and `block` strategies by default.
14+
*/
15+
@Injectable()
16+
export class ScrollStrategyOptions {
17+
constructor(
18+
private _scrollDispatcher: ScrollDispatcher,
19+
private _viewportRuler: ViewportRuler) { }
20+
21+
get(strategy: string): ScrollStrategy {
22+
switch (strategy) {
23+
case 'reposition': return new RepositionScrollStrategy(this._scrollDispatcher);
24+
case 'close': return new CloseScrollStrategy(this._scrollDispatcher);
25+
case 'noop': return new NoopScrollStrategy();
26+
case 'block': return new BlockScrollStrategy(this._viewportRuler);
27+
}
28+
29+
throw new Error(`Unsupported scroll strategy "${strategy}".`);
30+
}
31+
}

0 commit comments

Comments
 (0)