diff --git a/e2e/modal-navigation-ng/app/app.component.ts b/e2e/modal-navigation-ng/app/app.component.ts
index a86fef745..4c61400d7 100644
--- a/e2e/modal-navigation-ng/app/app.component.ts
+++ b/e2e/modal-navigation-ng/app/app.component.ts
@@ -1,21 +1,27 @@
-import { Component } from "@angular/core";
+import { Component, ViewContainerRef } from "@angular/core";
import { Router, NavigationEnd } from "@angular/router";
import { NSLocationStrategy } from "nativescript-angular/router/ns-location-strategy";
+import { ViewContainerRefService } from "./shared/ViewContainerRefService";
+
@Component({
selector: "ns-app",
templateUrl: "app.component.html",
})
export class AppComponent {
- constructor(router: Router, location: NSLocationStrategy) {
+ constructor(
+ router: Router,
+ location: NSLocationStrategy,
+ private _vcRef: ViewContainerRef,
+ private _viewContainerRefService: ViewContainerRefService) {
router.events.subscribe(e => {
- // console.log("[ROUTER]: " + e.toString());
-
if (e instanceof NavigationEnd) {
console.log("[ROUTER]: " + e.toString());
console.log(location.toString());
}
});
+
+ this._viewContainerRefService.root = this._vcRef;
}
}
diff --git a/e2e/modal-navigation-ng/app/app.module.ts b/e2e/modal-navigation-ng/app/app.module.ts
index af8060319..119ec1f16 100644
--- a/e2e/modal-navigation-ng/app/app.module.ts
+++ b/e2e/modal-navigation-ng/app/app.module.ts
@@ -9,9 +9,13 @@ import { ModalSecondComponent } from "./modal-second/modal-second.component";
import { ModalComponent } from "./modal/modal.component";
import { NestedModalComponent } from "./modal-nested/modal-nested.component";
import { ModalRouterComponent } from "./modal/modal-router/modal-router.component";
+import { ModalViewComponent } from "./modal-shared/modal-view.component";
+import { ModalViewContentComponent } from "./modal-shared/modal-view-content.component";
+import { ModalSharedSecondComponent } from "./modal-shared/modal-shared-second.component";
+import { ViewContainerRefService } from "./shared/ViewContainerRefService";
-import { enable as traceEnable, addCategories } from "tns-core-modules/trace";
-import { routerTraceCategory } from "nativescript-angular/trace";
+// import { enable as traceEnable, addCategories } from "tns-core-modules/trace";
+// import { routerTraceCategory } from "nativescript-angular/trace";
// addCategories(routerTraceCategory);
// traceEnable();
@@ -24,7 +28,12 @@ import { routerTraceCategory } from "nativescript-angular/trace";
NativeScriptModule,
AppRoutingModule
],
- entryComponents: [ModalRouterComponent, NestedModalComponent, ModalComponent],
+ entryComponents: [
+ ModalRouterComponent,
+ NestedModalComponent,
+ ModalComponent,
+ ModalViewComponent
+ ],
declarations: [
AppComponent,
HomeComponent,
@@ -32,7 +41,13 @@ import { routerTraceCategory } from "nativescript-angular/trace";
ModalComponent,
NestedModalComponent,
ModalRouterComponent,
- ModalSecondComponent
+ ModalSecondComponent,
+ ModalViewComponent,
+ ModalViewContentComponent,
+ ModalSharedSecondComponent
+ ],
+ providers: [
+ ViewContainerRefService
],
schemas: [
NO_ERRORS_SCHEMA
diff --git a/e2e/modal-navigation-ng/app/app.routing.ts b/e2e/modal-navigation-ng/app/app.routing.ts
index 9c5a1a973..6e7346d84 100644
--- a/e2e/modal-navigation-ng/app/app.routing.ts
+++ b/e2e/modal-navigation-ng/app/app.routing.ts
@@ -1,13 +1,14 @@
import { NgModule } from "@angular/core";
import { NativeScriptRouterModule } from "nativescript-angular/router";
-import { Routes, ChildrenOutletContexts } from "@angular/router";
+import { Routes } from "@angular/router";
import { HomeComponent } from "./home/home.component";
import { SecondComponent } from "./second/second.component";
import { ModalSecondComponent } from "./modal-second/modal-second.component";
import { ModalComponent } from "./modal/modal.component";
import { NestedModalComponent } from "./modal-nested/modal-nested.component";
-import { ModalRouterComponent } from "./modal/modal-router/modal-router.component";
+import { ModalViewContentComponent } from "./modal-shared/modal-view-content.component";
+import { ModalSharedSecondComponent } from "./modal-shared/modal-shared-second.component";
const routes: Routes = [
{ path: "", redirectTo: "/home", pathMatch: "full" },
@@ -28,6 +29,12 @@ const routes: Routes = [
},
{ path: "modal-second", component: ModalSecondComponent }
]
+ },
+ {
+ path: "modal-shared", component: ModalViewContentComponent, outlet: "modalOutlet"
+ },
+ {
+ path: "modal-shared-second-host", component: ModalSharedSecondComponent
}
];
diff --git a/e2e/modal-navigation-ng/app/home/home.component.html b/e2e/modal-navigation-ng/app/home/home.component.html
index ac5f99450..5379c23d9 100644
--- a/e2e/modal-navigation-ng/app/home/home.component.html
+++ b/e2e/modal-navigation-ng/app/home/home.component.html
@@ -9,4 +9,7 @@
+
+
+
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/home/home.component.ts b/e2e/modal-navigation-ng/app/home/home.component.ts
index d494180a6..d3410c3fc 100644
--- a/e2e/modal-navigation-ng/app/home/home.component.ts
+++ b/e2e/modal-navigation-ng/app/home/home.component.ts
@@ -1,12 +1,12 @@
import { Component, ViewContainerRef } from "@angular/core";
import { ModalDialogService, ModalDialogOptions } from "nativescript-angular/directives/dialogs";
+import { RouterExtensions } from "nativescript-angular/router";
import { EventData } from "tns-core-modules/data/observable";
-import { Frame } from "tns-core-modules/ui/frame";
-import { View } from "tns-core-modules/ui/core/view";
+
+import { ViewContainerRefService } from "../shared/ViewContainerRefService";
import { ModalRouterComponent } from "../modal/modal-router/modal-router.component";
-import { PageRouterOutlet } from "nativescript-angular/router/page-router-outlet";
-import { RouterExtensions } from "nativescript-angular/router";
import { ModalComponent } from "../modal/modal.component";
+import { ModalViewComponent } from "../modal-shared/modal-view.component";
@Component({
moduleId: module.id,
@@ -14,7 +14,11 @@ import { ModalComponent } from "../modal/modal.component";
templateUrl: "./home.component.html"
})
export class HomeComponent {
- constructor(private modal: ModalDialogService, private vcRef: ViewContainerRef, private routerExtension: RouterExtensions) { }
+ constructor(
+ private modal: ModalDialogService,
+ private vcRef: ViewContainerRef,
+ private viewContainerRefService: ViewContainerRefService,
+ private routerExtension: RouterExtensions) { }
onModalNoFrame(args: EventData) {
const options: ModalDialogOptions = {
@@ -52,4 +56,17 @@ export class HomeComponent {
onFrameRootViewReset(args: EventData) {
}
+
+ onRootModalTap(): void {
+ const options: ModalDialogOptions = {
+ viewContainerRef: this.viewContainerRefService.root,
+ context: {},
+ fullscreen: true
+ };
+
+ this.modal.showModal(ModalViewComponent, options)
+ .then((result: string) => {
+ console.log(result);
+ });
+ }
}
diff --git a/e2e/modal-navigation-ng/app/modal-shared/modal-shared-second.component.ts b/e2e/modal-navigation-ng/app/modal-shared/modal-shared-second.component.ts
new file mode 100644
index 000000000..6f9c5ca21
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/modal-shared/modal-shared-second.component.ts
@@ -0,0 +1,44 @@
+import { Component } from "@angular/core";
+import { ModalDialogOptions, ModalDialogService } from "nativescript-angular/modal-dialog";
+
+import { ViewContainerRefService } from "../shared/ViewContainerRefService";
+import { ModalViewComponent } from "../modal-shared/modal-view.component";
+import { RouterExtensions } from "nativescript-angular/router";
+
+@Component({
+ selector: "ns-second",
+ moduleId: module.id,
+ template: `
+
+
+
+
+
+ `
+})
+export class ModalSharedSecondComponent {
+ constructor(
+ private _modalService: ModalDialogService,
+ private _viewContainerRefService: ViewContainerRefService,
+ private _routerExtensions: RouterExtensions
+ ) { }
+
+ onRootModalTap(): void {
+ const options: ModalDialogOptions = {
+ viewContainerRef: this._viewContainerRefService.root,
+ context: {},
+ fullscreen: true
+ };
+
+ this._modalService.showModal(ModalViewComponent, options)
+ .then((result: string) => {
+ console.log(result);
+ });
+ }
+
+ onBackTap() {
+ if (this._routerExtensions.canGoBack()) {
+ this._routerExtensions.back();
+ }
+ }
+}
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/modal-shared/modal-view-content.component.ts b/e2e/modal-navigation-ng/app/modal-shared/modal-view-content.component.ts
new file mode 100644
index 000000000..30247073c
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/modal-shared/modal-view-content.component.ts
@@ -0,0 +1,27 @@
+import { Component } from "@angular/core";
+import { ModalDialogParams } from "nativescript-angular/modal-dialog";
+
+@Component({
+ selector: "ModalViewContent",
+ moduleId: module.id,
+ template: `
+
+
+
+
+
+
+ `,
+ styles: [`
+ .action-bar, .page {
+ background-color: chocolate;
+ }
+ `]
+})
+export class ModalViewContentComponent {
+ constructor(private _params: ModalDialogParams) { }
+
+ onTap(): void {
+ this._params.closeCallback("return value");
+ }
+}
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/app/modal-shared/modal-view.component.ts b/e2e/modal-navigation-ng/app/modal-shared/modal-view.component.ts
new file mode 100644
index 000000000..7089b8e14
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/modal-shared/modal-view.component.ts
@@ -0,0 +1,17 @@
+import { Component, OnInit } from "@angular/core";
+import { RouterExtensions } from "nativescript-angular/router";
+
+@Component({
+ selector: "ModalView",
+ moduleId: module.id,
+ template:`
+
+ `
+})
+export class ModalViewComponent implements OnInit {
+ constructor(private _routerExtensions: RouterExtensions) {}
+
+ ngOnInit(): void {
+ this._routerExtensions.navigate([{ outlets: { modalOutlet: ["modal-shared"]}}]);
+ }
+}
diff --git a/e2e/modal-navigation-ng/app/shared/ViewContainerRefService.ts b/e2e/modal-navigation-ng/app/shared/ViewContainerRefService.ts
new file mode 100644
index 000000000..e74a57a15
--- /dev/null
+++ b/e2e/modal-navigation-ng/app/shared/ViewContainerRefService.ts
@@ -0,0 +1,14 @@
+import { Injectable, ViewContainerRef } from "@angular/core";
+
+@Injectable()
+export class ViewContainerRefService {
+ private _rootViewContainerRef: ViewContainerRef;
+
+ get root():ViewContainerRef {
+ return this._rootViewContainerRef;
+ }
+
+ set root(viewContainerRef: ViewContainerRef) {
+ this._rootViewContainerRef = viewContainerRef;
+ }
+}
diff --git a/e2e/modal-navigation-ng/e2e/modal.shared.e2e-spec.ts b/e2e/modal-navigation-ng/e2e/modal.shared.e2e-spec.ts
new file mode 100644
index 000000000..ecdc240b4
--- /dev/null
+++ b/e2e/modal-navigation-ng/e2e/modal.shared.e2e-spec.ts
@@ -0,0 +1,168 @@
+import { AppiumDriver, createDriver, SearchOptions } from "nativescript-dev-appium";
+import { assert } from "chai";
+
+describe("Shared modal from home and back", () => {
+ let driver: AppiumDriver;
+
+ before(async () => {
+ driver = await createDriver();
+ await driver.resetApp();
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logPageSource(this.currentTest.title);
+ await driver.logScreenshot(this.currentTest.title);
+ }
+ });
+
+ it ("should find home component", async () => {
+ await assertComponent(driver, "home component");
+ });
+
+ it("should open/close shared modal from home component", async () => {
+ await openModal(driver);
+ await closeModal(driver);
+ });
+
+ it ("should find home component again", async () => {
+ await assertComponent(driver, "home component");
+ });
+});
+
+describe("Shared modal from second and back", () => {
+ let driver: AppiumDriver;
+
+ before(async () => {
+ driver = await createDriver();
+ await driver.resetApp();
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logPageSource(this.currentTest.title);
+ await driver.logScreenshot(this.currentTest.title);
+ }
+ });
+
+ it ("should find home component", async () => {
+ await assertComponent(driver, "home component");
+ });
+
+ it ("should navigate to second component", async() => {
+ await navigateToSecondComponent(driver);
+ });
+
+ it ("should find second component", async () => {
+ await assertComponent(driver, "second component");
+ });
+
+ it("should open/close shared modal from second component", async () => {
+ await openModal(driver);
+ await closeModal(driver);
+ });
+
+ it ("should find second component again", async () => {
+ await assertComponent(driver, "second component");
+ });
+});
+
+describe("Shared modal from different components", () => {
+ let driver: AppiumDriver;
+
+ before(async () => {
+ driver = await createDriver();
+ await driver.resetApp();
+ });
+
+ after(async () => {
+ await driver.quit();
+ console.log("Quit driver!");
+ });
+
+ afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logPageSource(this.currentTest.title);
+ await driver.logScreenshot(this.currentTest.title);
+ }
+ });
+
+ it ("should find home component", async () => {
+ await assertComponent(driver, "home component");
+ });
+
+ it("should open/close shared modal from home component", async () => {
+ await openModal(driver);
+ await closeModal(driver);
+ });
+
+ it ("should find home component again", async () => {
+ await assertComponent(driver, "home component");
+ });
+
+ it ("should navigate to second component", async() => {
+ await navigateToSecondComponent(driver);
+ });
+
+ it ("should find second component", async () => {
+ await assertComponent(driver, "second component");
+ });
+
+ it("should open/close shared modal from second component", async () => {
+ await openModal(driver);
+ await closeModal(driver);
+ });
+
+ it ("should find second component again", async () => {
+ await assertComponent(driver, "second component");
+ });
+
+ it ("should navigate back to home component", async () => {
+ await goBack(driver);
+ await assertComponent(driver, "home component");
+ });
+
+ it("should open/close shared modal from home component after manipulations with second", async () => {
+ await openModal(driver);
+ await closeModal(driver);
+ });
+
+ it ("should find home component again", async () => {
+ await assertComponent(driver, "home component");
+ });
+});
+
+async function assertComponent(driver: AppiumDriver, message: string) {
+ const lbl = await driver.findElementByText(message, SearchOptions.exact);
+ assert.isTrue(await lbl.isDisplayed());
+}
+
+async function navigateToSecondComponent(driver: AppiumDriver) {
+ const navigateBtnTap = await driver.findElementByText("go to second (to open shared modal)", SearchOptions.exact);
+ await navigateBtnTap.click();
+}
+
+async function openModal(driver: AppiumDriver) {
+ const btnTap = await driver.findElementByText("show shared modal", SearchOptions.exact);
+ await btnTap.click();
+}
+
+async function closeModal(driver: AppiumDriver) {
+ const closeBtnTap = await driver.findElementByText("close modal", SearchOptions.exact);
+ await closeBtnTap.click();
+}
+
+async function goBack(driver: AppiumDriver) {
+ const backBtnTap = await driver.findElementByText("go back", SearchOptions.exact);
+ await backBtnTap.click();
+}
diff --git a/e2e/modal-navigation-ng/e2e/sample.e2e-spec.ts b/e2e/modal-navigation-ng/e2e/sample.e2e-spec.ts
deleted file mode 100644
index 45bda4c9a..000000000
--- a/e2e/modal-navigation-ng/e2e/sample.e2e-spec.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { AppiumDriver, createDriver, SearchOptions } from "nativescript-dev-appium";
-import { assert } from "chai";
-
-describe("sample scenario", () => {
- const defaultWaitTime = 5000;
- let driver: AppiumDriver;
-
- before(async () => {
- driver = await createDriver();
- });
-
- after(async () => {
- await driver.quit();
- console.log("Quit driver!");
- });
-
- afterEach(async function () {
- if (this.currentTest.state === "failed") {
- await driver.logScreenshot(this.currentTest.title);
- }
- });
-
- it("should find an element by text", async () => {
- const btnTap = await driver.findElementByText("TAP", SearchOptions.exact);
- await btnTap.click();
-
- const message = " taps left";
- const lblMessage = await driver.findElementByText(message, SearchOptions.contains);
- assert.equal(await lblMessage.text(), "41" + message);
-
- // Image verification
- // const screen = await driver.compareScreen("hello-world-41");
- // assert.isTrue(screen);
- });
-
- it("should find an element by type", async () => {
- const btnTap = await driver.findElementByClassName(driver.locators.button);
- await btnTap.click();
-
- const message = " taps left";
- const lblMessage = await driver.findElementByText(message, SearchOptions.contains);
- assert.equal(await lblMessage.text(), "40" + message);
- });
-});
\ No newline at end of file
diff --git a/e2e/modal-navigation-ng/package.json b/e2e/modal-navigation-ng/package.json
index 021f56a06..4eb42d82c 100644
--- a/e2e/modal-navigation-ng/package.json
+++ b/e2e/modal-navigation-ng/package.json
@@ -6,57 +6,59 @@
"nativescript": {
"id": "org.nativescript.modalnavigationng",
"tns-android": {
- "version": "4.1.0-2018.4.16.8"
+ "version": "next"
},
"tns-ios": {
"version": "next"
}
},
"dependencies": {
- "@angular/animations": "~6.0.0-rc.3",
- "@angular/common": "~6.0.0-rc.3",
- "@angular/compiler": "~6.0.0-rc.3",
- "@angular/core": "~6.0.0-rc.3",
- "@angular/forms": "~6.0.0-rc.3",
- "@angular/http": "~6.0.0-rc.3",
- "@angular/platform-browser": "~6.0.0-rc.3",
- "@angular/platform-browser-dynamic": "~6.0.0-rc.3",
- "@angular/router": "~6.0.0-rc.3",
+ "@angular/animations": "~6.0.0",
+ "@angular/common": "~6.0.0",
+ "@angular/compiler": "~6.0.0",
+ "@angular/core": "~6.0.0",
+ "@angular/forms": "~6.0.0",
+ "@angular/http": "~6.0.0",
+ "@angular/platform-browser": "~6.0.0",
+ "@angular/platform-browser-dynamic": "~6.0.0",
+ "@angular/router": "~6.0.0",
"nativescript-angular": "file:../../nativescript-angular",
"nativescript-theme-core": "~1.0.4",
"reflect-metadata": "~0.1.8",
- "rxjs": "~6.0.0-rc.1",
+ "rxjs": "~6.1.0",
"tns-core-modules": "next",
"zone.js": "~0.8.2"
},
"devDependencies": {
- "@angular/compiler-cli": "~6.0.0-rc.3",
- "@ngtools/webpack": "~1.9.4",
+ "@angular-devkit/core": "~0.6.3",
+ "@angular/compiler-cli": "~6.0.0",
+ "@ngtools/webpack": "~6.0.3",
"@types/chai": "^4.0.2",
"@types/mocha": "^2.2.41",
"@types/node": "^7.0.5",
"babel-traverse": "6.26.0",
"babel-types": "6.26.0",
"babylon": "6.18.0",
- "copy-webpack-plugin": "~4.3.0",
- "css-loader": "~0.28.7",
+ "clean-webpack-plugin": "~0.1.19",
+ "copy-webpack-plugin": "~4.5.1",
+ "css-loader": "~0.28.11",
"extract-text-webpack-plugin": "~3.0.2",
"lazy": "1.0.11",
"nativescript-dev-appium": "next",
"nativescript-dev-typescript": "next",
"nativescript-dev-webpack": "next",
- "nativescript-worker-loader": "~0.8.1",
+ "nativescript-worker-loader": "~0.9.0",
"raw-loader": "~0.5.1",
- "resolve-url-loader": "~2.2.1",
+ "resolve-url-loader": "~2.3.0",
"typescript": "~2.7.2",
- "uglifyjs-webpack-plugin": "~1.1.6",
- "webpack": "~3.10.0",
- "webpack-bundle-analyzer": "^2.9.1",
- "webpack-sources": "~1.1.0",
- "clean-webpack-plugin": "~0.1.19"
+ "uglifyjs-webpack-plugin": "~1.2.5",
+ "webpack": "~4.6.0",
+ "webpack-bundle-analyzer": "~2.13.0",
+ "webpack-cli": "~2.1.3",
+ "webpack-sources": "~1.1.0"
},
"scripts": {
- "e2e": "tsc -p e2e && mocha --opts ./e2e/config/mocha.opts",
+ "e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
"e2e-watch": "tsc -p e2e --watch"
}
}
diff --git a/e2e/modal-navigation-ng/tsconfig.esm.json b/e2e/modal-navigation-ng/tsconfig.esm.json
new file mode 100644
index 000000000..95f2ecee0
--- /dev/null
+++ b/e2e/modal-navigation-ng/tsconfig.esm.json
@@ -0,0 +1,7 @@
+{
+ "extends": "./tsconfig",
+ "compilerOptions": {
+ "module": "es2015",
+ "moduleResolution": "node"
+ }
+}
diff --git a/e2e/router/tsconfig.esm.json b/e2e/router/tsconfig.esm.json
new file mode 100644
index 000000000..95f2ecee0
--- /dev/null
+++ b/e2e/router/tsconfig.esm.json
@@ -0,0 +1,7 @@
+{
+ "extends": "./tsconfig",
+ "compilerOptions": {
+ "module": "es2015",
+ "moduleResolution": "node"
+ }
+}
diff --git a/nativescript-angular/router/ns-location-strategy.ts b/nativescript-angular/router/ns-location-strategy.ts
index 9a8b1d22f..0ed989cd1 100644
--- a/nativescript-angular/router/ns-location-strategy.ts
+++ b/nativescript-angular/router/ns-location-strategy.ts
@@ -173,6 +173,7 @@ export class NSLocationStrategy extends LocationStrategy {
if (!state) {
modalStatesCleared = true;
+ this.callPopState(null, true);
continue;
}
@@ -239,7 +240,16 @@ export class NSLocationStrategy extends LocationStrategy {
private callPopState(state: LocationState, pop: boolean = true) {
const urlSerializer = new DefaultUrlSerializer();
- this.currentUrlTree.root.children[this.currentOutlet] = state.segmentGroup;
+ if (state) {
+ this.currentUrlTree.root.children[this.currentOutlet] = state.segmentGroup;
+ } else {
+ // when closing modal view there are scenarios (e.g. root viewContainerRef) when we need
+ // to clean up the named page router outlet to make sure we will open the modal properly again if needed.
+ delete this.statesByOutlet[this.currentOutlet];
+ delete this.currentUrlTree.root.children[this.currentOutlet];
+ this.currentOutlet = Object.keys(this.statesByOutlet)[0];
+ }
+
const url = urlSerializer.serialize(this.currentUrlTree);
const change = { url: url, pop: pop };
for (let fn of this.popStateCallbacks) {