diff --git a/e2e/modal-navigation-ng/app/app.component.ts b/e2e/modal-navigation-ng/app/app.component.ts index b95dd6f01..a86fef745 100644 --- a/e2e/modal-navigation-ng/app/app.component.ts +++ b/e2e/modal-navigation-ng/app/app.component.ts @@ -1,8 +1,21 @@ import { Component } from "@angular/core"; +import { Router, NavigationEnd } from "@angular/router"; +import { NSLocationStrategy } from "nativescript-angular/router/ns-location-strategy"; @Component({ selector: "ns-app", templateUrl: "app.component.html", }) -export class AppComponent { } +export class AppComponent { + constructor(router: Router, location: NSLocationStrategy) { + router.events.subscribe(e => { + // console.log("[ROUTER]: " + e.toString()); + + if (e instanceof NavigationEnd) { + console.log("[ROUTER]: " + e.toString()); + console.log(location.toString()); + } + }); + } +} diff --git a/e2e/modal-navigation-ng/app/app.module.ts b/e2e/modal-navigation-ng/app/app.module.ts index 5f3f2e17a..87e0f0857 100644 --- a/e2e/modal-navigation-ng/app/app.module.ts +++ b/e2e/modal-navigation-ng/app/app.module.ts @@ -6,6 +6,8 @@ import { AppComponent } from "./app.component"; import { ItemService } from "./item/item.service"; import { ItemsComponent } from "./item/items.component"; import { ItemDetailComponent } from "./item/item-detail.component"; +import { HomeComponent } from "./home/home.component"; +import { ModalTest } from "./modal/modal-test"; // Uncomment and add to NgModule imports if you need to use two-way binding // import { NativeScriptFormsModule } from "nativescript-angular/forms"; @@ -21,10 +23,14 @@ import { ItemDetailComponent } from "./item/item-detail.component"; NativeScriptModule, AppRoutingModule ], + entryComponents: [...ModalTest.entries], declarations: [ AppComponent, ItemsComponent, - ItemDetailComponent + ItemDetailComponent, + HomeComponent, + ModalTest, + ...ModalTest.entries ], providers: [ ItemService diff --git a/e2e/modal-navigation-ng/app/app.routing.ts b/e2e/modal-navigation-ng/app/app.routing.ts index 26c691b5d..b02a5ceb0 100644 --- a/e2e/modal-navigation-ng/app/app.routing.ts +++ b/e2e/modal-navigation-ng/app/app.routing.ts @@ -2,13 +2,20 @@ import { NgModule } from "@angular/core"; import { NativeScriptRouterModule } from "nativescript-angular/router"; import { Routes } from "@angular/router"; +import { HomeComponent } from "./home/home.component"; import { ItemsComponent } from "./item/items.component"; import { ItemDetailComponent } from "./item/item-detail.component"; +import { ModalTest } from "./modal/modal-test"; const routes: Routes = [ - { path: "", redirectTo: "/items", pathMatch: "full" }, - { path: "items", component: ItemsComponent }, - { path: "item/:id", component: ItemDetailComponent }, + { path: "", redirectTo: "/home", pathMatch: "full" }, + + { path: "home", component: HomeComponent }, + + { path: "modal", component: ModalTest , children: [ + { path: "items", component: ItemsComponent }, + { path: "item/:id", component: ItemDetailComponent }, + ] }, ]; @NgModule({ diff --git a/e2e/modal-navigation-ng/app/home/home.component.ts b/e2e/modal-navigation-ng/app/home/home.component.ts new file mode 100644 index 000000000..8d52b9051 --- /dev/null +++ b/e2e/modal-navigation-ng/app/home/home.component.ts @@ -0,0 +1,21 @@ +import { Component, ViewContainerRef } from "@angular/core"; +import { RouterExtensions } from "nativescript-angular/router"; + +@Component({ + selector: "modal-test", + template: ` + + + + ` +}) +export class HomeComponent { + + public result: string = "result"; + + constructor(private _routerExtensions: RouterExtensions, private vcRef: ViewContainerRef) { } + + public gotToModal() { + this._routerExtensions.navigate(["/modal"]); //{clearHistory: true} + } +} diff --git a/e2e/modal-navigation-ng/app/item/item-detail.component.ts b/e2e/modal-navigation-ng/app/item/item-detail.component.ts index 9183f7876..0fedf56b0 100644 --- a/e2e/modal-navigation-ng/app/item/item-detail.component.ts +++ b/e2e/modal-navigation-ng/app/item/item-detail.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from "@angular/core"; +import { Component, OnInit, OnDestroy } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; import { Item } from "./item"; @@ -9,7 +9,7 @@ import { ItemService } from "./item.service"; moduleId: module.id, templateUrl: "./item-detail.component.html", }) -export class ItemDetailComponent implements OnInit { +export class ItemDetailComponent implements OnInit, OnDestroy { item: Item; constructor( @@ -21,4 +21,7 @@ export class ItemDetailComponent implements OnInit { const id = +this.route.snapshot.params["id"]; this.item = this.itemService.getItem(id); } + + ngOnDestroy(): void { + } } diff --git a/e2e/modal-navigation-ng/app/item/items.component.html b/e2e/modal-navigation-ng/app/item/items.component.html index 55e87e3ab..6ae2a4ae8 100644 --- a/e2e/modal-navigation-ng/app/item/items.component.html +++ b/e2e/modal-navigation-ng/app/item/items.component.html @@ -26,7 +26,7 @@ - diff --git a/e2e/modal-navigation-ng/app/modal/modal-content.ts b/e2e/modal-navigation-ng/app/modal/modal-content.ts new file mode 100644 index 000000000..249227ac4 --- /dev/null +++ b/e2e/modal-navigation-ng/app/modal/modal-content.ts @@ -0,0 +1,50 @@ +import { Component, Input } from "@angular/core"; +import { ModalDialogParams } from "nativescript-angular/directives/dialogs"; +import { RouterExtensions, PageRoute } from "nativescript-angular/router"; +import { ActivatedRoute } from "@angular/router"; +import { View } from "ui/core/view" + +@Component({ + selector: "modal-content", + template: ` + + + + + + + + + + + ` +}) +export class ModalContent { + + @Input() public prompt: string; + public yes: boolean = true; + + constructor(private params: ModalDialogParams, private nav: RouterExtensions, private activeRoute: ActivatedRoute) { + console.log("ModalContent.constructor: " + JSON.stringify(params)); + this.prompt = params.context.promptMsg; + } + + public back() { + this.nav.back(); + } + + public close(layoutRoot: View) { + layoutRoot.closeModal(); + // this.params.closeCallback(res); + } + + ngOnInit() { + this.nav.navigate(["items"], { relativeTo: this.activeRoute }); + console.log("ModalContent.ngOnInit"); + } + + ngOnDestroy() { + console.log("ModalContent.ngOnDestroy"); + } + +} diff --git a/e2e/modal-navigation-ng/app/modal/modal-nested-test.ts b/e2e/modal-navigation-ng/app/modal/modal-nested-test.ts new file mode 100644 index 000000000..d1ae043f2 --- /dev/null +++ b/e2e/modal-navigation-ng/app/modal/modal-nested-test.ts @@ -0,0 +1,23 @@ +import { Component, ViewContainerRef } from "@angular/core"; +import * as dialogs from "ui/dialogs"; +import { ModalDialogService, ModalDialogOptions } from "nativescript-angular/directives/dialogs"; +import { ModalContent } from "./modal-content"; +import { ModalTest } from "./modal-test"; + +@Component({ + selector: "modal-nested-test", + template: ` + + ` +}) +export class ModalNestedTest { + + static entries = [ + ModalContent + ]; + + static exports = [ + ModalTest + ]; + +} \ No newline at end of file diff --git a/e2e/modal-navigation-ng/app/modal/modal-test.ts b/e2e/modal-navigation-ng/app/modal/modal-test.ts new file mode 100644 index 000000000..9e7de20db --- /dev/null +++ b/e2e/modal-navigation-ng/app/modal/modal-test.ts @@ -0,0 +1,109 @@ +import { Component, ViewContainerRef } from "@angular/core"; +import * as dialogs from "ui/dialogs"; +import { ModalDialogService, ModalDialogOptions } from "nativescript-angular/directives/dialogs"; +import { ModalContent } from "./modal-content"; + +@Component({ + selector: "modal-test", + template: ` + + + + + + + + + + + + + + ` +}) +export class ModalTest { + + public result: string = "result"; + + constructor(private modal: ModalDialogService, private vcRef: ViewContainerRef) { } + + static entries = [ + ModalContent + ]; + + public showModal(fullscreen: boolean) { + const options: ModalDialogOptions = { + context: { promptMsg: "This is the prompt message!" }, + fullscreen: fullscreen, + viewContainerRef: this.vcRef + }; + + this.modal.showModal(ModalContent, options).then((res: string) => { + this.result = res || "empty result"; + // console.log("MODAL:" + this.result); + }); + } + + public showAlert() { + dialogs.alert({ + title: "Alert Title", + message: "The name will change.", + okButtonText: "OK" + }).then(() => { + this.result = "alert closed"; + }); + } + + public showConfirm() { + dialogs.confirm({ + title: "Name", + message: "Do you want to change the name?", + cancelButtonText: "No", + neutralButtonText: "Ignore", + okButtonText: "Yes" + }).then((confirmResult) => { + this.result = confirmResult + ""; + }); + } + + public showPrompt() { + dialogs.prompt({ + title: "Name", + message: "Enter name:", + cancelButtonText: "Cancel", + neutralButtonText: "Ignore", + okButtonText: "OK", + defaultText: "John Reese", + inputType: dialogs.inputType.text + }).then((promptResult) => { + this.result = promptResult.result ? promptResult.text : "no result"; + }); + } + + public showAction() { + dialogs.action({ + message: "Choose action:", + cancelButtonText: "Close", + actions: ["Foo", "Bar"] + }).then((actionResult) => { + this.result = actionResult; + }); + } + + public showLogin() { + dialogs.login({ + title: "Name", + message: "Enter name:", + cancelButtonText: "Cancel", + neutralButtonText: "Ignore", + okButtonText: "OK", + userName: "John", + password: "Reese" + }).then((loginResult) => { + this.result = loginResult.result ? + ("user: " + loginResult.userName + " pass: " + loginResult.password) : + "no result"; + }); + } + +} diff --git a/e2e/modal-navigation-ng/e2e/config/appium.capabilities.json b/e2e/modal-navigation-ng/e2e/config/appium.capabilities.json deleted file mode 100644 index 64f28061b..000000000 --- a/e2e/modal-navigation-ng/e2e/config/appium.capabilities.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "android19": { - "platformName": "Android", - "platformVersion": "4.4", - "deviceName": "Emulator-Api19-Default", - "avd": "Emulator-Api19-Default", - "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", - "newCommandTimeout": 720, - "noReset": true, - "fullReset": false, - "app": "" - }, - "android21": { - "platformName": "Android", - "platformVersion": "5.0", - "deviceName": "Emulator-Api21-Default", - "avd": "Emulator-Api21-Default", - "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", - "newCommandTimeout": 720, - "noReset": true, - "fullReset": false, - "app": "" - }, - "android23": { - "platformName": "Android", - "platformVersion": "6.0", - "deviceName": "Emulator-Api23-Default", - "avd": "Emulator-Api23-Default", - "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", - "newCommandTimeout": 720, - "noReset": true, - "fullReset": false, - "app": "" - }, - "android24": { - "platformName": "Android", - "platformVersion": "7.0", - "deviceName": "Emulator-Api24-Default", - "avd": "Emulator-Api24-Default", - "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", - "newCommandTimeout": 720, - "noReset": true, - "fullReset": false, - "app": "" - }, - "android25": { - "platformName": "Android", - "platformVersion": "7.1", - "deviceName": "Emulator-Api25-Google", - "avd": "Emulator-Api25-Google", - "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", - "newCommandTimeout": 720, - "noReset": true, - "fullReset": false, - "app": "" - }, - "android26": { - "platformName": "Android", - "platformVersion": "8.0", - "deviceName": "Emulator-Api26-Google", - "avd": "Emulator-Api26-Google", - "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", - "newCommandTimeout": 720, - "noReset": true, - "fullReset": false, - "app": "" - }, - "sim.iPhone7.iOS100": { - "platformName": "iOS", - "platformVersion": "10.0", - "deviceName": "iPhone 7 100", - "noReset": true, - "fullReset": false, - "app": "" - }, - "sim.iPhone8.iOS110": { - "platformName": "iOS", - "platformVersion": "11.0", - "deviceName": "iPhone 8 110", - "noReset": true, - "fullReset": false, - "app": "" - }, - "sim.iPhoneX.iOS110": { - "platformName": "iOS", - "platformVersion": "11.0", - "deviceName": "iPhone X", - "noReset": true, - "fullReset": false, - "app": "" - } -} diff --git a/e2e/modal-navigation-ng/e2e/config/mocha.opts b/e2e/modal-navigation-ng/e2e/config/mocha.opts deleted file mode 100644 index 796ec4724..000000000 --- a/e2e/modal-navigation-ng/e2e/config/mocha.opts +++ /dev/null @@ -1,4 +0,0 @@ ---timeout 80000 ---recursive e2e ---reporter mocha-multi ---reporter-options spec=-,mocha-junit-reporter=test-results.xml \ No newline at end of file diff --git a/e2e/router-tab-view/app/app.component.html b/e2e/router-tab-view/app/app.component.html index e148fd408..8872891ae 100644 --- a/e2e/router-tab-view/app/app.component.html +++ b/e2e/router-tab-view/app/app.component.html @@ -1,13 +1,11 @@ + name="playerTab"> + name="teamTab"> \ No newline at end of file diff --git a/e2e/router-tab-view/app/app.component.ts b/e2e/router-tab-view/app/app.component.ts index e0b5f980e..d3e62efe5 100644 --- a/e2e/router-tab-view/app/app.component.ts +++ b/e2e/router-tab-view/app/app.component.ts @@ -16,23 +16,11 @@ export class AppComponent { constructor(router: Router, location: NSLocationStrategy) { router.events.subscribe(e => { - // console.log("[ROUTER]: " + e.toString()); - if (e instanceof NavigationEnd) { this.isInitialNavigation = false; console.log("[ROUTER]: " + e.toString()); - // console.log("[ROUTER] NAVIGATION END. Location history:"); - location._getStates().forEach(state => { - console.log(`[page: ${state.isPageNavigation}] ${state.url}`); - }); + console.log(location.toString()); } }) } - - onActivate(tabIndex: number) { - // if (!this.isInitialNavigation && this.tabView.selectedIndex !== tabIndex) { - // console.log(`---> onActivate changing tabIndex from: ${this.tabView.selectedIndex} to: ${tabIndex}`); - // this.tabView.selectedIndex = tabIndex; - // } - } } diff --git a/e2e/router-tab-view/e2e/config/appium.capabilities.json b/e2e/router-tab-view/e2e/config/appium.capabilities.json deleted file mode 100644 index 080553981..000000000 --- a/e2e/router-tab-view/e2e/config/appium.capabilities.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "android19": { - "platformName": "Android", - "platformVersion": "4.4", - "deviceName": "Emulator-Api19-Default", - "avd": "Emulator-Api19-Default", - "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", - "newCommandTimeout": 720, - "noReset": true, - "fullReset": false, - "app": "" - }, - "android21": { - "platformName": "Android", - "platformVersion": "5.0", - "deviceName": "Emulator-Api21-Default", - "avd": "Emulator-Api21-Default", - "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", - "newCommandTimeout": 720, - "noReset": true, - "fullReset": false, - "app": "" - }, - "android23": { - "platformName": "Android", - "platformVersion": "6.0", - "deviceName": "Emulator-Api23-Default", - "avd": "Emulator-Api23-Default", - "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", - "newCommandTimeout": 720, - "noReset": true, - "fullReset": false, - "app": "" - }, - "android24": { - "platformName": "Android", - "platformVersion": "7.0", - "deviceName": "Emulator-Api24-Default", - "avd": "Emulator-Api24-Default", - "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", - "newCommandTimeout": 720, - "noReset": true, - "fullReset": false, - "app": "" - }, - "android25": { - "platformName": "Android", - "platformVersion": "7.1", - "deviceName": "Emulator-Api25-Google", - "avd": "Emulator-Api25-Google", - "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", - "newCommandTimeout": 720, - "noReset": true, - "fullReset": false, - "app": "" - }, - "android26": { - "platformName": "Android", - "platformVersion": "8.0", - "deviceName": "Emulator-Api26-Google", - "avd": "Emulator-Api26-Google", - "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", - "newCommandTimeout": 720, - "noReset": true, - "fullReset": false, - "app": "" - }, - "sim.iPhone7.iOS100": { - "platformName": "iOS", - "platformVersion": "10.0", - "deviceName": "iPhone 7 100", - "noReset": true, - "fullReset": false, - "app": "" - }, - "sim.iPhone7.iOS112": { - "platformName": "iOS", - "platformVersion": "11.2", - "deviceName": "iPhone 7", - "noReset": true, - "fullReset": false, - "app": "" - }, - "sim.iPhone8.iOS110": { - "platformName": "iOS", - "platformVersion": "11.0", - "deviceName": "iPhone 8 110", - "noReset": true, - "fullReset": false, - "app": "" - }, - "sim.iPhoneX.iOS110": { - "platformName": "iOS", - "platformVersion": "11.0", - "deviceName": "iPhone X", - "noReset": true, - "fullReset": false, - "app": "" - } -} diff --git a/e2e/router-tab-view/e2e/config/mocha.opts b/e2e/router-tab-view/e2e/config/mocha.opts deleted file mode 100644 index 796ec4724..000000000 --- a/e2e/router-tab-view/e2e/config/mocha.opts +++ /dev/null @@ -1,4 +0,0 @@ ---timeout 80000 ---recursive e2e ---reporter mocha-multi ---reporter-options spec=-,mocha-junit-reporter=test-results.xml \ No newline at end of file diff --git a/e2e/router-tab-view/e2e/tab-view-navigation.e2e-spec.ts b/e2e/router-tab-view/e2e/tab-view-navigation.e2e-spec.ts index 3bafee864..d169ff7e3 100644 --- a/e2e/router-tab-view/e2e/tab-view-navigation.e2e-spec.ts +++ b/e2e/router-tab-view/e2e/tab-view-navigation.e2e-spec.ts @@ -19,7 +19,7 @@ describe("TabView with page-router-outlet in each tab", () => { } }); - it("should find an tabs by text", async () => { + it("should find any tabs by text", async () => { await driver.findElementByText("Players", SearchOptions.exact); await driver.findElementByText("Teams", SearchOptions.exact); await driver.findElementByText("Player List", SearchOptions.exact); diff --git a/e2e/single-page/app/second/second.component.ts b/e2e/single-page/app/second/second.component.ts index 516cc2642..1cfa15616 100644 --- a/e2e/single-page/app/second/second.component.ts +++ b/e2e/single-page/app/second/second.component.ts @@ -4,6 +4,7 @@ import { ActivatedRoute, Router, Route } from "@angular/router"; import { Page } from "ui/page"; import { Observable } from "rxjs"; import { map } from "rxjs/operators"; +import { RouterExtensions } from "nativescript-angular/router"; @Component({ selector: "second", @@ -18,11 +19,12 @@ import { map } from "rxjs/operators"; + ` }) export class SecondComponent implements OnInit, OnDestroy { public id$: Observable; - constructor(route: ActivatedRoute) { + constructor(route: ActivatedRoute, private routerExtensions: RouterExtensions) { this.id$ = route.params.pipe(map(r => +r["id"])); } @@ -33,4 +35,8 @@ export class SecondComponent implements OnInit, OnDestroy { ngOnDestroy() { console.log("SecondComponent - ngOnDestroy()"); } + + back() { + this.routerExtensions.back(); + } } \ No newline at end of file diff --git a/nativescript-angular/app-host-view.ts b/nativescript-angular/app-host-view.ts index 021567e3a..60c0fca72 100644 --- a/nativescript-angular/app-host-view.ts +++ b/nativescript-angular/app-host-view.ts @@ -1,6 +1,43 @@ import { ContentView } from "tns-core-modules/ui/content-view"; -import { ViewBase } from "tns-core-modules/ui/core/view/view"; +import { GridLayout } from "tns-core-modules/ui/layouts/grid-layout"; +import { ProxyViewContainer } from "tns-core-modules/ui/proxy-view-container"; +import { View } from "tns-core-modules/ui/core/view"; export class AppHostView extends ContentView { - ngAppRoot: ViewBase; + + private _ngAppRoot: View; + private _content: View; + + get ngAppRoot(): View { + return this._ngAppRoot; + } + + set ngAppRoot(value: View) { + this._ngAppRoot = value; + } + + get content(): View { + return this._content; + } + + set content(value: View) { + if (this._content) { + this._content.parentNode = undefined; + } + + this._content = value; + + if (value) { + this._content.parentNode = this; + } + + this.ngAppRoot = value; + + if (this._content instanceof ProxyViewContainer) { + const grid = new GridLayout(); + grid.addChild(this._content); + this.ngAppRoot = grid; + } + } + } diff --git a/nativescript-angular/directives/dialogs.ts b/nativescript-angular/directives/dialogs.ts index 8907f44cb..fcbe436f6 100644 --- a/nativescript-angular/directives/dialogs.ts +++ b/nativescript-angular/directives/dialogs.ts @@ -9,8 +9,9 @@ import { ViewContainerRef, } from "@angular/core"; -import { Page } from "tns-core-modules/ui/page"; +import { NSLocationStrategy } from "../router/ns-location-strategy"; import { View, ViewBase } from "tns-core-modules/ui/core/view"; +import { ProxyViewContainer } from "tns-core-modules/ui/proxy-view-container/proxy-view-container"; import { AppHostView } from "../app-host-view"; import { DetachedLoader } from "../common/detached-loader"; @@ -43,6 +44,9 @@ interface ShowDialogOptions { @Injectable() export class ModalDialogService { + constructor(private location: NSLocationStrategy) { + } + public showModal(type: Type, { viewContainerRef, moduleRef, context, fullscreen }: ModalDialogOptions ): Promise { @@ -65,8 +69,10 @@ export class ModalDialogService { const componentContainer = moduleRef || viewContainerRef; const resolver = componentContainer.injector.get(ComponentFactoryResolver); + this.location._beginModalNavigation(); + return new Promise(resolve => { - setTimeout(() => ModalDialogService.showDialog({ + setTimeout(() => this._showDialog({ containerRef: viewContainerRef, context, doneCallback: resolve, @@ -79,7 +85,7 @@ export class ModalDialogService { }); } - private static showDialog({ + private _showDialog({ containerRef, context, doneCallback, @@ -89,41 +95,48 @@ export class ModalDialogService { resolver, type, }: ShowDialogOptions): void { - const page = pageFactory({ isModal: true, componentType: type }); - + let componentView: View; let detachedLoaderRef: ComponentRef; + const closeCallback = (...args) => { doneCallback.apply(undefined, args); - page.closeModal(); - detachedLoaderRef.instance.detectChanges(); - detachedLoaderRef.destroy(); + if (componentView && !this.location._isModalClosing) { + this.location._beginCloseModalNavigation(); + + componentView.closeModal(); + } else if (this.location._isModalClosing) { + this.location.back(); + this.location._finishCloseModalNavigation(); + detachedLoaderRef.instance.detectChanges(); + detachedLoaderRef.destroy(); + } }; const modalParams = new ModalDialogParams(context, closeCallback); - const providers = ReflectiveInjector.resolve([ - { provide: Page, useValue: page }, { provide: ModalDialogParams, useValue: modalParams }, ]); - const childInjector = ReflectiveInjector.fromResolvedProviders( - providers, containerRef.parentInjector); + const childInjector = ReflectiveInjector.fromResolvedProviders(providers, containerRef.parentInjector); const detachedFactory = resolver.resolveComponentFactory(DetachedLoader); detachedLoaderRef = containerRef.createComponent(detachedFactory, -1, childInjector, null); detachedLoaderRef.instance.loadComponent(type).then((compRef) => { - const componentView = compRef.location.nativeElement; + const detachedProxy = compRef.location.nativeElement; + + if (detachedProxy.getChildrenCount() > 1) { + throw new Error("Modal content has more than one root view."); + } + componentView = detachedProxy.getChildAt(0); if (componentView.parent) { (componentView.parent).removeChild(componentView); } - page.content = componentView; - parentView.showModal(page, context, closeCallback, fullscreen); + parentView.showModal(componentView, context, closeCallback, fullscreen); }); } } - @Directive({ selector: "[modal-dialog-host]" // tslint:disable-line:directive-selector }) diff --git a/nativescript-angular/platform-common.ts b/nativescript-angular/platform-common.ts index b37262098..91ce4df2d 100644 --- a/nativescript-angular/platform-common.ts +++ b/nativescript-angular/platform-common.ts @@ -172,7 +172,7 @@ export class NativeScriptPlatformRef extends PlatformRef { bootstrapLog(`Angular bootstrap bootstrap done. uptime: ${uptime()}`); if (!autoCreateFrame) { - rootContent = this.extractContentFromHost(tempAppHostView); + rootContent = tempAppHostView.content; } lastBootstrappedModule = new WeakRef(moduleRef); @@ -233,7 +233,7 @@ export class NativeScriptPlatformRef extends PlatformRef { onAfterLivesync.next({ moduleRef }); if (!autoCreateFrame) { - rootContent = this.extractContentFromHost(tempAppHostView); + rootContent = tempAppHostView.content; } lastBootstrappedModule = new WeakRef(moduleRef); @@ -282,12 +282,4 @@ export class NativeScriptPlatformRef extends PlatformRef { frame.navigate({ create: () => { return page; } }); return { page, frame }; } - - private extractContentFromHost(tempAppHostView: AppHostView) { - const result = tempAppHostView.content; - tempAppHostView.content = null; - tempAppHostView.ngAppRoot = result; - result.parentNode = tempAppHostView; - return result; - } } diff --git a/nativescript-angular/router/ns-location-strategy.ts b/nativescript-angular/router/ns-location-strategy.ts index ca26635e8..c220d9084 100644 --- a/nativescript-angular/router/ns-location-strategy.ts +++ b/nativescript-angular/router/ns-location-strategy.ts @@ -1,5 +1,6 @@ import { Injectable } from "@angular/core"; import { LocationStrategy } from "@angular/common"; +import { DefaultUrlSerializer, UrlSegmentGroup, UrlTree } from "@angular/router"; import { routerLog } from "../trace"; import { NavigationTransition } from "tns-core-modules/ui/frame"; import { isPresent } from "../lang-facade"; @@ -19,29 +20,51 @@ const defaultNavOptions: NavigationOptions = { export interface LocationState { state: any; title: string; - url: string; queryParams: string; + segmentGroup: UrlSegmentGroup; + isRootSegmentGroup: boolean; isPageNavigation: boolean; + isModalNavigation: boolean; } @Injectable() export class NSLocationStrategy extends LocationStrategy { - private states = new Array(); + private statesByOutlet: { [key: string]: Array } = {}; + private currentUrlTree: UrlTree; + private currentOutlet: string; private popStateCallbacks = new Array<(_: any) => any>(); private _isPageNavigationBack = false; private _currentNavigationOptions: NavigationOptions; + public _isModalClosing = false; + public _isModalNavigation = false; + constructor(private frameService: FrameService) { super(); routerLog("NSLocationStrategy.constructor()"); } path(): string { - let state = this.peekState(); - const result = state ? state.url : "/"; - routerLog("NSLocationStrategy.path(): " + result); - return result; + if (!this.currentUrlTree) { + return "/"; + } + + let tree = this.currentUrlTree; + const state = this.peekState(this.currentOutlet); + + // Handle case where the user declares a component at path "/". + // The url serializer doesn't parse this url as having a primary outlet. + if (state.isRootSegmentGroup) { + tree.root = state.segmentGroup; + } else { + tree.root.children[this.currentOutlet] = state.segmentGroup; + } + + const urlSerializer = new DefaultUrlSerializer(); + const url = urlSerializer.serialize(tree); + routerLog("NSLocationStrategy.path(): " + url); + return url; } prepareExternalUrl(internal: string): string { @@ -50,31 +73,77 @@ export class NSLocationStrategy extends LocationStrategy { } pushState(state: any, title: string, url: string, queryParams: string): void { - routerLog("NSLocationStrategy.pushState state: " + + routerLog("NSLocationStrategy.pushState state: " + `${state}, title: ${title}, url: ${url}, queryParams: ${queryParams}`); this.pushStateInternal(state, title, url, queryParams); } pushStateInternal(state: any, title: string, url: string, queryParams: string): void { - let isNewPage = this.states.length === 0; - this.states.push({ - state: state, - title: title, - url: url, - queryParams: queryParams, - isPageNavigation: isNewPage + const urlSerializer = new DefaultUrlSerializer(); + const stateUrlTree: UrlTree = urlSerializer.parse(url); + const rootOutlets = stateUrlTree.root.children; + + this.currentUrlTree = stateUrlTree; + + // Handle case where the user declares a component at path "/". + // The url serializer doesn't parse this url as having a primary outlet. + if (!Object.keys(rootOutlets).length) { + const outletStates = this.statesByOutlet["primary"] = this.statesByOutlet["primary"] || []; + const isNewPage = outletStates.length === 0; + outletStates.push({ + state: state, + title: title, + queryParams: queryParams, + segmentGroup: stateUrlTree.root, + isRootSegmentGroup: true, + isPageNavigation: isNewPage, + isModalNavigation: false + }); + this.currentOutlet = "primary"; + } + + Object.keys(rootOutlets).forEach(outletName => { + const outletStates = this.statesByOutlet[outletName] = this.statesByOutlet[outletName] || []; + const isNewPage = outletStates.length === 0; + const lastState = this.peekState(outletName); + + if (!lastState || lastState.segmentGroup.toString() !== rootOutlets[outletName].toString()) { + outletStates.push({ + state: state, + title: title, + queryParams: queryParams, + segmentGroup: rootOutlets[outletName], + isRootSegmentGroup: false, + isPageNavigation: isNewPage, + isModalNavigation: false + }); + + this.currentOutlet = outletName; + } }); } replaceState(state: any, title: string, url: string, queryParams: string): void { - if (this.states.length > 0) { + const states = this.statesByOutlet[this.currentOutlet]; + if (states && states.length > 0) { routerLog("NSLocationStrategy.replaceState changing existing state: " + `${state}, title: ${title}, url: ${url}, queryParams: ${queryParams}`); - const topState = this.peekState(); - topState.state = state; - topState.title = title; - topState.url = url; - topState.queryParams = queryParams; + + const tree = this.currentUrlTree; + if (url !== tree.toString()) { + const urlSerializer = new DefaultUrlSerializer(); + const stateUrlTree: UrlTree = urlSerializer.parse(url); + const rootOutlets = stateUrlTree.root.children; + + Object.keys(rootOutlets).forEach(outletName => { + const topState = this.peekState(outletName); + + topState.segmentGroup = rootOutlets[outletName]; + topState.state = state; + topState.title = title; + topState.queryParams = queryParams; + }); + } } else { routerLog("NSLocationStrategy.replaceState pushing new state: " + `${state}, title: ${title}, url: ${url}, queryParams: ${queryParams}`); @@ -87,21 +156,52 @@ export class NSLocationStrategy extends LocationStrategy { } back(): void { - if (this._isPageNavigationBack) { + if (this._isModalClosing) { + const states = this.statesByOutlet[this.currentOutlet]; + // We are closing modal view + // clear the stack until we get to the page that opened the modal view + let state; + let modalStatesCleared = false; + let count = 1; + + while (!modalStatesCleared) { + state = this.peekState(this.currentOutlet); + + if (!state) { + modalStatesCleared = true; + continue; + } + + if (!state.isModalNavigation) { + states.pop(); + count++; + } else { + modalStatesCleared = true; + state.isModalNavigation = false; + } + } + + routerLog("NSLocationStrategy.back() while closing modal. States popped: " + count); + + if (state) { + this.callPopState(state, true); + } + } else if (this._isPageNavigationBack) { + const states = this.statesByOutlet[this.currentOutlet]; // We are navigating to the previous page // clear the stack until we get to a page navigation state - let state = this.states.pop(); + let state = states.pop(); let count = 1; - while (!(state.isPageNavigation)) { - state = this.states.pop(); + while (!state.isPageNavigation) { + state = states.pop(); count++; } routerLog("NSLocationStrategy.back() while navigating back. States popped: " + count); this.callPopState(state, true); } else { - let state = this.peekState(); + let state = this.peekState(this.currentOutlet); if (state.isPageNavigation) { // This was a page navigation - so navigate through frame. routerLog("NSLocationStrategy.back() while not navigating back but top" + @@ -112,14 +212,15 @@ export class NSLocationStrategy extends LocationStrategy { // Nested navigation - just pop the state routerLog("NSLocationStrategy.back() while not navigating back but top" + " state is not page - just pop"); - this.callPopState(this.states.pop(), true); + + this.callPopState(this.statesByOutlet[this.currentOutlet].pop(), true); } } } canGoBack() { - return this.states.length > 1; + return this.statesByOutlet[this.currentOutlet].length > 1; } onPopState(fn: (_: any) => any): void { @@ -133,33 +234,48 @@ export class NSLocationStrategy extends LocationStrategy { } private callPopState(state: LocationState, pop: boolean = true) { - const change = { url: state.url, pop: pop }; + const urlSerializer = new DefaultUrlSerializer(); + this.currentUrlTree.root.children[this.currentOutlet] = state.segmentGroup; + const url = urlSerializer.serialize(this.currentUrlTree); + const change = { url: url, pop: pop }; for (let fn of this.popStateCallbacks) { fn(change); } } - private peekState(): LocationState { - if (this.states.length > 0) { - return this.states[this.states.length - 1]; + private peekState(name: string) { + const states = this.statesByOutlet[name] || []; + if (states.length > 0) { + return states[states.length - 1]; } return null; } public toString() { - return this.states - .map((v, i) => `${i}.[${v.isPageNavigation ? "PAGE" : "INTERNAL"}] "${v.url}"`) - .reverse() - .join("\n"); + let result = []; + + Object.keys(this.statesByOutlet).forEach(outletName => { + const outletStates = this.statesByOutlet[outletName]; + const outletLog = outletStates + // tslint:disable-next-line:max-line-length + .map((v, i) => `${outletName}.${i}.[${v.isPageNavigation ? "PAGE" : "INTERNAL"}].[${v.isModalNavigation ? "MODAL" : "BASE"}] "${v.segmentGroup.toString()}"`) + .reverse(); + + + result = result.concat(outletLog); + }); + + return result.join("\n"); } // Methods for syncing with page navigation in PageRouterOutlet - public _beginBackPageNavigation() { + public _beginBackPageNavigation(name: string) { routerLog("NSLocationStrategy.startGoBack()"); if (this._isPageNavigationBack) { throw new Error("Calling startGoBack while going back."); } this._isPageNavigationBack = true; + this.currentOutlet = name; } public _finishBackPageNavigation() { @@ -174,17 +290,46 @@ export class NSLocationStrategy extends LocationStrategy { return this._isPageNavigationBack; } - public _beginPageNavigation(): NavigationOptions { + public _beginModalNavigation(): void { + routerLog("NSLocationStrategy._beginModalNavigation()"); + const lastState = this.peekState(this.currentOutlet); + + if (lastState) { + lastState.isModalNavigation = true; + } + + this._isModalNavigation = true; + } + + public _beginCloseModalNavigation(): void { + routerLog("NSLocationStrategy.startCloseModal()"); + if (this._isModalClosing) { + throw new Error("Calling startCloseModal while closing modal."); + } + this._isModalClosing = true; + } + + public _finishCloseModalNavigation() { + routerLog("NSLocationStrategy.finishCloseModalNavigation()"); + if (!this._isModalClosing) { + throw new Error("Calling startCloseModal while not closing modal."); + } + this._isModalClosing = false; + } + + public _beginPageNavigation(name: string): NavigationOptions { routerLog("NSLocationStrategy._beginPageNavigation()"); - const lastState = this.peekState(); + const lastState = this.peekState(name); if (lastState) { lastState.isPageNavigation = true; } + this.currentOutlet = name; + const navOptions = this._currentNavigationOptions || defaultNavOptions; if (navOptions.clearHistory) { routerLog("NSLocationStrategy._beginPageNavigation clearing states history"); - this.states = [lastState]; + this.statesByOutlet[name] = [lastState]; } this._currentNavigationOptions = undefined; @@ -201,7 +346,7 @@ export class NSLocationStrategy extends LocationStrategy { `${JSON.stringify(this._currentNavigationOptions)})`); } - public _getStates(): Array { - return this.states.slice(); + public _getStates(): { [key: string]: Array } { + return this.statesByOutlet; } } diff --git a/nativescript-angular/router/ns-route-reuse-strategy.ts b/nativescript-angular/router/ns-route-reuse-strategy.ts index d6b489180..e9b425020 100644 --- a/nativescript-angular/router/ns-route-reuse-strategy.ts +++ b/nativescript-angular/router/ns-route-reuse-strategy.ts @@ -12,6 +12,7 @@ import { interface CacheItem { key: string; state: DetachedRouteHandle; + isModal: boolean; } /** @@ -48,6 +49,36 @@ class DetachedStateCache { destroyComponentRef(state.componentRef); } } + + public clearModalCache() { + let removedItemsCount = 0; + const hasModalPages = this.cache.some(cacheItem => { + return cacheItem.isModal; + }); + + if (hasModalPages) { + let modalCacheCleared = false; + + while (!modalCacheCleared) { + let cacheItem = this.peek(); + const state = cacheItem.state; + + if (!state.componentRef) { + throw new Error("No componentRef found in DetachedRouteHandle"); + } + + destroyComponentRef(state.componentRef); + if (cacheItem.isModal) { + modalCacheCleared = true; + } + + this.pop(); + removedItemsCount++; + } + } + + log(`DetachedStateCache.clearModalCache() ${removedItemsCount} items will be destroyed`); + } } /** @@ -57,7 +88,7 @@ class DetachedStateCache { */ @Injectable() export class NSRouteReuseStrategy implements RouteReuseStrategy { - private cache: DetachedStateCache = new DetachedStateCache(); + private cacheByOutlet: { [key: string]: DetachedStateCache } = {}; constructor(private location: NSLocationStrategy) { } @@ -77,9 +108,14 @@ export class NSRouteReuseStrategy implements RouteReuseStrategy { shouldAttach(route: ActivatedRouteSnapshot): boolean { route = findTopActivatedRouteNodeForOutlet(route); + const cache = this.cacheByOutlet[route.outlet]; + if (!cache) { + return false; + } + const key = getSnapshotKey(route); const isBack = this.location._isPageNavigatingBack(); - const shouldAttach = isBack && this.cache.peek().key === key; + const shouldAttach = isBack && cache.peek().key === key; log(`shouldAttach isBack: ${isBack} key: ${key} result: ${shouldAttach}`); @@ -92,12 +128,20 @@ export class NSRouteReuseStrategy implements RouteReuseStrategy { const key = getSnapshotKey(route); log(`store key: ${key}, state: ${state}`); + const cache = this.cacheByOutlet[route.outlet] = this.cacheByOutlet[route.outlet] || new DetachedStateCache(); + if (state) { - this.cache.push({ key, state }); + let isModal = false; + if (this.location._isModalNavigation) { + isModal = true; + this.location._isModalNavigation = false; + } + + cache.push({ key, state, isModal }); } else { - const topItem = this.cache.peek(); + const topItem = cache.peek(); if (topItem.key === key) { - this.cache.pop(); + cache.pop(); } else { throw new Error("Trying to pop from DetachedStateCache but keys don't match. " + `expected: ${topItem.key} actual: ${key}`); @@ -108,9 +152,14 @@ export class NSRouteReuseStrategy implements RouteReuseStrategy { retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null { route = findTopActivatedRouteNodeForOutlet(route); + const cache = this.cacheByOutlet[route.outlet]; + if (!cache) { + return null; + } + const key = getSnapshotKey(route); const isBack = this.location._isPageNavigatingBack(); - const cachedItem = this.cache.peek(); + const cachedItem = cache.peek(); let state = null; if (isBack && cachedItem && cachedItem.key === key) { @@ -136,8 +185,20 @@ export class NSRouteReuseStrategy implements RouteReuseStrategy { return shouldReuse; } - clearCache() { - this.cache.clear(); + clearCache(outletName: string) { + const cache = this.cacheByOutlet[outletName]; + + if (cache) { + cache.clear(); + } + } + + clearModalCache(outletName: string) { + const cache = this.cacheByOutlet[outletName]; + + if (cache) { + cache.clearModalCache(); + } } } diff --git a/nativescript-angular/router/page-router-outlet.ts b/nativescript-angular/router/page-router-outlet.ts index 4db554d51..eb5950558 100644 --- a/nativescript-angular/router/page-router-outlet.ts +++ b/nativescript-angular/router/page-router-outlet.ts @@ -2,7 +2,7 @@ import { Attribute, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, Directive, Inject, InjectionToken, Injector, - OnDestroy, OnInit, EventEmitter, Output, + OnDestroy, EventEmitter, Output, Type, ViewContainerRef, ElementRef } from "@angular/core"; import { @@ -102,7 +102,7 @@ function routeToString(activatedRoute: ActivatedRoute | ActivatedRouteSnapshot): } @Directive({ selector: "page-router-outlet" }) // tslint:disable-line:directive-selector -export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-line:directive-class-suffix +export class PageRouterOutlet implements OnDestroy { // tslint:disable-line:directive-class-suffix private activated: ComponentRef | null = null; private _activatedRoute: ActivatedRoute | null = null; private detachedLoaderFactory: ComponentFactory; @@ -161,28 +161,10 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l this.detachedLoaderFactory = resolver.resolveComponentFactory(DetachedLoader); } - ngOnInit(): void { - if (this.isActivated) { - return; - } - - // If the outlet was not instantiated at the time the route got activated we need to populate - // the outlet when it is initialized (ie inside a NgIf) - const context = this.parentContexts.getContext(this.name); - if (!context || !context.route) { - return; - } - - if (context.attachRef) { - // `attachRef` is populated when there is an existing component to mount - this.attach(context.attachRef, context.route); - } else { - // otherwise the component defined in the configuration is created - this.activateWith(context.route, context.resolver || null); - } - } - ngOnDestroy(): void { + // Clear accumulated modal view page cache when page-router-outlet + // destroyed on modal view closing + this.routeReuseStrategy.clearModalCache(this.name); this.parentContexts.onChildOutletDestroyed(this.name); } @@ -267,7 +249,7 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l loadedResolver: ComponentFactoryResolver ): void { log("PageRouterOutlet.activate() forward navigation - " + - "create detached loader in the loader container"); + "create detached loader in the loader container"); const factory = this.getComponentFactory(activatedRoute, loadedResolver); const page = this.pageFactory({ @@ -304,17 +286,17 @@ export class PageRouterOutlet implements OnDestroy, OnInit { // tslint:disable-l page.on(Page.navigatedFromEvent, (global).Zone.current.wrap((args: NavigatedData) => { if (args.isBackNavigation) { - this.locationStrategy._beginBackPageNavigation(); + this.locationStrategy._beginBackPageNavigation(this.name); this.locationStrategy.back(); } })); - const navOptions = this.locationStrategy._beginPageNavigation(); + const navOptions = this.locationStrategy._beginPageNavigation(this.name); // Clear refCache if navigation with clearHistory if (navOptions.clearHistory) { const clearCallback = () => setTimeout(() => { - this.routeReuseStrategy.clearCache(); + this.routeReuseStrategy.clearCache(this.name); page.off(Page.navigatedToEvent, clearCallback); }); diff --git a/ng-sample/.vscode/launch.json b/ng-sample/.vscode/launch.json index a12095658..ee3fe690b 100644 --- a/ng-sample/.vscode/launch.json +++ b/ng-sample/.vscode/launch.json @@ -1,72 +1,58 @@ { - "version": "0.2.0", - "configurations": [ - { - "name": "Sync on iOS", - "type": "nativescript", - "platform": "ios", - "request": "launch", - "appRoot": "${workspaceRoot}", - "sourceMaps": true, - "diagnosticLogging": false, - "emulator": false, - "rebuild": false, - "syncAllFiles": false - }, - { - "name": "Launch on iOS", - "type": "nativescript", - "platform": "ios", - "request": "launch", - "appRoot": "${workspaceRoot}", - "sourceMaps": true, - "diagnosticLogging": false, - "emulator": false, - "rebuild": true, - "stopOnEntry": true - }, - { - "name": "Attach on iOS", - "type": "nativescript", - "platform": "ios", - "request": "attach", - "appRoot": "${workspaceRoot}", - "sourceMaps": true, - "diagnosticLogging": false, - "emulator": false - }, - { - "name": "Sync on Android", - "type": "nativescript", - "platform": "android", - "request": "launch", - "appRoot": "${workspaceRoot}", - "sourceMaps": true, - "diagnosticLogging": false, - "emulator": false, - "rebuild": false - }, - { - "name": "Launch on Android", - "type": "nativescript", - "platform": "android", - "request": "launch", - "appRoot": "${workspaceRoot}", - "sourceMaps": true, - "diagnosticLogging": false, - "emulator": false, - "rebuild": true, - "stopOnEntry": true - }, - { - "name": "Attach on Android", - "type": "nativescript", - "platform": "android", - "request": "attach", - "appRoot": "${workspaceRoot}", - "sourceMaps": true, - "diagnosticLogging": false, - "emulator": false - } - ] + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch on iOS", + "type": "nativescript", + "request": "launch", + "platform": "ios", + "appRoot": "${workspaceRoot}", + "sourceMaps": true, + "stopOnEntry": false, + "tnsArgs": [ + "--syncAllFiles" + ], + "watch": true + }, + { + "name": "Attach on iOS", + "type": "nativescript", + "request": "attach", + "platform": "ios", + "appRoot": "${workspaceRoot}", + "sourceMaps": true, + "tnsArgs": [ + "--syncAllFiles" + ], + "watch": false + }, + { + "name": "Launch on Android", + "type": "nativescript", + "request": "launch", + "platform": "android", + "appRoot": "${workspaceRoot}", + "sourceMaps": true, + "stopOnEntry": false, + "tnsArgs": [ + "--syncAllFiles" + ], + "watch": true + }, + { + "name": "Attach on Android", + "type": "nativescript", + "request": "attach", + "platform": "android", + "appRoot": "${workspaceRoot}", + "sourceMaps": true, + "tnsArgs": [ + "--syncAllFiles" + ], + "watch": false + } + ] } \ No newline at end of file diff --git a/ng-sample/app/app.ts b/ng-sample/app/app.ts index 666a570e5..eedfe1344 100644 --- a/ng-sample/app/app.ts +++ b/ng-sample/app/app.ts @@ -45,11 +45,15 @@ import { ImageTest } from "./examples/image/image-test"; import { HttpTest } from "./examples/http/http-test"; import { HttpClientTest } from "./examples/http-client/http-client-test"; import { ActionBarTest } from "./examples/action-bar/action-bar-test"; -import { ModalTest } from "./examples/modal/modal-test"; -import { ModalNestedTest } from "./examples/modal/modal-nested-test"; import { PlatfromDirectivesTest } from "./examples/platform-directives/platform-directives-test"; import { LivesyncApp } from "./examples/livesync-test/livesync-test-app"; +// modal +import { ModalTest } from "./examples/modal/modal-test"; +import { ModalNestedTest } from "./examples/modal/modal-nested-test"; +import { ModalRouterOutletTest } from "./examples/modal/modal-router-outlet-test"; +import { ModalPageRouterOutletTest } from "./examples/modal/modal-page-router-outlet-test"; + // new router import { RouterOutletAppComponent } from "./examples/router/router-outlet-test"; import { PageRouterOutletAppComponent } from "./examples/router/page-router-outlet-test"; @@ -82,7 +86,7 @@ import { AnimationStatesMultiTest } from "./examples/animation/animation-states- ], providers: [], }) -class ExampleModule {} +class ExampleModule { } function makeExampleModule(componentType) { let imports: any[] = [NativeScriptAnimationsModule, ExampleModule]; @@ -112,7 +116,7 @@ function makeExampleModule(componentType) { providers, exports, }) - class ExampleModuleForComponent {} + class ExampleModuleForComponent { } return ExampleModuleForComponent; } @@ -188,3 +192,6 @@ onAfterLivesync.subscribe(({ moduleRef, error }) => { // platformNativeScriptDynamic().bootstrapModule(makeExampleModule(LivesyncApp)); // console.log("APP RESTART!!!! !!!"); // platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ModalTest)); +// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ModalNestedTest)); +// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ModalRouterOutletTest)); +platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ModalPageRouterOutletTest)); diff --git a/ng-sample/app/examples/modal/modal-nested-test.ts b/ng-sample/app/examples/modal/modal-nested-test.ts index 0330d5525..d1ae043f2 100644 --- a/ng-sample/app/examples/modal/modal-nested-test.ts +++ b/ng-sample/app/examples/modal/modal-nested-test.ts @@ -17,7 +17,6 @@ export class ModalNestedTest { ]; static exports = [ - ModalContent, ModalTest ]; diff --git a/ng-sample/app/examples/modal/modal-page-router-outlet-test.ts b/ng-sample/app/examples/modal/modal-page-router-outlet-test.ts new file mode 100644 index 000000000..0477c8289 --- /dev/null +++ b/ng-sample/app/examples/modal/modal-page-router-outlet-test.ts @@ -0,0 +1,47 @@ +import { Component, ViewContainerRef } from "@angular/core"; +import * as dialogs from "ui/dialogs"; +import { ModalDialogService, ModalDialogOptions } from "nativescript-angular/directives/dialogs"; +import { FirstComponent, SecondComponent, ThirdComponent, PageRouterOutletAppComponent } from "../router/page-router-outlet-test"; + +@Component({ + selector: "modal-router-outlet-test", + template: ` + + + + + + + + ` +}) +export class ModalPageRouterOutletTest { + + public result: string = "result"; + + constructor(private modal: ModalDialogService, private vcRef: ViewContainerRef) { } + + static entries = [ + PageRouterOutletAppComponent, + ]; + + static exports = [ + FirstComponent, + SecondComponent, + ThirdComponent + ]; + + static routes = PageRouterOutletAppComponent.routes; + + public showModal(fullscreen: boolean) { + const options: ModalDialogOptions = { + fullscreen: fullscreen, + viewContainerRef: this.vcRef + }; + + this.modal.showModal(PageRouterOutletAppComponent, options).then((res: string) => { + this.result = res || "empty result"; + }); + } + +} diff --git a/ng-sample/app/examples/modal/modal-router-outlet-test.ts b/ng-sample/app/examples/modal/modal-router-outlet-test.ts new file mode 100644 index 000000000..9bdac0d99 --- /dev/null +++ b/ng-sample/app/examples/modal/modal-router-outlet-test.ts @@ -0,0 +1,46 @@ +import { Component, ViewContainerRef } from "@angular/core"; +import * as dialogs from "ui/dialogs"; +import { ModalDialogService, ModalDialogOptions } from "nativescript-angular/directives/dialogs"; +import { FirstComponent, SecondComponent, RouterOutletAppComponent } from "../router/router-outlet-test"; + +@Component({ + selector: "modal-router-outlet-test", + template: ` + + + + + + + + ` +}) +export class ModalRouterOutletTest { + + public result: string = "result"; + + constructor(private modal: ModalDialogService, private vcRef: ViewContainerRef) { } + + static entries = [ + RouterOutletAppComponent, + ]; + + static exports = [ + FirstComponent, + SecondComponent + ]; + + static routes = RouterOutletAppComponent.routes; + + public showModal(fullscreen: boolean) { + const options: ModalDialogOptions = { + fullscreen: fullscreen, + viewContainerRef: this.vcRef + }; + + this.modal.showModal(RouterOutletAppComponent, options).then((res: string) => { + this.result = res || "empty result"; + }); + } + +} diff --git a/ng-sample/app/examples/modal/modal-test.ts b/ng-sample/app/examples/modal/modal-test.ts index edfebf4fc..5bc742b10 100644 --- a/ng-sample/app/examples/modal/modal-test.ts +++ b/ng-sample/app/examples/modal/modal-test.ts @@ -31,9 +31,7 @@ export class ModalTest { ModalContent ]; - static exports = [ - ModalContent - ]; + static exports = []; public showModal(fullscreen: boolean) { const options: ModalDialogOptions = { diff --git a/ng-sample/app/examples/router/page-router-outlet-test.ts b/ng-sample/app/examples/router/page-router-outlet-test.ts index 28261d6b3..9ce54352f 100644 --- a/ng-sample/app/examples/router/page-router-outlet-test.ts +++ b/ng-sample/app/examples/router/page-router-outlet-test.ts @@ -5,7 +5,6 @@ import { Page } from "ui/page"; import { Observable } from "rxjs"; import { map } from "rxjs/operators"; - @Component({ selector: "first", styleUrls: ["examples/router/styles.css"], @@ -19,7 +18,7 @@ import { map } from "rxjs/operators"; ` }) -class FirstComponent implements OnInit, OnDestroy { +export class FirstComponent implements OnInit, OnDestroy { constructor(page: Page) { console.log("FirstComponent.constructor() page: " + page); } @@ -47,7 +46,7 @@ class FirstComponent implements OnInit, OnDestroy { ` }) -class SecondComponent implements OnInit, OnDestroy { +export class SecondComponent implements OnInit, OnDestroy { public id: Observable; constructor(private location: Location, route: ActivatedRoute, page: Page) { console.log("SecondComponent.constructor() page: " + page); @@ -81,7 +80,7 @@ class SecondComponent implements OnInit, OnDestroy { ` }) -class ThirdComponent implements OnInit, OnDestroy { +export class ThirdComponent implements OnInit, OnDestroy { public id: Observable; constructor(private location: Location, route: ActivatedRoute, page: Page) { console.log("ThirdComponent.constructor() page: " + page); diff --git a/ng-sample/app/examples/router/router-outlet-test.ts b/ng-sample/app/examples/router/router-outlet-test.ts index 04e82388a..90d37c7dc 100644 --- a/ng-sample/app/examples/router/router-outlet-test.ts +++ b/ng-sample/app/examples/router/router-outlet-test.ts @@ -10,7 +10,7 @@ import { map } from "rxjs/operators"; ` }) -class FirstComponent implements OnInit, OnDestroy { +export class FirstComponent implements OnInit, OnDestroy { ngOnInit() { console.log("FirstComponent - ngOnInit()"); } @@ -28,7 +28,7 @@ class FirstComponent implements OnInit, OnDestroy { ` }) -class SecondComponent implements OnInit, OnDestroy { +export class SecondComponent implements OnInit, OnDestroy { id; constructor(route: ActivatedRoute) { this.id = route.params.pipe(map(r => r["id"])); @@ -49,8 +49,14 @@ class SecondComponent implements OnInit, OnDestroy { template: ` - - + +