Skip to content
This repository was archived by the owner on Oct 7, 2020. It is now read-only.

Commit 84ba5e7

Browse files
authored
feat(dialog): Support passing custom ComponentFactoryResolver (#1978)
Allows for a different `ComponentFactoryResolver` to be passed in when creating a dialog. #### Potential use case Support lazy loaded dialog components; allowing code modularity and precise code splitting.
1 parent 6537877 commit 84ba5e7

File tree

5 files changed

+70
-37
lines changed

5 files changed

+70
-37
lines changed

demos/src/app/components/dialog-demo/api.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ <h4 mdcSubtitle2>MdcDialogConfig</h4>
7070
<td>Enable or disable the stacking of action buttons, if they can't fit
7171
on a single line.</td>
7272
</tr>
73+
<tr>
74+
<td>componentFactoryResolver?: ComponentFactoryResolver</td>
75+
<td>Allows for a different `ComponentFactoryResolver` to be passed in when creating a dialog.</td>
76+
</tr>
7377
<tr>
7478
<td>data: any</td>
7579
<td>Data that can be assigned inside child component.</td>
Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,45 @@
11
<div class="demo-content">
2+
<h3 class="demo-content__headline">Configuring Dialog via
3+
entryComponents</h3>
4+
<p mdcBody2>Because <code class="docs-markdown-code">MdcDialog</code>
5+
instantiates components at run-time, the
6+
Angular compiler needs extra information to create the necessary
7+
ComponentFactory for your dialog content component.</p>
8+
<p mdcBody2>For any component loaded into a dialog, you must include your
9+
component
10+
class in the list of <code class="docs-markdown-code">entryComponents</code>
11+
in your NgModule definition so that
12+
the Angular compiler knows to create the ComponentFactory.</p>
13+
<pre><code highlight="@NgModule({
14+
imports: [
15+
// ...
16+
MdcDialogModule
17+
],
18+
declarations: [
19+
AppComponent,
20+
ExampleDialogComponent
21+
],
22+
entryComponents: [
23+
ExampleDialogComponent
24+
],
25+
bootstrap: [AppComponent]
26+
})
27+
export class AppModule {}"></code></pre>
28+
229
<h3 class="demo-content__headline">Usage</h3>
3-
A dialog is opened by calling the <code class="docs-markdown-code">open</code> method with a component to be loaded
4-
and an optional config object. The <code class="docs-markdown-code">open</code> method will return an instance of
30+
A dialog is opened by calling the <code class="docs-markdown-code">open</code>
31+
method with a component to be loaded
32+
and an optional config object. The <code
33+
class="docs-markdown-code">open</code> method will return an instance of
534
<code class="docs-markdown-code">MdcDialogRef</code>:
635

736
<pre><code highlight="let dialogRef = dialog.open(UserProfileComponent, {
837
clickOutsideToClose: true,
938
escapeToClose: true,
1039
});"></code></pre>
1140

12-
The <code class="docs-markdown-code">MdcDialogRef</code> provides a handle on the opened dialog. It can be used to
41+
The <code class="docs-markdown-code">MdcDialogRef</code> provides a handle on
42+
the opened dialog. It can be used to
1343
close the dialog and to receive notification when the dialog has been closed.
1444

1545
<pre><code highlight="dialogRef.afterClosed().subscribe(result => {
@@ -18,9 +48,12 @@ <h3 class="demo-content__headline">Usage</h3>
1848
1949
dialogRef.close('Pizza!');"></code></pre>
2050

21-
<p>Components created via <code class="docs-markdown-code">MdcDialog</code> can inject <code class="docs-markdown-code">MdcDialogRef</code>
22-
and use it to close the dialog in which they are contained. When closing, an optional result value can be
23-
provided. This result value is forwarded as the result of the <code class="docs-markdown-code">afterClosed</code>
51+
<p>Components created via <code class="docs-markdown-code">MdcDialog</code>
52+
can inject <code class="docs-markdown-code">MdcDialogRef</code>
53+
and use it to close the dialog in which they are contained. When closing, an
54+
optional result value can be
55+
provided. This result value is forwarded as the result of the <code
56+
class="docs-markdown-code">afterClosed</code>
2457
promise.</p>
2558

2659
<pre><code highlight="@Component({/* ... */})
@@ -34,7 +67,8 @@ <h3 class="demo-content__headline">Usage</h3>
3467
}"></code></pre>
3568

3669
<h3 class="demo-content__headline">Sharing data with the Dialog component</h3>
37-
If you want to share data with your dialog, you can use the <code class="docs-markdown-code">data</code> option to
70+
If you want to share data with your dialog, you can use the <code
71+
class="docs-markdown-code">data</code> option to
3872
pass information to the dialog component.
3973

