Skip to content

Commit 990e79a

Browse files
committed
refactor(material/dialog): use embedded inejctor to provide ref to template dialogs
Previously we had to walk the DOM in order to figure out which dialog ref belonged to a specific template dialog. We no longer need to do that if we provide an injector to the template portal.
1 parent 26fc03e commit 990e79a

File tree

4 files changed

+61
-112
lines changed

4 files changed

+61
-112
lines changed

src/material-experimental/mdc-dialog/dialog-content-directives.ts

Lines changed: 22 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@
99
import {
1010
Directive,
1111
ElementRef,
12+
Inject,
1213
Input,
1314
OnChanges,
1415
OnInit,
15-
Optional,
1616
SimpleChanges,
1717
} from '@angular/core';
1818
import {_closeDialogVia} from '@angular/material/dialog';
1919

20-
import {MatDialog} from './dialog';
2120
import {MatDialogRef} from './dialog-ref';
2221

2322
/** Counter used to generate unique IDs for dialog elements. */
@@ -35,7 +34,7 @@ let dialogElementUid = 0;
3534
'[attr.type]': 'type',
3635
},
3736
})
38-
export class MatDialogClose implements OnInit, OnChanges {
37+
export class MatDialogClose implements OnChanges {
3938
/** Screenreader label for the button. */
4039
@Input('aria-label') ariaLabel: string;
4140

@@ -48,24 +47,15 @@ export class MatDialogClose implements OnInit, OnChanges {
4847
@Input('matDialogClose') _matDialogClose: any;
4948

5049
constructor(
51-
// The dialog title directive is always used in combination with a `MatDialogRef`.
52-
// tslint:disable-next-line: lightweight-tokens
53-
@Optional() public dialogRef: MatDialogRef<any>,
54-
private _elementRef: ElementRef<HTMLElement>,
55-
private _dialog: MatDialog,
50+
public dialogRef: MatDialogRef<any>,
51+
/**
52+
* @deprecated `_elementRef` and `_dialog` parameters to be removed.
53+
* @breaking-change 15.0.0
54+
*/
55+
@Inject(ElementRef) _elementRef?: any,
56+
@Inject(ElementRef) _dialog?: any,
5657
) {}
5758

58-
ngOnInit() {
59-
if (!this.dialogRef) {
60-
// When this directive is included in a dialog via TemplateRef (rather than being
61-
// in a Component), the DialogRef isn't available via injection because embedded
62-
// views cannot be given a custom injector. Instead, we look up the DialogRef by
63-
// ID. This must occur in `onInit`, as the ID binding for the dialog container won't
64-
// be resolved at constructor time.
65-
this.dialogRef = getClosestDialog(this._elementRef, this._dialog.openDialogs)!;
66-
}
67-
}
68-
6959
ngOnChanges(changes: SimpleChanges) {
7060
const proxiedChange = changes['_matDialogClose'] || changes['_matDialogCloseResult'];
7161

@@ -102,27 +92,23 @@ export class MatDialogTitle implements OnInit {
10292
@Input() id: string = `mat-mdc-dialog-title-${dialogElementUid++}`;
10393

10494
constructor(
105-
// The dialog title directive is always used in combination with a `MatDialogRef`.
106-
// tslint:disable-next-line: lightweight-tokens
107-
@Optional() private _dialogRef: MatDialogRef<any>,
108-
private _elementRef: ElementRef<HTMLElement>,
109-
private _dialog: MatDialog,
95+
private _dialogRef: MatDialogRef<any>,
96+
/**
97+
* @deprecated `_elementRef` and `_dialog` parameters to be removed.
98+
* @breaking-change 15.0.0
99+
*/
100+
@Inject(ElementRef) _elementRef?: any,
101+
@Inject(ElementRef) _dialog?: any,
110102
) {}
111103

112104
ngOnInit() {
113-
if (!this._dialogRef) {
114-
this._dialogRef = getClosestDialog(this._elementRef, this._dialog.openDialogs)!;
115-
}
116-
117-
if (this._dialogRef) {
118-
Promise.resolve().then(() => {
119-
const container = this._dialogRef._containerInstance;
105+
Promise.resolve().then(() => {
106+
const container = this._dialogRef._containerInstance;
120107

121-
if (container && !container._ariaLabelledBy) {
122-
container._ariaLabelledBy = this.id;
123-
}
124-
});
125-
}
108+
if (container && !container._ariaLabelledBy) {
109+
container._ariaLabelledBy = this.id;
110+
}
111+
});
126112
}
127113
}
128114

@@ -153,18 +139,3 @@ export class MatDialogActions {
153139
*/
154140
@Input() align?: 'start' | 'center' | 'end' = 'start';
155141
}
156-
157-
/**
158-
* Finds the closest MatDialogRef to an element by looking at the DOM.
159-
* @param element Element relative to which to look for a dialog.
160-
* @param openDialogs References to the currently-open dialogs.
161-
*/
162-
function getClosestDialog(element: ElementRef<HTMLElement>, openDialogs: MatDialogRef<any>[]) {
163-
let parent: HTMLElement | null = element.nativeElement.parentElement;
164-
165-
while (parent && !parent.classList.contains('mat-mdc-dialog-container')) {
166-
parent = parent.parentElement;
167-
}
168-
169-
return parent ? openDialogs.find(dialog => dialog.id === parent!.id) : null;
170-
}

src/material/dialog/dialog-content-directives.ts

Lines changed: 22 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,10 @@ import {
1111
Input,
1212
OnChanges,
1313
OnInit,
14-
Optional,
1514
SimpleChanges,
1615
ElementRef,
16+
Inject,
1717
} from '@angular/core';
18-
import {MatDialog} from './dialog';
1918
import {_closeDialogVia, MatDialogRef} from './dialog-ref';
2019

2120
/** Counter used to generate unique IDs for dialog elements. */
@@ -33,7 +32,7 @@ let dialogElementUid = 0;
3332
'[attr.type]': 'type',
3433
},
3534
})
36-
export class MatDialogClose implements OnInit, OnChanges {
35+
export class MatDialogClose implements OnChanges {
3736
/** Screenreader label for the button. */
3837
@Input('aria-label') ariaLabel: string;
3938

@@ -53,22 +52,15 @@ export class MatDialogClose implements OnInit, OnChanges {
5352
*/
5453
// The dialog title directive is always used in combination with a `MatDialogRef`.
5554
// tslint:disable-next-line: lightweight-tokens
56-
@Optional() public dialogRef: MatDialogRef<any>,
57-
private _elementRef: ElementRef<HTMLElement>,
58-
private _dialog: MatDialog,
55+
public dialogRef: MatDialogRef<any>,
56+
/**
57+
* @deprecated `_elementRef` and `_dialog` parameters to be removed.
58+
* @breaking-change 15.0.0
59+
*/
60+
@Inject(ElementRef) _elementRef?: any,
61+
@Inject(ElementRef) _dialog?: any,
5962
) {}
6063

61-
ngOnInit() {
62-
if (!this.dialogRef) {
63-
// When this directive is included in a dialog via TemplateRef (rather than being
64-
// in a Component), the DialogRef isn't available via injection because embedded
65-
// views cannot be given a custom injector. Instead, we look up the DialogRef by
66-
// ID. This must occur in `onInit`, as the ID binding for the dialog container won't
67-
// be resolved at constructor time.
68-
this.dialogRef = getClosestDialog(this._elementRef, this._dialog.openDialogs)!;
69-
}
70-
}
71-
7264
ngOnChanges(changes: SimpleChanges) {
7365
const proxiedChange = changes['_matDialogClose'] || changes['_matDialogCloseResult'];
7466

@@ -106,27 +98,23 @@ export class MatDialogTitle implements OnInit {
10698
@Input() id: string = `mat-dialog-title-${dialogElementUid++}`;
10799

108100
constructor(
109-
// The dialog title directive is always used in combination with a `MatDialogRef`.
110-
// tslint:disable-next-line: lightweight-tokens
111-
@Optional() private _dialogRef: MatDialogRef<any>,
112-
private _elementRef: ElementRef<HTMLElement>,
113-
private _dialog: MatDialog,
101+
private _dialogRef: MatDialogRef<any>,
102+
/**
103+
* @deprecated `_elementRef` and `_dialog` parameters to be removed.
104+
* @breaking-change 15.0.0
105+
*/
106+
@Inject(ElementRef) _elementRef?: any,
107+
@Inject(ElementRef) _dialog?: any,
114108
) {}
115109

116110
ngOnInit() {
117-
if (!this._dialogRef) {
118-
this._dialogRef = getClosestDialog(this._elementRef, this._dialog.openDialogs)!;
119-
}
120-
121-
if (this._dialogRef) {
122-
Promise.resolve().then(() => {
123-
const container = this._dialogRef._containerInstance;
111+
Promise.resolve().then(() => {
112+
const container = this._dialogRef._containerInstance;
124113

125-
if (container && !container._ariaLabelledBy) {
126-
container._ariaLabelledBy = this.id;
127-
}
128-
});
129-
}
114+
if (container && !container._ariaLabelledBy) {
115+
container._ariaLabelledBy = this.id;
116+
}
117+
});
130118
}
131119
}
132120

@@ -157,18 +145,3 @@ export class MatDialogActions {
157145
*/
158146
@Input() align?: 'start' | 'center' | 'end' = 'start';
159147
}
160-
161-
/**
162-
* Finds the closest MatDialogRef to an element by looking at the DOM.
163-
* @param element Element relative to which to look for a dialog.
164-
* @param openDialogs References to the currently-open dialogs.
165-
*/
166-
function getClosestDialog(element: ElementRef<HTMLElement>, openDialogs: MatDialogRef<any>[]) {
167-
let parent: HTMLElement | null = element.nativeElement.parentElement;
168-
169-
while (parent && !parent.classList.contains('mat-dialog-container')) {
170-
parent = parent.parentElement;
171-
}
172-
173-
return parent ? openDialogs.find(dialog => dialog.id === parent!.id) : null;
174-
}

src/material/dialog/dialog.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -292,16 +292,21 @@ export abstract class _MatDialogBase<C extends _MatDialogContainerBase> implemen
292292
// Create a reference to the dialog we're creating in order to give the user a handle
293293
// to modify and close it.
294294
const dialogRef = new this._dialogRefConstructor(overlayRef, dialogContainer, config.id);
295+
const injector = this._createInjector<T>(config, dialogRef, dialogContainer);
295296

296297
if (componentOrTemplateRef instanceof TemplateRef) {
297298
dialogContainer.attachTemplatePortal(
298-
new TemplatePortal<T>(componentOrTemplateRef, null!, <any>{
299-
$implicit: config.data,
300-
dialogRef,
301-
}),
299+
new TemplatePortal<T>(
300+
componentOrTemplateRef,
301+
null!,
302+
<any>{
303+
$implicit: config.data,
304+
dialogRef,
305+
},
306+
injector,
307+
),
302308
);
303309
} else {
304-
const injector = this._createInjector<T>(config, dialogRef, dialogContainer);
305310
const contentRef = dialogContainer.attachComponentPortal<T>(
306311
new ComponentPortal(
307312
componentOrTemplateRef,

tools/public_api_guard/material/dialog.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,10 @@ export abstract class _MatDialogBase<C extends _MatDialogContainerBase> implemen
133133
}
134134

135135
// @public
136-
export class MatDialogClose implements OnInit, OnChanges {
136+
export class MatDialogClose implements OnChanges {
137137
constructor(
138-
dialogRef: MatDialogRef<any>, _elementRef: ElementRef<HTMLElement>, _dialog: MatDialog);
138+
dialogRef: MatDialogRef<any>,
139+
_elementRef?: any, _dialog?: any);
139140
ariaLabel: string;
140141
// @deprecated
141142
dialogRef: MatDialogRef<any>;
@@ -145,14 +146,12 @@ export class MatDialogClose implements OnInit, OnChanges {
145146
// (undocumented)
146147
ngOnChanges(changes: SimpleChanges): void;
147148
// (undocumented)
148-
ngOnInit(): void;
149-
// (undocumented)
150149
_onButtonClick(event: MouseEvent): void;
151150
type: 'submit' | 'button' | 'reset';
152151
// (undocumented)
153152
static ɵdir: i0.ɵɵDirectiveDeclaration<MatDialogClose, "[mat-dialog-close], [matDialogClose]", ["matDialogClose"], { "ariaLabel": "aria-label"; "type": "type"; "dialogResult": "mat-dialog-close"; "_matDialogClose": "matDialogClose"; }, {}, never>;
154153
// (undocumented)
155-
static ɵfac: i0.ɵɵFactoryDeclaration<MatDialogClose, [{ optional: true; }, null, null]>;
154+
static ɵfac: i0.ɵɵFactoryDeclaration<MatDialogClose, never>;
156155
}
157156

158157
// @public
@@ -283,14 +282,15 @@ export const enum MatDialogState {
283282

284283
// @public
285284
export class MatDialogTitle implements OnInit {
286-
constructor(_dialogRef: MatDialogRef<any>, _elementRef: ElementRef<HTMLElement>, _dialog: MatDialog);
285+
constructor(_dialogRef: MatDialogRef<any>,
286+
_elementRef?: any, _dialog?: any);
287287
id: string;
288288
// (undocumented)
289289
ngOnInit(): void;
290290
// (undocumented)
291291
static ɵdir: i0.ɵɵDirectiveDeclaration<MatDialogTitle, "[mat-dialog-title], [matDialogTitle]", ["matDialogTitle"], { "id": "id"; }, {}, never>;
292292
// (undocumented)
293-
static ɵfac: i0.ɵɵFactoryDeclaration<MatDialogTitle, [{ optional: true; }, null, null]>;
293+
static ɵfac: i0.ɵɵFactoryDeclaration<MatDialogTitle, never>;
294294
}
295295

296296
// @public

0 commit comments

Comments
 (0)