Skip to content

Commit 2052abe

Browse files
author
vakrilov
committed
Modal dialogs reafactoring
1 parent dcb2bd4 commit 2052abe

File tree

4 files changed

+68
-46
lines changed

4 files changed

+68
-46
lines changed
+35-23
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import {
22
ReflectiveInjector, ComponentFactoryResolver, ViewContainerRef,
3-
Injector, Type, Injectable, ComponentRef, Directive
3+
Type, Injectable, ComponentRef, Directive
44
} from '@angular/core';
5-
import {Page} from 'ui/page';
6-
import {View} from 'ui/core/view';
7-
import {DetachedLoader} from '../common/detached-loader';
5+
import { Page } from 'ui/page';
6+
import { View } from 'ui/core/view';
7+
import { DetachedLoader } from '../common/detached-loader';
88

99
export interface ModalDialogOptions {
1010
context?: any;
1111
fullscreen?: boolean;
12+
viewContainerRef?: ViewContainerRef;
1213
}
1314

1415
export class ModalDialogParams {
@@ -22,52 +23,61 @@ export class ModalDialogParams {
2223
export class ModalDialogService {
2324
private containerRef: ViewContainerRef;
2425

25-
constructor(
26-
private page: Page,
27-
private resolver: ComponentFactoryResolver) {
28-
}
29-
3026
public registerViewContainerRef(ref: ViewContainerRef) {
3127
this.containerRef = ref;
3228
}
3329

3430
public showModal(type: Type<any>, options: ModalDialogOptions): Promise<any> {
35-
if (!this.containerRef) {
36-
throw new Error("No viewContainerRef: Make sure you have the modal-dialog-host directive inside your component.");
31+
let viewContainerRef = options.viewContainerRef || this.containerRef;
32+
33+
if (!viewContainerRef) {
34+
throw new Error("No viewContainerRef: Make sure you pass viewContainerRef in ModalDialogOptions.");
3735
}
36+
37+
const parentPage: Page = viewContainerRef.injector.get(Page);
38+
const resolver: ComponentFactoryResolver = viewContainerRef.injector.get(ComponentFactoryResolver);
39+
3840
return new Promise((resolve, reject) => {
39-
setTimeout(() => this.showDialog(type, options, resolve), 10);
41+
setTimeout(() => ModalDialogService.showDialog(type, options, resolve, viewContainerRef, resolver, parentPage), 10);
4042
});
4143
}
4244

43-
private showDialog(type: Type<any>, options: ModalDialogOptions, doneCallback): void {
45+
private static showDialog(
46+
type: Type<any>,
47+
options: ModalDialogOptions,
48+
doneCallback,
49+
containerRef: ViewContainerRef,
50+
resolver: ComponentFactoryResolver,
51+
parentPage: Page): void {
52+
4453
const page = new Page();
4554

46-
var detachedLoaderRef: ComponentRef<DetachedLoader>;
55+
let detachedLoaderRef: ComponentRef<DetachedLoader>;
4756
const closeCallback = (...args) => {
4857
doneCallback.apply(undefined, args);
4958
page.closeModal();
5059
detachedLoaderRef.destroy();
51-
}
60+
};
61+
5262
const modalParams = new ModalDialogParams(options.context, closeCallback);
5363

5464
const providers = ReflectiveInjector.resolve([
55-
{provide: Page, useValue: page },
56-
{provide: ModalDialogParams, useValue: modalParams },
65+
{ provide: Page, useValue: page },
66+
{ provide: ModalDialogParams, useValue: modalParams },
5767
]);
5868

59-
const childInjector = ReflectiveInjector.fromResolvedProviders(providers, this.containerRef.parentInjector);
60-
const detachedFactory = this.resolver.resolveComponentFactory(DetachedLoader);
61-
detachedLoaderRef = this.containerRef.createComponent(detachedFactory, -1, childInjector, null)
69+
const childInjector = ReflectiveInjector.fromResolvedProviders(providers, containerRef.parentInjector);
70+
const detachedFactory = resolver.resolveComponentFactory(DetachedLoader);
71+
detachedLoaderRef = containerRef.createComponent(detachedFactory, -1, childInjector, null);
6272
detachedLoaderRef.instance.loadComponent(type).then((compRef) => {
6373
const componentView = <View>compRef.location.nativeElement;
64-
74+
6575
if (componentView.parent) {
6676
(<any>componentView.parent).removeChild(componentView);
6777
}
68-
78+
6979
page.content = componentView;
70-
this.page.showModal(page, options.context, closeCallback, options.fullscreen);
80+
parentPage.showModal(page, options.context, closeCallback, options.fullscreen);
7181
});
7282
}
7383
}
@@ -78,6 +88,8 @@ export class ModalDialogService {
7888
})
7989
export class ModalDialogHost {
8090
constructor(containerRef: ViewContainerRef, modalService: ModalDialogService) {
91+
console.log("ModalDialogHost is deprecated. Call ModalDialogService.showModal() by passing ViewContainerRef in the options instead.")
92+
8193
modalService.registerViewContainerRef(containerRef);
8294
}
8395
}

nativescript-angular/platform.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {CommonModule} from '@angular/common';
1616
import {Provider} from '@angular/core';
1717
import {NativeScriptRootRenderer, NativeScriptRenderer} from './renderer';
1818
import {DetachedLoader} from "./common/detached-loader";
19-
import {ModalDialogHost} from "./directives/dialogs";
19+
import {ModalDialogHost, ModalDialogService} from "./directives/dialogs";
2020
import {
2121
Type,
2222
Injector,
@@ -84,6 +84,7 @@ export interface AppOptions {
8484
NativeScriptRenderer,
8585
{provide: Renderer, useClass: NativeScriptRenderer},
8686
{provide: Sanitizer, useClass: NativeScriptSanitizer},
87+
ModalDialogService
8788
],
8889
entryComponents: [
8990
DetachedLoader,

ng-sample/app/examples/modal/modal-test.ts

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
import {Component} from '@angular/core';
1+
import { Component, ViewContainerRef } from '@angular/core';
22
import * as dialogs from "ui/dialogs";
3-
import {ModalDialogService, ModalDialogOptions, ModalDialogHost} from "nativescript-angular/directives/dialogs";
4-
import {ModalContent} from "./modal-content";
3+
import { ModalDialogService, ModalDialogOptions } from "nativescript-angular/directives/dialogs";
4+
import { ModalContent} from "./modal-content";
55

66
@Component({
77
selector: 'modal-test',
8-
providers: [ModalDialogService],
98
template: `
10-
<GridLayout rows="*, auto" modal-dialog-host>
9+
<GridLayout rows="*, auto">
1110
<StackLayout verticalAlignment="top" margin="12">
1211
<Button text="show component" (tap)="showModal(false)"></Button>
1312
<Button text="show component fullscreen" (tap)="showModal(true)"></Button>
@@ -27,7 +26,7 @@ import {ModalContent} from "./modal-content";
2726
export class ModalTest {
2827
public result: string = "result";
2928

30-
constructor(private modal: ModalDialogService) {
29+
constructor(private modal: ModalDialogService, private vcRef: ViewContainerRef) {
3130
}
3231

3332
static entries = [
@@ -39,14 +38,15 @@ export class ModalTest {
3938
];
4039

4140
public showModal(fullscreen: boolean) {
42-
var options: ModalDialogOptions = {
41+
const options: ModalDialogOptions = {
4342
context: { promptMsg: "This is the prompt message!" },
44-
fullscreen: fullscreen
43+
fullscreen: fullscreen,
44+
viewContainerRef: this.vcRef
4545
};
4646

4747
this.modal.showModal(ModalContent, options).then((res: string) => {
4848
this.result = res || "empty result";
49-
})
49+
});
5050
}
5151

5252
public showAlert() {

tests/app/tests/modal-dialog.ts

+22-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//make sure you import mocha-config before @angular/core
22
import {assert} from "./test-config";
33
import {TestApp} from "./test-app";
4-
import {Component, ComponentRef} from "@angular/core";
4+
import {Component, ViewContainerRef} from "@angular/core";
55
import {Page} from "ui/page";
66
import {topmost} from "ui/frame";
77
import {ModalDialogParams, ModalDialogService} from "nativescript-angular/directives/dialogs";
@@ -16,7 +16,7 @@ const CLOSE_WAIT = (device.os === platformNames.ios) ? 1000 : 0;
1616
export class ModalComponent {
1717
constructor(public params: ModalDialogParams, private page: Page) {
1818
page.on("shownModally", () => {
19-
var result = this.params.context;
19+
const result = this.params.context;
2020
this.params.closeCallback(result);
2121
});
2222
}
@@ -42,7 +42,7 @@ export class FailComponent {
4242
</GridLayout>`
4343
})
4444
export class SuccessComponent {
45-
constructor(public service: ModalDialogService) {
45+
constructor(public service: ModalDialogService, public vcRef: ViewContainerRef) {
4646
}
4747
}
4848

@@ -56,15 +56,15 @@ describe('modal-dialog', () => {
5656
// HACK: Wait for the navigations from the test runner app
5757
// Remove the setTimeout when test runner start tests on page.navigatedTo
5858
setTimeout(done, 1000);
59-
})
59+
});
6060
});
6161

6262
after(() => {
6363
testApp.dispose();
6464
});
6565

6666
afterEach(() => {
67-
var page = topmost().currentPage;
67+
const page = topmost().currentPage;
6868
if (page && page.modal) {
6969
console.log("Warning: closing a leftover modal page!");
7070
page.modal.closeModal();
@@ -73,34 +73,43 @@ describe('modal-dialog', () => {
7373
});
7474

7575

76-
it("showModal throws when there is no modal-dialog-host", (done) => {
76+
it("showModal throws when there is no modal-dialog-host and no viewContainer provided", (done) => {
7777
testApp.loadComponent(FailComponent)
7878
.then((ref) => {
79-
var service = <ModalDialogService>ref.instance.service;
79+
const service = <ModalDialogService>ref.instance.service;
8080
assert.throws(() => service.showModal(ModalComponent, {}), "No viewContainerRef: Make sure you have the modal-dialog-host directive inside your component.");
8181
}).then(() => done(), err => done(err));
8282
});
8383

8484
it("showModal succeeds when there is modal-dialog-host", (done) => {
8585
testApp.loadComponent(SuccessComponent)
8686
.then((ref) => {
87-
var service = <ModalDialogService>ref.instance.service;
87+
const service = <ModalDialogService>ref.instance.service;
8888
return service.showModal(ModalComponent, {});
8989
})
90-
.then((res) => setTimeout(done, CLOSE_WAIT), err => done(err)) // wait for the dialog to close in IOS
90+
.then((res) => setTimeout(done, CLOSE_WAIT), err => done(err)); // wait for the dialog to close in IOS
91+
});
92+
93+
it("showModal succeeds when there is viewContainer provided", (done) => {
94+
testApp.loadComponent(SuccessComponent)
95+
.then((ref) => {
96+
const service = <ModalDialogService>ref.instance.service;
97+
return service.showModal(ModalComponent, {});
98+
})
99+
.then((res) => setTimeout(done, CLOSE_WAIT), err => done(err)); // wait for the dialog to close in IOS
91100
});
92101

93102

94103
it("showModal passes modal params and gets result when resolved", (done) => {
95-
var context = { property: "my context" };
104+
const context = { property: "my context" };
96105
testApp.loadComponent(SuccessComponent)
97106
.then((ref) => {
98-
var service = <ModalDialogService>ref.instance.service;
107+
const service = <ModalDialogService>ref.instance.service;
99108
return service.showModal(ModalComponent, { context: context });
100109
})
101110
.then((res) => {
102111
assert.strictEqual(res, context);
103-
setTimeout(done, CLOSE_WAIT) // wait for the dialog to close in IOS
112+
setTimeout(done, CLOSE_WAIT); // wait for the dialog to close in IOS
104113
}, err => done(err));
105-
})
114+
});
106115
});

0 commit comments

Comments
 (0)