Skip to content

Commit cf9b8ab

Browse files
crisbetojelbourn
authored andcommitted
refactor: use strongly-typed ElementRef (#10825)
Currently the `nativeElement` in all of the `ElementRef` usages is typed to be `any`. These changes add proper typing to help us catch some errors at compile time.
1 parent 7e67fe9 commit cf9b8ab

File tree

68 files changed

+143
-128
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+143
-128
lines changed

guides/creating-a-custom-form-field-control.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,31 +159,31 @@ private _placeholder: string;
159159

160160
#### `ngControl`
161161

162-
This property allows the form field control to specify the `@angular/forms` control that is bound to this component. Since we haven't set up our component to act as a `ControlValueAccessor`, we'll just set this to `null` in our component.
162+
This property allows the form field control to specify the `@angular/forms` control that is bound to this component. Since we haven't set up our component to act as a `ControlValueAccessor`, we'll just set this to `null` in our component.
163163

164164
```ts
165165
ngControl: NgControl = null;
166166
```
167167

168-
It is likely you will want to implement `ControlValueAccessor` so that your component can work with `formControl` and `ngModel`. If you do implement `ControlValueAccessor` you will need to get a reference to the `NgControl` associated with your control and make it publicly available.
168+
It is likely you will want to implement `ControlValueAccessor` so that your component can work with `formControl` and `ngModel`. If you do implement `ControlValueAccessor` you will need to get a reference to the `NgControl` associated with your control and make it publicly available.
169169

170170
The easy way is to add it as a public property to your constructor and let dependency injection handle it:
171171

172172
```ts
173173
constructor(
174-
...,
174+
...,
175175
@Optional() @Self() public ngControl: NgControl,
176176
...,
177177
) { }
178178
```
179179

180-
Note that if your component implements `ControlValueAccessor`, it may already be set up to provide `NG_VALUE_ACCESSOR` (in the `providers` part of the component's decorator, or possibly in a module declaration). If so you may get a *cannot instantiate cyclic dependency* error.
180+
Note that if your component implements `ControlValueAccessor`, it may already be set up to provide `NG_VALUE_ACCESSOR` (in the `providers` part of the component's decorator, or possibly in a module declaration). If so you may get a *cannot instantiate cyclic dependency* error.
181181

182182
To resolve this, remove the `NG_VALUE_ACCESSOR` provider and instead set the value accessor directly:
183183

184184
```ts
185185
constructor(
186-
...,
186+
...,
187187
@Optional() @Self() public ngControl: NgControl,
188188
...,
189189
) {
@@ -207,7 +207,7 @@ need to remember to emit on the `stateChanges` stream so change detection can ha
207207
```ts
208208
focused = false;
209209

210-
constructor(fb: FormBuilder, private fm: FocusMonitor, private elRef: ElementRef) {
210+
constructor(fb: FormBuilder, private fm: FocusMonitor, private elRef: ElementRef<HTMLElement>) {
211211
...
212212
fm.monitor(elRef.nativeElement, true).subscribe(origin => {
213213
this.focused = !!origin;

src/cdk-experimental/dialog/dialog-container.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export class CdkDialogContainer extends BasePortalOutlet implements OnDestroy {
104104
_afterExit: Subject<void> = new Subject();
105105

106106
constructor(
107-
private _elementRef: ElementRef,
107+
private _elementRef: ElementRef<HTMLElement>,
108108
private _focusTrapFactory: FocusTrapFactory,
109109
private _changeDetectorRef: ChangeDetectorRef,
110110
@Optional() @Inject(DOCUMENT) private _document: any,

src/cdk/a11y/aria-describer/aria-describer.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,16 +181,16 @@ function expectMessage(el: Element, message: string) {
181181
`,
182182
})
183183
class TestApp {
184-
@ViewChild('element1') _element1: ElementRef;
184+
@ViewChild('element1') _element1: ElementRef<HTMLElement>;
185185
get element1(): Element { return this._element1.nativeElement; }
186186

187-
@ViewChild('element2') _element2: ElementRef;
187+
@ViewChild('element2') _element2: ElementRef<HTMLElement>;
188188
get element2(): Element { return this._element2.nativeElement; }
189189

190-
@ViewChild('element3') _element3: ElementRef;
190+
@ViewChild('element3') _element3: ElementRef<HTMLElement>;
191191
get element3(): Element { return this._element3.nativeElement; }
192192

193-
@ViewChild('element4') _element4: ElementRef;
193+
@ViewChild('element4') _element4: ElementRef<HTMLElement>;
194194
get element4(): Element { return this._element4.nativeElement; }
195195

196196

src/cdk/a11y/focus-monitor/focus-monitor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ export class CdkMonitorFocus implements OnDestroy {
417417
private _monitorSubscription: Subscription;
418418
@Output() cdkFocusChange = new EventEmitter<FocusOrigin>();
419419

420-
constructor(private _elementRef: ElementRef, private _focusMonitor: FocusMonitor) {
420+
constructor(private _elementRef: ElementRef<HTMLElement>, private _focusMonitor: FocusMonitor) {
421421
this._monitorSubscription = this._focusMonitor.monitor(
422422
this._elementRef,
423423
this._elementRef.nativeElement.hasAttribute('cdkMonitorSubtreeFocus'))

src/cdk/a11y/focus-trap/focus-trap.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ export class CdkTrapFocus implements OnDestroy, AfterContentInit, DoCheck {
350350
private _autoCapture: boolean;
351351

352352
constructor(
353-
private _elementRef: ElementRef,
353+
private _elementRef: ElementRef<HTMLElement>,
354354
private _focusTrapFactory: FocusTrapFactory,
355355
@Inject(DOCUMENT) _document: any) {
356356

src/cdk/observers/observe-content.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ export class CdkObserveContent implements AfterContentInit, OnDestroy {
164164

165165
private _currentSubscription: Subscription | null = null;
166166

167-
constructor(private _contentObserver: ContentObserver, private _elementRef: ElementRef,
167+
constructor(private _contentObserver: ContentObserver,
168+
private _elementRef: ElementRef<HTMLElement>,
168169
private _ngZone: NgZone) {}
169170

170171
ngAfterContentInit() {

src/cdk/overlay/position/connected-position-strategy.spec.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ describe('ConnectedPositionStrategy', () => {
6363

6464
let originElement: HTMLElement;
6565
let positionStrategy: ConnectedPositionStrategy;
66-
let fakeElementRef: ElementRef;
66+
let fakeElementRef: ElementRef<HTMLElement>;
6767

6868
let originRect: ClientRect | null;
6969
let originCenterX: number | null;
@@ -73,7 +73,7 @@ describe('ConnectedPositionStrategy', () => {
7373
// The origin and overlay elements need to be in the document body in order to have geometry.
7474
originElement = createPositionedBlockElement();
7575
document.body.appendChild(originElement);
76-
fakeElementRef = new ElementRef(originElement);
76+
fakeElementRef = new ElementRef<HTMLElement>(originElement);
7777
});
7878

7979
afterEach(() => {
@@ -583,7 +583,7 @@ describe('ConnectedPositionStrategy', () => {
583583
let positionChangeHandler: jasmine.Spy;
584584
let onPositionChangeSubscription: Subscription;
585585
let positionChange: ConnectedOverlayPositionChange;
586-
let fakeElementRef: ElementRef;
586+
let fakeElementRef: ElementRef<HTMLElement>;
587587
let positionStrategy: ConnectedPositionStrategy;
588588

589589
beforeEach(() => {
@@ -597,14 +597,14 @@ describe('ConnectedPositionStrategy', () => {
597597
scrollable.appendChild(originElement);
598598

599599
// Create a strategy with knowledge of the scrollable container
600-
fakeElementRef = new ElementRef(originElement);
600+
fakeElementRef = new ElementRef<HTMLElement>(originElement);
601601
positionStrategy = overlay.position().connectedTo(
602602
fakeElementRef,
603603
{originX: 'start', originY: 'bottom'},
604604
{overlayX: 'start', overlayY: 'top'});
605605

606606
positionStrategy.withScrollableContainers([
607-
new CdkScrollable(new ElementRef(scrollable), null!, null!)]);
607+
new CdkScrollable(new ElementRef<HTMLElement>(scrollable), null!, null!)]);
608608
positionChangeHandler = jasmine.createSpy('positionChangeHandler');
609609
onPositionChangeSubscription =
610610
positionStrategy.onPositionChange.subscribe(positionChangeHandler);
@@ -673,13 +673,13 @@ describe('ConnectedPositionStrategy', () => {
673673
describe('positioning properties', () => {
674674
let originElement: HTMLElement;
675675
let positionStrategy: ConnectedPositionStrategy;
676-
let fakeElementRef: ElementRef;
676+
let fakeElementRef: ElementRef<HTMLElement>;
677677

678678
beforeEach(() => {
679679
// The origin and overlay elements need to be in the document body in order to have geometry.
680680
originElement = createPositionedBlockElement();
681681
document.body.appendChild(originElement);
682-
fakeElementRef = new ElementRef(originElement);
682+
fakeElementRef = new ElementRef<HTMLElement>(originElement);
683683
});
684684

685685
afterEach(() => {

src/cdk/overlay/position/connected-position-strategy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class ConnectedPositionStrategy implements PositionStrategy {
5858
constructor(
5959
originPos: OriginConnectionPosition,
6060
overlayPos: OverlayConnectionPosition,
61-
connectedTo: ElementRef,
61+
connectedTo: ElementRef<HTMLElement>,
6262
viewportRuler: ViewportRuler,
6363
document: Document,
6464
// @breaking-change 7.0.0 `platform` parameter to be made required.

src/cdk/overlay/position/flexible-connected-position-strategy.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1575,7 +1575,7 @@ describe('FlexibleConnectedPositionStrategy', () => {
15751575
}]);
15761576

15771577
strategy.withScrollableContainers([
1578-
new CdkScrollable(new ElementRef(scrollable), null!, null!)
1578+
new CdkScrollable(new ElementRef<HTMLElement>(scrollable), null!, null!)
15791579
]);
15801580

15811581
positionChangeHandler = jasmine.createSpy('positionChange handler');

src/cdk/scrolling/scroll-dispatcher.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ describe('ScrollDispatcher', () => {
117117
describe('Nested scrollables', () => {
118118
let scroll: ScrollDispatcher;
119119
let fixture: ComponentFixture<NestedScrollingComponent>;
120-
let element: ElementRef;
120+
let element: ElementRef<HTMLElement>;
121121

122122
beforeEach(inject([ScrollDispatcher], (s: ScrollDispatcher) => {
123123
scroll = s;
@@ -228,7 +228,7 @@ describe('ScrollDispatcher', () => {
228228
})
229229
class ScrollingComponent {
230230
@ViewChild(CdkScrollable) scrollable: CdkScrollable;
231-
@ViewChild('scrollingElement') scrollingElement: ElementRef;
231+
@ViewChild('scrollingElement') scrollingElement: ElementRef<HTMLElement>;
232232
}
233233

234234

@@ -245,7 +245,7 @@ class ScrollingComponent {
245245
`
246246
})
247247
class NestedScrollingComponent {
248-
@ViewChild('interestingElement') interestingElement: ElementRef;
248+
@ViewChild('interestingElement') interestingElement: ElementRef<HTMLElement>;
249249
}
250250

251251
const TEST_COMPONENTS = [ScrollingComponent, NestedScrollingComponent];

src/cdk/scrolling/scroll-dispatcher.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,14 @@ export class ScrollDispatcher implements OnDestroy {
141141

142142
/** Returns true if the element is contained within the provided Scrollable. */
143143
private _scrollableContainsElement(scrollable: CdkScrollable, elementRef: ElementRef): boolean {
144-
let element = elementRef.nativeElement;
144+
let element: HTMLElement | null = elementRef.nativeElement;
145145
let scrollableElement = scrollable.getElementRef().nativeElement;
146146

147147
// Traverse through the element parents until we reach null, checking if any of the elements
148148
// are the scrollable's element.
149149
do {
150150
if (element == scrollableElement) { return true; }
151-
} while (element = element.parentElement);
151+
} while (element = element!.parentElement);
152152

153153
return false;
154154
}

src/cdk/table/table.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ export interface RowOutlet {
7070
*/
7171
@Directive({selector: '[rowOutlet]'})
7272
export class DataRowOutlet implements RowOutlet {
73-
constructor(public viewContainer: ViewContainerRef, public elementRef: ElementRef) { }
73+
constructor(public viewContainer: ViewContainerRef,
74+
public elementRef: ElementRef) { }
7475
}
7576

7677
/**
@@ -79,7 +80,8 @@ export class DataRowOutlet implements RowOutlet {
7980
*/
8081
@Directive({selector: '[headerRowOutlet]'})
8182
export class HeaderRowOutlet implements RowOutlet {
82-
constructor(public viewContainer: ViewContainerRef, public elementRef: ElementRef) { }
83+
constructor(public viewContainer: ViewContainerRef,
84+
public elementRef: ElementRef) { }
8385
}
8486

8587
/**
@@ -88,7 +90,8 @@ export class HeaderRowOutlet implements RowOutlet {
8890
*/
8991
@Directive({selector: '[footerRowOutlet]'})
9092
export class FooterRowOutlet implements RowOutlet {
91-
constructor(public viewContainer: ViewContainerRef, public elementRef: ElementRef) { }
93+
constructor(public viewContainer: ViewContainerRef,
94+
public elementRef: ElementRef) { }
9295
}
9396

9497
/**

src/cdk/text-field/autofill.spec.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,14 +216,16 @@ describe('cdkAutofill', () => {
216216
`
217217
})
218218
class Inputs {
219-
@ViewChild('input1') input1: ElementRef;
220-
@ViewChild('input2') input2: ElementRef;
221-
@ViewChild('input3') input3: ElementRef;
219+
// Cast to `any` so we can stub out some methods in the tests.
220+
@ViewChild('input1') input1: ElementRef<any>;
221+
@ViewChild('input2') input2: ElementRef<any>;
222+
@ViewChild('input3') input3: ElementRef<any>;
222223
}
223224

224225
@Component({
225226
template: `<input #input cdkAutofill>`
226227
})
227228
class InputWithCdkAutofilled {
228-
@ViewChild('input') input: ElementRef;
229+
// Cast to `any` so we can stub out some methods in the tests.
230+
@ViewChild('input') input: ElementRef<any>;
229231
}

src/cdk/text-field/autofill.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ export class CdkAutofill implements OnDestroy, OnInit {
148148
/** Emits when the autofill state of the element changes. */
149149
@Output() cdkAutofill: EventEmitter<AutofillEvent> = new EventEmitter<AutofillEvent>();
150150

151-
constructor(private _elementRef: ElementRef, private _autofillMonitor: AutofillMonitor) {}
151+
constructor(private _elementRef: ElementRef<HTMLElement>,
152+
private _autofillMonitor: AutofillMonitor) {}
152153

153154
ngOnInit() {
154155
this._autofillMonitor

src/cdk/text-field/autosize.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export class CdkTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy {
7878
private _cachedLineHeight: number;
7979

8080
constructor(
81-
private _elementRef: ElementRef,
81+
private _elementRef: ElementRef<HTMLElement>,
8282
private _platform: Platform,
8383
private _ngZone: NgZone) {
8484
this._textareaElement = this._elementRef.nativeElement as HTMLTextAreaElement;

src/cdk/tree/nested-node.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export class CdkNestedTreeNode<T> extends CdkTreeNode<T> implements AfterContent
6363
/** The children node placeholder. */
6464
@ContentChildren(CdkTreeNodeOutlet) nodeOutlet: QueryList<CdkTreeNodeOutlet>;
6565

66-
constructor(protected _elementRef: ElementRef,
66+
constructor(protected _elementRef: ElementRef<HTMLElement>,
6767
protected _tree: CdkTree<T>,
6868
protected _differs: IterableDiffers) {
6969
super(_elementRef, _tree);

src/cdk/tree/padding.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export class CdkTreeNodePadding<T> implements OnDestroy {
4646
constructor(private _treeNode: CdkTreeNode<T>,
4747
private _tree: CdkTree<T>,
4848
private _renderer: Renderer2,
49-
private _element: ElementRef,
49+
private _element: ElementRef<HTMLElement>,
5050
@Optional() private _dir: Directionality) {
5151
this._setPadding();
5252
if (this._dir) {

src/cdk/tree/tree.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ export class CdkTreeNode<T> implements FocusableOption, OnDestroy {
323323
*/
324324
@Input() role: 'treeitem' | 'group' = 'treeitem';
325325

326-
constructor(protected _elementRef: ElementRef,
326+
constructor(protected _elementRef: ElementRef<HTMLElement>,
327327
protected _tree: CdkTree<T>) {
328328
CdkTreeNode.mostRecentTreeNode = this as CdkTreeNode<T>;
329329
}

src/demo-app/a11y/a11y.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ export class AccessibilityDemo implements OnDestroy {
3131

3232
private _routerSubscription = Subscription.EMPTY;
3333

34-
@ViewChild('maincontent') mainContent: ElementRef;
35-
@ViewChild('header') sectionHeader: ElementRef;
34+
@ViewChild('maincontent') mainContent: ElementRef<HTMLElement>;
35+
@ViewChild('header') sectionHeader: ElementRef<HTMLElement>;
3636

3737
navItems = [
3838
{name: 'Home', route: '.'},

src/demo-app/demo-app/demo-app.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,12 @@ export class DemoApp {
9696
];
9797

9898
constructor(
99-
private _element: ElementRef,
99+
private _element: ElementRef<HTMLElement>,
100100
private _overlayContainer: OverlayContainer) {}
101101

102102
toggleFullscreen() {
103-
const elem = this._element.nativeElement.querySelector('.demo-content');
103+
// Cast to `any`, because the typings don't include the browser-prefixed methods.
104+
const elem = this._element.nativeElement.querySelector('.demo-content') as any;
104105
if (elem.requestFullscreen) {
105106
elem.requestFullscreen();
106107
} else if (elem.webkitRequestFullScreen) {

src/e2e-app/fullscreen/fullscreen-e2e.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export class FullscreenE2E {
1010

1111
dialogRef: MatDialogRef<TestDialogFullScreen> | null;
1212

13-
constructor (private _element: ElementRef, private _dialog: MatDialog) { }
13+
constructor (private _element: ElementRef<HTMLElement>, private _dialog: MatDialog) { }
1414

1515
openDialog() {
1616
this.dialogRef = this._dialog.open(TestDialogFullScreen);
@@ -21,16 +21,16 @@ export class FullscreenE2E {
2121
}
2222

2323
openFullscreen() {
24-
let element = this._element.nativeElement.querySelector('#fullscreen-pane');
24+
let element = this._element.nativeElement.querySelector('#fullscreen-pane') as any;
2525

2626
if (element.requestFullscreen) {
2727
element.requestFullscreen();
2828
} else if (element.webkitRequestFullScreen) {
2929
element.webkitRequestFullScreen();
30-
} else if ((element as any).mozRequestFullScreen) {
31-
(element as any).mozRequestFullScreen();
32-
} else if ((element as any).msRequestFullScreen) {
33-
(element as any).msRequestFullScreen();
30+
} else if (element.mozRequestFullScreen) {
31+
element.mozRequestFullScreen();
32+
} else if (element.msRequestFullScreen) {
33+
element.msRequestFullScreen();
3434
}
3535
}
3636

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
189189
this._autocompleteDisabled = coerceBooleanProperty(value);
190190
}
191191

192-
constructor(private _element: ElementRef, private _overlay: Overlay,
192+
constructor(private _element: ElementRef<HTMLInputElement>, private _overlay: Overlay,
193193
private _viewContainerRef: ViewContainerRef,
194194
private _zone: NgZone,
195195
private _changeDetectorRef: ChangeDetectorRef,
@@ -667,7 +667,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
667667

668668
/** Determines whether the panel can be opened. */
669669
private _canOpen(): boolean {
670-
const element: HTMLInputElement = this._element.nativeElement;
670+
const element = this._element.nativeElement;
671671
return !element.readOnly && !element.disabled && !this._autocompleteDisabled;
672672
}
673673
}

0 commit comments

Comments
 (0)