4074
<pre><code highlight="let dialogRef = dialog.open(YourDialog, {
@@ -59,26 +93,4 @@ <h3 class="demo-content__headline">Sharing data with the Dialog component</h3>
5993
userName: string = data.name;
6094
}
6195
}"></code></pre>
62-
<h3 class="demo-content__headline">Configuring dialog content via entryComponents</h3>
63-
<p mdcBody2>Because <code class="docs-markdown-code">MdcDialog</code> instantiates components at run-time, the
64-
Angular compiler needs extra information to create the necessary
65-
ComponentFactory for your dialog content component.</p>
66-
<p mdcBody2>For any component loaded into a dialog, you must include your component
67-
class in the list of <code class="docs-markdown-code">entryComponents</code> in your NgModule definition so that
68-
the Angular compiler knows to create the ComponentFactory.</p>
69-
<pre><code highlight="@NgModule({
70-
imports: [
71-
// ...
72-
MdcDialogModule
73-
],
74-
declarations: [
75-
AppComponent,
76-
ExampleDialogComponent
77-
],
78-
entryComponents: [
79-
ExampleDialogComponent
80-
],
81-
bootstrap: [AppComponent]
82-
})
83-
export class AppModule {}"></code></pre>
84-
</div>
96+
</div>

packages/dialog/dialog-config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {ViewContainerRef} from '@angular/core';
1+
import {ViewContainerRef, ComponentFactoryResolver} from '@angular/core';
22

33
export class MdcDialogConfig<D = any> {
44
viewContainerRef?: ViewContainerRef;
@@ -24,6 +24,9 @@ export class MdcDialogConfig<D = any> {
2424
/** Applied automatically when the dialog's action buttons can't fit on a single line and must be stacked. */
2525
buttonsStacked?: boolean = true;
2626

27+
/** Alternate `ComponentFactoryResolver` to use when resolving the associated component. */
28+
componentFactoryResolver?: ComponentFactoryResolver;
29+
2730
/** Data to be injected into the dialog content. */
2831
data?: D | null = null;
2932
}

packages/dialog/dialog.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export class MdcDialog implements OnDestroy {
6666
private _overlay: Overlay,
6767
private _injector: Injector,
6868
@Optional() @Inject(MDC_DIALOG_DEFAULT_OPTIONS) private _defaultOptions: MdcDialogConfig,
69-
@Optional() @SkipSelf() private _parentDialog: MdcDialog) { }
69+
@Optional() @SkipSelf() private _parentDialog: MdcDialog) {}
7070

7171
/**
7272
* Opens a modal dialog containing the given template.
@@ -138,8 +138,8 @@ export class MdcDialog implements OnDestroy {
138138
const injector = new PortalInjector(userInjector || this._injector, new WeakMap([
139139
[MdcDialogConfig, config]
140140
]));
141-
const containerPortal =
142-
new ComponentPortal(MdcDialogPortal, config.viewContainerRef, injector);
141+
const containerPortal = new ComponentPortal(MdcDialogPortal,
142+
config.viewContainerRef, injector, config.componentFactoryResolver);
143143
const containerRef = overlay.attach<MdcDialogPortal>(containerPortal);
144144

145145
return containerRef.instance;
@@ -167,7 +167,7 @@ export class MdcDialog implements OnDestroy {
167167
if (componentOrTemplateRef instanceof TemplateRef) {
168168
dialogContainer.attachTemplatePortal(
169169
new TemplatePortal<T>(componentOrTemplateRef, null!,
170-
<any>{ $implicit: config.data, dialogRef }));
170+
<any>{$implicit: config.data, dialogRef}));
171171
} else {
172172
const injector = this._createInjector<T>(config, dialogRef, dialogContainer);
173173
const contentRef = dialogContainer.attachComponentPortal<T>(
@@ -255,5 +255,5 @@ export class MdcDialog implements OnDestroy {
255255
*/
256256
function _applyConfigDefaults(
257257
config?: MdcDialogConfig, defaultOptions?: MdcDialogConfig): MdcDialogConfig {
258-
return { ...defaultOptions, ...config };
258+
return {...defaultOptions, ...config};
259259
}

test/dialog/dialog.test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
NgModule, Directive, Component, Inject,
3-
Injector, TemplateRef, ViewContainerRef, ViewChild
3+
Injector, TemplateRef, ViewContainerRef, ViewChild,
4+
ComponentFactoryResolver
45
} from '@angular/core';
56
import {inject, flush, fakeAsync, flushMicrotasks, tick, ComponentFixture, TestBed} from '@angular/core/testing';
67
import {Location} from '@angular/common';
@@ -218,6 +219,19 @@ describe('MdcDialog Service', () => {
218219
sibling.parentNode!.removeChild(sibling);
219220
}));
220221

222+
it('should be able to pass in an alternate ComponentFactoryResolver',
223+
inject([ComponentFactoryResolver], (resolver: ComponentFactoryResolver) => {
224+
spyOn(resolver, 'resolveComponentFactory').and.callThrough();
225+
226+
dialog.open(PizzaMsg, {
227+
viewContainerRef: testViewContainerRef,
228+
componentFactoryResolver: resolver
229+
});
230+
viewContainerFixture.detectChanges();
231+
232+
expect(resolver.resolveComponentFactory).toHaveBeenCalled();
233+
}));
234+
221235
it('should notify the observers if all open dialogs have finished closing', fakeAsync(() => {
222236
const ref1 = dialog.open(PizzaMsg, {viewContainerRef: testViewContainerRef});
223237
const ref2 = dialog.open(SimpleDialog, {viewContainerRef: testViewContainerRef});

0 commit comments

Comments
 (0)