Skip to content

Commit ed814c6

Browse files
committed
Merge pull request #127 from NativeScript/modal-pages
Modal dialog service
2 parents aceb269 + e91f52c commit ed814c6

File tree

18 files changed

+400
-58
lines changed

18 files changed

+400
-58
lines changed

ng-sample/app/app.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {ListTestAsync} from "./examples/list/list-test-async";
2323
import {ImageTest} from "./examples/image/image-test";
2424
import {NavigationTest} from "./examples/navigation/navigation-test";
2525
import {ActionBarTest} from "./examples/action-bar/action-bar-test";
26-
26+
import {ModalTest} from "./examples/modal/modal-test";
2727

2828
nativeScriptBootstrap(RendererTest);
2929
//nativeScriptBootstrap(Benchmark);
@@ -35,3 +35,4 @@ nativeScriptBootstrap(RendererTest);
3535
//nativeScriptBootstrap(ImageTest);
3636
//nativeScriptBootstrap(NavigationTest, [NS_ROUTER_PROVIDERS]);
3737
//nativeScriptBootstrap(ActionBarTest, [NS_ROUTER_PROVIDERS], { startPageActionBarHidden: false });
38+
// nativeScriptBootstrap(ModalTest);

ng-sample/app/examples/action-bar/action-bar-test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class FirstComponent {
3131

3232

3333
@Component({
34-
selector: "nested-componenet",
34+
selector: "nested-component",
3535
directives: [NS_ROUTER_DIRECTIVES, NS_DIRECTIVES],
3636
template: `
3737
@@ -65,7 +65,7 @@ class NestedComponent {
6565
6666
<StackLayout verticalAlignment="center">
6767
<Label text="Second Page is Here" class="title"></Label>
68-
<nested-componenet></nested-componenet>
68+
<nested-component></nested-component>
6969
</StackLayout>
7070
`,
7171
})
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import {Component} from 'angular2/core';
2+
import {ModalDialogParams} from "../../nativescript-angular/directives/dialogs";
3+
4+
@Component({
5+
selector: 'modal-content',
6+
template: `
7+
<StackLayout margin="24" horizontalAlignment="center" verticalAlignment="center">
8+
<Label [text]="prompt"></Label>
9+
<StackLayout orientation="horizontal" marginTop="12">
10+
<Button text="ok" (tap)="close('OK')"></Button>
11+
<Button text="cancel" (tap)="close('Cancel')"></Button>
12+
</StackLayout>
13+
</StackLayout>
14+
`
15+
})
16+
export class ModalContent {
17+
public prompt: string;
18+
constructor(private params: ModalDialogParams) {
19+
console.log("ModalContent.constructor: " + JSON.stringify(params))
20+
this.prompt = params.context.promptMsg;
21+
}
22+
23+
public close(res: string) {
24+
this.params.closeCallback(res);
25+
}
26+
27+
ngOnInit() {
28+
console.log("ModalContent.ngOnInit");
29+
30+
}
31+
32+
ngOnDestroy() {
33+
console.log("ModalContent.ngOnDestroy");
34+
}
35+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import {Component} from 'angular2/core';
2+
import * as dialogs from "ui/dialogs";
3+
import {ModalDialogService, ModalDialogOptions, ModalDialogHost} from "../../nativescript-angular/directives/dialogs";
4+
import {ModalContent} from "./modal-content";
5+
6+
@Component({
7+
selector: 'modal-test',
8+
directives: [ModalDialogHost],
9+
providers: [ModalDialogService],
10+
template: `
11+
<GridLayout rows="*, auto" modal-dialog-host>
12+
<StackLayout verticalAlignment="top" margin="12">
13+
<Button text="show component" (tap)="showModal(false)"></Button>
14+
<Button text="show component fullscreen" (tap)="showModal(true)"></Button>
15+
16+
<Button text="alert" (tap)="showAlert()"></Button>
17+
<Button text="confirm" (tap)="showConfirm()"></Button>
18+
<Button text="prompt" (tap)="showPrompt()"></Button>
19+
20+
<Button text="action" (tap)="showAction()"></Button>
21+
<Button text="login" (tap)="showLogin()"></Button>
22+
</StackLayout>
23+
24+
<Label [text]="'RESULT: ' + result" row="1" margin="12"></Label>
25+
</GridLayout>
26+
`
27+
})
28+
export class ModalTest {
29+
public result: string = "result";
30+
31+
constructor(private modal: ModalDialogService) {
32+
}
33+
34+
public showModal(fullscreen: boolean) {
35+
var options: ModalDialogOptions = {
36+
context: { promptMsg: "This is the prompt message!" },
37+
fullscreen: fullscreen
38+
};
39+
40+
this.modal.showModal(ModalContent, options).then((res: string) => {
41+
this.result = res || "empty result";
42+
})
43+
}
44+
45+
public showAlert() {
46+
dialogs.alert({
47+
title: "Alert Title",
48+
message: "The name will change.",
49+
okButtonText: "OK"
50+
}).then(() => {
51+
this.result = "alert closed";
52+
});
53+
}
54+
55+
public showConfirm() {
56+
dialogs.confirm({
57+
title: "Name",
58+
message: "Do you want to change the name?",
59+
cancelButtonText: "No",
60+
neutralButtonText: "Ignore",
61+
okButtonText: "Yes"
62+
}).then((confirmResult) => {
63+
this.result = confirmResult + "";
64+
});
65+
}
66+
67+
public showPrompt() {
68+
dialogs.prompt({
69+
title: "Name",
70+
message: "Enter name:",
71+
cancelButtonText: "Cancel",
72+
neutralButtonText: "Ignore",
73+
okButtonText: "OK",
74+
defaultText: "John Reese",
75+
inputType: dialogs.inputType.text
76+
}).then((promptResult) => {
77+
this.result = promptResult.result ? promptResult.text : "no result";
78+
});
79+
}
80+
81+
public showAction() {
82+
dialogs.action({
83+
message: "Choose action:",
84+
cancelButtonText: "Close",
85+
actions: ["Foo", "Bar"]
86+
}).then((actionResult) => {
87+
this.result = actionResult;
88+
});
89+
}
90+
91+
public showLogin() {
92+
dialogs.login({
93+
title: "Name",
94+
message: "Enter name:",
95+
cancelButtonText: "Cancel",
96+
neutralButtonText: "Ignore",
97+
okButtonText: "OK",
98+
userName: "John",
99+
password: "Reese"
100+
}).then((loginResult) => {
101+
this.result = loginResult.result ? ("user: " + loginResult.userName + " pass: " + loginResult.password) : "no result";
102+
});
103+
}
104+
}

ng-sample/app/examples/navigation/nav-component.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class DetailComponent {
7373
directives: [ROUTER_DIRECTIVES, NS_ROUTER_DIRECTIVES],
7474
template: `
7575
<GridLayout rows="auto, auto, auto, *" columns="*, *" margin="10" backgroundColor="lightgreen">
76-
<Label [text]="'Componenet ID: ' + compId" colSpan="2" row="0"
76+
<Label [text]="'Component ID: ' + compId" colSpan="2" row="0"
7777
style="font-size: 30; horizontal-align: center"></Label>
7878
7979
<Label [text]="'Depth: ' + depth" colSpan="2" row="1"
@@ -108,29 +108,29 @@ export class NavComponent implements OnActivate, OnDeactivate {
108108
this.compId = NavComponent.counter;
109109
this.depth = parseInt(params.get("depth"));
110110

111-
console.log("NavComponent.constructor() componenetID: " + this.compId)
111+
console.log("NavComponent.constructor() componentID: " + this.compId)
112112
}
113113

114114
public goBack() {
115115
this.location.back();
116116
}
117117

118118
routerOnActivate(nextInstruction: ComponentInstruction, prevInstruction: ComponentInstruction): any {
119-
console.log("NavComponent.routerOnActivate() componenetID: " + this.compId)
119+
console.log("NavComponent.routerOnActivate() componentID: " + this.compId)
120120
}
121121

122122
routerOnDeactivate(nextInstruction: ComponentInstruction, prevInstruction: ComponentInstruction): any {
123-
console.log("NavComponent.routerOnDeactivate() componenetID: " + this.compId)
123+
console.log("NavComponent.routerOnDeactivate() componentID: " + this.compId)
124124
}
125125

126126
routerCanReuse(nextInstruction: ComponentInstruction, prevInstruction: ComponentInstruction): any {
127127
// Reuse if depth is the same.
128128
var reuse = (prevInstruction.params["depth"] === nextInstruction.params["depth"]);
129-
console.log("NavComponent.routerCanReuse() componenetID: " + this.compId + " return: " + reuse);
129+
console.log("NavComponent.routerCanReuse() componentID: " + this.compId + " return: " + reuse);
130130
return reuse;
131131
}
132132

133133
routerOnReuse(nextInstruction: ComponentInstruction, prevInstruction: ComponentInstruction): any {
134-
console.log("NavComponent.routerOnReuse() componenetID: " + this.compId);
134+
console.log("NavComponent.routerOnReuse() componentID: " + this.compId);
135135
}
136136
}

ng-sample/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
},
2424
"homepage": "https://github.com/NativeScript/template-hello-world",
2525
"dependencies": {
26-
"tns-core-modules": "^2.0.0-angular-0",
26+
"tns-core-modules": "^2.0.0-angular-1",
2727
"angular2": "2.0.0-beta.9",
2828
"parse5": "1.4.2",
2929
"punycode": "1.3.2",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
},
1515
"scripts": {},
1616
"dependencies": {
17-
"tns-core-modules": "^2.0.0-angular-0",
17+
"tns-core-modules": "^2.0.0-angular-1",
1818
"angular2": "2.0.0-beta.9",
1919
"es6-shim": "^0.33.3",
2020
"parse5": "1.4.2",
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import {DynamicComponentLoader, ComponentRef, ElementRef, Component, Type} from 'angular2/core';
2+
3+
/**
4+
* Wrapper component used for loading compnenets when navigating
5+
* It uses DetachedContainer as selector so that it is elementRef is not attached to the visual tree.
6+
*/
7+
@Component({
8+
selector: 'DetachedContainer',
9+
template: `<Placeholder #loader></Placeholder>`
10+
})
11+
export class DetachedLoader {
12+
constructor(
13+
private element: ElementRef,
14+
private loader: DynamicComponentLoader
15+
) {
16+
}
17+
18+
public loadComponent(componentType: Type): Promise<ComponentRef> {
19+
return this.loader.loadIntoLocation(componentType, this.element, 'loader');
20+
}
21+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import {DynamicComponentLoader, ElementRef, Injector, provide, Type, Injectable, ComponentRef, Directive} from 'angular2/core';
2+
import {Page} from 'ui/page';
3+
import {View} from 'ui/core/view';
4+
import {DetachedLoader} from '../common/detached-loader';
5+
6+
export interface ModalDialogOptions {
7+
context?: any;
8+
fullscreen?: boolean;
9+
}
10+
11+
export class ModalDialogParams {
12+
constructor(
13+
public context: any = {},
14+
public closeCallback: (...args) => any) {
15+
}
16+
}
17+
18+
@Injectable()
19+
export class ModalDialogService {
20+
private elementRef: ElementRef;
21+
22+
constructor(
23+
private page: Page,
24+
private loader: DynamicComponentLoader) {
25+
}
26+
27+
public registerElementRef(ref: ElementRef) {
28+
this.elementRef = ref;
29+
}
30+
31+
public showModal(type: Type, options: ModalDialogOptions): Promise<any> {
32+
if (!this.elementRef) {
33+
throw new Error("No elementRef: Make sure you have the modal-dialog-host directive inside your component.");
34+
}
35+
return new Promise((resove, reject) => {
36+
const page = new Page();
37+
38+
var detachedLoaderRef: ComponentRef;
39+
const closeCallback = (...args) => {
40+
resove.apply(undefined, args);
41+
page.closeModal();
42+
detachedLoaderRef.dispose();
43+
}
44+
const modalParams = new ModalDialogParams(options.context, closeCallback);
45+
46+
const providers = Injector.resolve([
47+
provide(Page, { useValue: page }),
48+
provide(ModalDialogParams, { useValue: modalParams }),
49+
]);
50+
51+
this.loader.loadNextToLocation(DetachedLoader, this.elementRef, providers)
52+
.then((loaderRef) => {
53+
detachedLoaderRef = loaderRef;
54+
return (<DetachedLoader>loaderRef.instance).loadComponent(type)
55+
})
56+
.then((compRef) => {
57+
//Component loaded. Find its root native view.
58+
const componentView = <View>compRef.location.nativeElement;
59+
if (componentView.parent) {
60+
(<any>componentView.parent).removeChild(componentView);
61+
}
62+
page.content = componentView;
63+
this.page.showModal(page, options.context, closeCallback, options.fullscreen);
64+
});
65+
})
66+
}
67+
}
68+
69+
70+
@Directive({
71+
selector: "[modal-dialog-host]"
72+
})
73+
export class ModalDialogHost {
74+
constructor(elementRef: ElementRef, modalService: ModalDialogService) {
75+
modalService.registerElementRef(elementRef);
76+
}
77+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import {Type} from 'angular2/core';
2+
3+
export interface ModalDialogOptions {
4+
context?: any;
5+
fullscreen?: boolean;
6+
}
7+
8+
export class ModalDialogParams {
9+
public context: any;
10+
public closeCallback: (...args) => any;
11+
}
12+
13+
export class ModalDialogService {
14+
public showModal(type: Type, options: ModalDialogOptions): Promise<any>;
15+
}
16+
17+
export class ModalDialogHost {
18+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export {
2+
ModalDialogHost,
3+
ModalDialogOptions,
4+
ModalDialogParams,
5+
ModalDialogService
6+
} from "./directives/dialogs";

0 commit comments

Comments
 (0)