Skip to content

Commit 4f939bc

Browse files
committed
refactor(material/dialog): use embedded injector 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 45fae71 commit 4f939bc

File tree

4 files changed

+43
-82
lines changed

4 files changed

+43
-82
lines changed

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

Lines changed: 14 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import {
1010
Directive,
1111
ElementRef,
12+
Inject,
1213
Input,
1314
OnChanges,
1415
OnInit,
@@ -17,7 +18,6 @@ import {
1718
} from '@angular/core';
1819
import {_closeDialogVia} from '@angular/material/dialog';
1920

20-
import {MatDialog} from './dialog';
2121
import {MatDialogRef} from './dialog-ref';
2222

2323
/** Counter used to generate unique IDs for dialog elements. */
@@ -35,7 +35,7 @@ let dialogElementUid = 0;
3535
'[attr.type]': 'type',
3636
},
3737
})
38-
export class MatDialogClose implements OnInit, OnChanges {
38+
export class MatDialogClose implements OnChanges {
3939
/** Screenreader label for the button. */
4040
@Input('aria-label') ariaLabel: string;
4141

@@ -51,21 +51,14 @@ export class MatDialogClose implements OnInit, OnChanges {
5151
// The dialog title directive is always used in combination with a `MatDialogRef`.
5252
// tslint:disable-next-line: lightweight-tokens
5353
@Optional() public dialogRef: MatDialogRef<any>,
54-
private _elementRef: ElementRef<HTMLElement>,
55-
private _dialog: MatDialog,
54+
/**
55+
* @deprecated `_elementRef` and `_dialog` parameters to be removed.
56+
* @breaking-change 15.0.0
57+
*/
58+
@Inject(ElementRef) _elementRef?: any,
59+
@Inject(ElementRef) _dialog?: any,
5660
) {}
5761

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-
6962
ngOnChanges(changes: SimpleChanges) {
7063
const proxiedChange = changes['_matDialogClose'] || changes['_matDialogCloseResult'];
7164

@@ -105,15 +98,15 @@ export class MatDialogTitle implements OnInit {
10598
// The dialog title directive is always used in combination with a `MatDialogRef`.
10699
// tslint:disable-next-line: lightweight-tokens
107100
@Optional() private _dialogRef: MatDialogRef<any>,
108-
private _elementRef: ElementRef<HTMLElement>,
109-
private _dialog: MatDialog,
101+
/**
102+
* @deprecated `_elementRef` and `_dialog` parameters to be removed.
103+
* @breaking-change 15.0.0
104+
*/
105+
@Inject(ElementRef) _elementRef?: any,
106+
@Inject(ElementRef) _dialog?: any,
110107
) {}
111108

112109
ngOnInit() {
113-
if (!this._dialogRef) {
114-
this._dialogRef = getClosestDialog(this._elementRef, this._dialog.openDialogs)!;
115-
}
116-
117110
if (this._dialogRef) {
118111
Promise.resolve().then(() => {
119112
const container = this._dialogRef._containerInstance;
@@ -153,18 +146,3 @@ export class MatDialogActions {
153146
*/
154147
@Input() align?: 'start' | 'center' | 'end' = 'start';
155148
}
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: 14 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import {
1414
Optional,
1515
SimpleChanges,
1616
ElementRef,
17+
Inject,
1718
} from '@angular/core';
18-
import {MatDialog} from './dialog';
1919
import {_closeDialogVia, MatDialogRef} from './dialog-ref';
2020

2121
/** Counter used to generate unique IDs for dialog elements. */
@@ -33,7 +33,7 @@ let dialogElementUid = 0;
3333
'[attr.type]': 'type',
3434
},
3535
})
36-
export class MatDialogClose implements OnInit, OnChanges {
36+
export class MatDialogClose implements OnChanges {
3737
/** Screenreader label for the button. */
3838
@Input('aria-label') ariaLabel: string;
3939

@@ -54,21 +54,14 @@ export class MatDialogClose implements OnInit, OnChanges {
5454
// The dialog title directive is always used in combination with a `MatDialogRef`.
5555
// tslint:disable-next-line: lightweight-tokens
5656
@Optional() public dialogRef: MatDialogRef<any>,
57-
private _elementRef: ElementRef<HTMLElement>,
58-
private _dialog: MatDialog,
57+
/**
58+
* @deprecated `_elementRef` and `_dialog` parameters to be removed.
59+
* @breaking-change 15.0.0
60+
*/
61+
@Inject(ElementRef) _elementRef?: any,
62+
@Inject(ElementRef) _dialog?: any,
5963
) {}
6064

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-
7265
ngOnChanges(changes: SimpleChanges) {
7366
const proxiedChange = changes['_matDialogClose'] || changes['_matDialogCloseResult'];
7467

@@ -109,15 +102,15 @@ export class MatDialogTitle implements OnInit {
109102
// The dialog title directive is always used in combination with a `MatDialogRef`.
110103
// tslint:disable-next-line: lightweight-tokens
111104
@Optional() private _dialogRef: MatDialogRef<any>,
112-
private _elementRef: ElementRef<HTMLElement>,
113-
private _dialog: MatDialog,
105+
/**
106+
* @deprecated `_elementRef` and `_dialog` parameters to be removed.
107+
* @breaking-change 15.0.0
108+
*/
109+
@Inject(ElementRef) _elementRef?: any,
110+
@Inject(ElementRef) _dialog?: any,
114111
) {}
115112

116113
ngOnInit() {
117-
if (!this._dialogRef) {
118-
this._dialogRef = getClosestDialog(this._elementRef, this._dialog.openDialogs)!;
119-
}
120-
121114
if (this._dialogRef) {
122115
Promise.resolve().then(() => {
123116
const container = this._dialogRef._containerInstance;
@@ -157,18 +150,3 @@ export class MatDialogActions {
157150
*/
158151
@Input() align?: 'start' | 'center' | 'end' = 'start';
159152
}
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: 5 additions & 5 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,8 +146,6 @@ 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)
@@ -283,7 +282,8 @@ 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;

0 commit comments

Comments
 (0)