Skip to content

Commit 4241cb9

Browse files
committed
Modal dialog service
1 parent d520da5 commit 4241cb9

File tree

5 files changed

+241
-38
lines changed

5 files changed

+241
-38
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);
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import {Component, DynamicComponentLoader, ElementRef, Injector, provide, Type, Injectable, Host, ComponentRef} from 'angular2/core';
2+
import {Page, ShownModallyData} from 'ui/page';
3+
import {View} from 'ui/core/view';
4+
import * as dialogs from "ui/dialogs";
5+
import {ModalDialogService, ModalDialogOptions, ModalDialogParams} from "../../nativescript-angular/services/dialogs";
6+
7+
@Component({
8+
selector: 'modal-content',
9+
template: `
10+
<StackLayout margin="24" horizontalAlignment="center" verticalAlignment="center">
11+
<Label [text]="prompt"></Label>
12+
<StackLayout orientation="horizontal" marginTop="12">
13+
<Button text="ok" (tap)="close('OK')"></Button>
14+
<Button text="cancel" (tap)="close('Cancel')"></Button>
15+
</StackLayout>
16+
</StackLayout>
17+
`
18+
})
19+
export class ModalContent {
20+
public prompt: string;
21+
constructor(private params: ModalDialogParams) {
22+
console.log("ModalContent.constructor: " + JSON.stringify(params))
23+
this.prompt = params.context.promptMsg;
24+
}
25+
26+
public close(res: string) {
27+
this.params.closeCallback(res);
28+
}
29+
30+
ngOnInit() {
31+
console.log("ModalContent.ngOnInit");
32+
33+
}
34+
35+
ngOnDestroy() {
36+
console.log("ModalContent.ngOnDestroy");
37+
}
38+
}
39+
40+
@Component({
41+
selector: 'modal-test',
42+
directives: [],
43+
providers: [ModalDialogService],
44+
template: `
45+
<GridLayout rows="*, auto">
46+
<StackLayout verticalAlignment="top" margin="12">
47+
<Button text="show component" (tap)="showModal(false)"></Button>
48+
<Button text="show component fullscreen" (tap)="showModal(true)"></Button>
49+
50+
<Button text="alert" (tap)="showAlert()"></Button>
51+
<Button text="confirm" (tap)="showConfirm()"></Button>
52+
<Button text="prompt" (tap)="showPrompt()"></Button>
53+
54+
<Button text="action" (tap)="showAction()"></Button>
55+
<Button text="login" (tap)="showLogin()"></Button>
56+
</StackLayout>
57+
58+
<Label [text]="'RESULT: ' + result" row="1" margin="12"></Label>
59+
</GridLayout>
60+
`
61+
})
62+
export class ModalTest {
63+
public result: string = "result";
64+
65+
constructor(
66+
private elementRef: ElementRef,
67+
private modal: ModalDialogService) {
68+
}
69+
70+
public dialog() {
71+
dialogs.alert({ title: "alert title", message: "alert message", okButtonText: "KO, NE!" });
72+
}
73+
74+
public showModal(fullscreen: boolean) {
75+
var options = new ModalDialogOptions({ promptMsg: "This is the prompt message!" }, fullscreen);
76+
77+
this.modal.showModal(ModalContent, this.elementRef, options).then((res: string) => {
78+
this.result = res || "empty result";
79+
})
80+
}
81+
82+
public showAlert() {
83+
dialogs.alert({
84+
title: "Alert Title",
85+
message: "The name will change.",
86+
okButtonText: "OK"
87+
}).then(() => {
88+
this.result = "alert closed";
89+
});
90+
}
91+
92+
public showConfirm() {
93+
dialogs.confirm({
94+
title: "Name",
95+
message: "Do you want to change the name?",
96+
cancelButtonText: "No",
97+
neutralButtonText: "Ignore",
98+
okButtonText: "Yes"
99+
}).then((confirmResult) => {
100+
this.result = confirmResult + "";
101+
});
102+
}
103+
104+
public showPrompt() {
105+
dialogs.prompt({
106+
title: "Name",
107+
message: "Enter name:",
108+
cancelButtonText: "Cancel",
109+
neutralButtonText: "Ignore",
110+
okButtonText: "OK",
111+
defaultText: "John Reese",
112+
inputType: dialogs.inputType.text
113+
}).then((promptResult) => {
114+
this.result = promptResult.result ? promptResult.text : "no result";
115+
});
116+
}
117+
118+
public showAction() {
119+
dialogs.action({
120+
message: "Choose action:",
121+
cancelButtonText: "Close",
122+
actions: ["Foo", "Bar"]
123+
}).then((actionResult) => {
124+
this.result = actionResult;
125+
});
126+
}
127+
128+
public showLogin() {
129+
dialogs.login({
130+
title: "Name",
131+
message: "Enter name:",
132+
cancelButtonText: "Cancel",
133+
neutralButtonText: "Ignore",
134+
okButtonText: "OK",
135+
userName: "John",
136+
password: "Reese"
137+
}).then((loginResult) => {
138+
this.result = loginResult.result ? ("user: " + loginResult.userName + " pass: " + loginResult.password) : "no result";
139+
});
140+
}
141+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {DynamicComponentLoader, ComponentRef, ElementRef, Component, Type} from 'angular2/core';
2+
3+
/**
4+
* Page shim used for loading compnenets when navigating
5+
*/
6+
@Component({
7+
selector: 'nativescript-page-shim',
8+
template: `
9+
<DetachedContainer>
10+
<Placeholder #loader></Placeholder>
11+
</DetachedContainer>
12+
`
13+
})
14+
export class DetachedLoader {
15+
constructor(
16+
private element: ElementRef,
17+
private loader: DynamicComponentLoader
18+
) {
19+
}
20+
21+
public loadComponent(componentType: Type): Promise<ComponentRef> {
22+
return this.loader.loadIntoLocation(componentType, this.element, 'loader');
23+
}
24+
}

src/nativescript-angular/router/page-router-outlet.ts

Lines changed: 14 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import {PromiseWrapper} from 'angular2/src/facade/async';
22
import {isBlank, isPresent} from 'angular2/src/facade/lang';
33
import {StringMapWrapper} from 'angular2/src/facade/collection';
44

5-
import {Attribute, DynamicComponentLoader, ComponentRef, ElementRef,
6-
Injector, provide, Type, Component, OpaqueToken, Inject} from 'angular2/core';
5+
import {Attribute, DynamicComponentLoader, ComponentRef,
6+
ElementRef, Injector, provide, Type, Component} from 'angular2/core';
77

88
import * as routerHooks from 'angular2/src/router/lifecycle/lifecycle_annotations';
99
import {hasLifecycleHook} from 'angular2/src/router/lifecycle/route_lifecycle_reflector';
@@ -18,12 +18,13 @@ import {topmost} from "ui/frame";
1818
import {Page, NavigatedData} from "ui/page";
1919
import {log} from "./common";
2020
import {NSLocationStrategy} from "./ns-location-strategy";
21+
import {DetachedLoader} from "../common/detached-loader";
2122

2223
let _resolveToTrue = PromiseWrapper.resolve(true);
2324

2425
interface CacheItem {
2526
componentRef: ComponentRef;
26-
pageShimRef?: ComponentRef;
27+
loaderRef?: ComponentRef;
2728
router: Router;
2829
}
2930

@@ -33,8 +34,8 @@ interface CacheItem {
3334
class RefCache {
3435
private cache: Array<CacheItem> = new Array<CacheItem>();
3536

36-
public push(comp: ComponentRef, router: Router, pageShimRef?: ComponentRef) {
37-
this.cache.push({ componentRef: comp, router: router, pageShimRef: pageShimRef });
37+
public push(comp: ComponentRef, router: Router, loaderRef?: ComponentRef) {
38+
this.cache.push({ componentRef: comp, router: router, loaderRef: loaderRef });
3839
}
3940

4041
public pop(): CacheItem {
@@ -46,30 +47,6 @@ class RefCache {
4647
}
4748
}
4849

49-
/**
50-
* Page shim used for loadin compnenets when navigating
51-
*/
52-
@Component({
53-
selector: 'nativescript-page-shim',
54-
template: `
55-
<DetachedContainer>
56-
<Placeholder #loader></Placeholder>
57-
</DetachedContainer>
58-
`
59-
})
60-
class PageShim {
61-
constructor(
62-
private element: ElementRef,
63-
private loader: DynamicComponentLoader
64-
) {
65-
}
66-
67-
public loadComponent(componentType: Type): Promise<ComponentRef> {
68-
return this.loader.loadIntoLocation(componentType, this.element, 'loader');
69-
}
70-
}
71-
72-
7350
/**
7451
* A router outlet that does page navigation in NativeScript
7552
*
@@ -136,7 +113,7 @@ export class PageRouterOutlet extends RouterOutlet {
136113
private activateOnGoForward(nextInstruction: ComponentInstruction, previousInstruction: ComponentInstruction): Promise<any> {
137114
let componentType = nextInstruction.componentType;
138115
let resultPromise: Promise<any>;
139-
let pageShimRef: ComponentRef = undefined;
116+
let loaderRef: ComponentRef = undefined;
140117
const childRouter = this.parentRouter.childRouter(componentType);
141118

142119
const providersArray = [
@@ -150,14 +127,14 @@ export class PageRouterOutlet extends RouterOutlet {
150127
this.isInitalPage = false;
151128
resultPromise = this.loader.loadNextToLocation(componentType, this.elementRef, Injector.resolve(providersArray));
152129
} else {
153-
log("PageRouterOutlet.activate() forward navigation - create page shim in the loader container: " + componentType.name);
130+
log("PageRouterOutlet.activate() forward navigation - create detached loader in the loader container: " + componentType.name);
154131

155132
const page = new Page();
156133
providersArray.push(provide(Page, { useValue: page }));
157-
resultPromise = this.loader.loadIntoLocation(PageShim, this.elementRef, "loader", Injector.resolve(providersArray))
134+
resultPromise = this.loader.loadIntoLocation(DetachedLoader, this.elementRef, "loader", Injector.resolve(providersArray))
158135
.then((pageComponentRef) => {
159-
pageShimRef = pageComponentRef;
160-
return (<PageShim>pageShimRef.instance).loadComponent(componentType);
136+
loaderRef = pageComponentRef;
137+
return (<DetachedLoader>loaderRef.instance).loadComponent(componentType);
161138
})
162139
.then((actualCoponenetRef) => {
163140
return this.loadComponentInPage(page, actualCoponenetRef);
@@ -166,7 +143,7 @@ export class PageRouterOutlet extends RouterOutlet {
166143

167144
return resultPromise.then((componentRef) => {
168145
this.componentRef = componentRef;
169-
this.refCache.push(componentRef, childRouter, pageShimRef);
146+
this.refCache.push(componentRef, childRouter, loaderRef);
170147

171148
if (hasLifecycleHook(routerHooks.routerOnActivate, componentType)) {
172149
return (<OnActivate>this.componentRef.instance)
@@ -238,8 +215,8 @@ export class PageRouterOutlet extends RouterOutlet {
238215
this.componentRef = null;
239216
}
240217

241-
if (isPresent(popedItem.pageShimRef)) {
242-
popedItem.pageShimRef.dispose();
218+
if (isPresent(popedItem.loaderRef)) {
219+
popedItem.loaderRef.dispose();
243220
}
244221
});
245222
} else {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import {DynamicComponentLoader, ElementRef, Injector, provide, Type, Injectable, ComponentRef} 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 class ModalDialogOptions {
7+
constructor(
8+
public context: any = {},
9+
public fullscreen: boolean = true) {
10+
}
11+
}
12+
13+
export class ModalDialogParams {
14+
constructor(
15+
public context: any = {},
16+
public closeCallback: (...args) => any) {
17+
}
18+
}
19+
20+
@Injectable()
21+
export class ModalDialogService {
22+
constructor(
23+
private page: Page,
24+
private loader: DynamicComponentLoader) {
25+
}
26+
27+
public showModal(type: Type, elementRef: ElementRef, options: ModalDialogOptions): Promise<any> {
28+
return new Promise((resove, reject) => {
29+
const page = new Page();
30+
31+
var detachedLoaderRef: ComponentRef;
32+
const closeCallback = (...args) => {
33+
resove.apply(undefined, args);
34+
page.closeModal();
35+
detachedLoaderRef.dispose();
36+
}
37+
const modalParams = new ModalDialogParams(options.context, closeCallback);
38+
39+
const providers = Injector.resolve([
40+
provide(Page, { useValue: page }),
41+
provide(ModalDialogParams, { useValue: modalParams }),
42+
]);
43+
44+
this.loader.loadNextToLocation(DetachedLoader, elementRef, providers)
45+
.then((loaderRef) => {
46+
detachedLoaderRef = loaderRef;
47+
return (<DetachedLoader>loaderRef.instance).loadComponent(type)
48+
})
49+
.then((compRef) => {
50+
//Component loaded. Find its root native view.
51+
const componenetView = <View>compRef.location.nativeElement;
52+
if (componenetView.parent) {
53+
(<any>componenetView.parent).removeChild(componenetView);
54+
}
55+
page.content = componenetView;
56+
(<any>page)._showNativeModalView(this.page, options.context, closeCallback, options.fullscreen);
57+
});
58+
})
59+
}
60+
}

0 commit comments

Comments
 (0)