diff --git a/CHANGELOG.md b/CHANGELOG.md index a2472065c..99d09f304 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ + +## [7.2.1](https://github.com/NativeScript/nativescript-angular/compare/7.2.0...7.2.1) (2019-02-10) + + +### Bug Fixes + +* **location-strategy:** extend support for nested primary outlets ([566896d](https://github.com/NativeScript/nativescript-angular/commit/566896d)) +* Router tracing does not work with webpack ([e87ef68](https://github.com/NativeScript/nativescript-angular/commit/e87ef68)) + + + # [7.2.0](https://github.com/NativeScript/nativescript-angular/compare/7.1.2...7.2.0) (2019-01-31) diff --git a/e2e/nested-router-tab-view/app/app.component.html b/e2e/nested-router-tab-view/app/app.component.html index 1265aa9c8..74b776fc0 100644 --- a/e2e/nested-router-tab-view/app/app.component.html +++ b/e2e/nested-router-tab-view/app/app.component.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/e2e/nested-router-tab-view/app/app.routing.ts b/e2e/nested-router-tab-view/app/app.routing.ts index 276f26d91..b77942d75 100644 --- a/e2e/nested-router-tab-view/app/app.routing.ts +++ b/e2e/nested-router-tab-view/app/app.routing.ts @@ -48,6 +48,10 @@ const routes: Routes = [ path: "home-lazy", loadChildren: "./home-lazy/home-lazy.module#HomeLazyModule", }, + { + path: "custom-tabs", + loadChildren: "./custom-tabs/custom-tabs.module#CustomTabsModule", + }, { path: "tabs", component: TabsComponent, children: [ { path: "players", component: PlayerComponent, outlet: "playerTab" }, diff --git a/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.component.html b/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.component.html new file mode 100644 index 000000000..851c8765a --- /dev/null +++ b/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.component.html @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.component.ts b/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.component.ts new file mode 100644 index 000000000..18ca7663b --- /dev/null +++ b/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.component.ts @@ -0,0 +1,44 @@ +import { Component, OnInit } from '@angular/core'; +import { ModalDialogService, ModalDialogOptions } from "nativescript-angular/directives/dialogs"; +import { RouterExtensions } from "nativescript-angular/router"; +import { ActivatedRoute } from "@angular/router"; +import { confirm } from "tns-core-modules/ui/dialogs"; +import { Page } from 'tns-core-modules/ui/page/page'; + +@Component({ + moduleId: module.id, + selector: 'custom-tabs', + templateUrl: './custom-tabs.component.html' +}) +export class CustomTabsComponent implements OnInit { + + constructor( + private activeRoute: ActivatedRoute, + private routerExtension: RouterExtensions, + private page: Page) { } + + ngOnInit() { + } + + canGoBackParentRoute() { + const canGoBackParentRoute = this.routerExtension.canGoBack({ relativeTo: this.activeRoute }); + const title = "CanGoBack(ParentRoute)"; + this.onShowDialog(title, title + ` ${canGoBackParentRoute}`); + } + + onRootBack() { + this.page.frame.goBack(); + } + + onShowDialog(title: string, result: string) { + let options: any = { + title: title, + message: result, + okButtonText: "Ok" + } + + confirm(options).then((result: boolean) => { + console.log(result); + }) + } +} diff --git a/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.module.ts b/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.module.ts new file mode 100644 index 000000000..cd29535d2 --- /dev/null +++ b/e2e/nested-router-tab-view/app/custom-tabs/custom-tabs.module.ts @@ -0,0 +1,38 @@ +import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; +import { NativeScriptCommonModule } from 'nativescript-angular/common'; +import { NativeScriptRouterModule } from 'nativescript-angular/router'; + +import { CustomTabsComponent } from './custom-tabs.component'; +import { PlayerComponent } from "../player/players.component"; +import { PlayerDetailComponent } from "../player/player-detail.component"; +import { TeamsComponent } from "../team/teams.component"; +import { TeamDetailComponent } from "../team/team-detail.component"; +import { Route } from "@angular/router"; +import { SharedModule } from "../shared.module"; + +const routes: Route[] = [ + { + path: 'tabs', + component: CustomTabsComponent, + children: [ + { path: "players", component: PlayerComponent }, + { path: "player/:id", component: PlayerDetailComponent }, + + { path: "teams", component: TeamsComponent }, + { path: "team/:id", component: TeamDetailComponent }, + ] + }, +]; + +@NgModule({ + declarations: [CustomTabsComponent + ], + imports: [ + NativeScriptCommonModule, + NativeScriptRouterModule, + NativeScriptRouterModule.forChild(routes), + SharedModule + ], + schemas: [NO_ERRORS_SCHEMA] +}) +export class CustomTabsModule { } diff --git a/e2e/nested-router-tab-view/app/login/login.component.html b/e2e/nested-router-tab-view/app/login/login.component.html index f022099ed..26e1c80ac 100644 --- a/e2e/nested-router-tab-view/app/login/login.component.html +++ b/e2e/nested-router-tab-view/app/login/login.component.html @@ -3,7 +3,7 @@ - + \ No newline at end of file diff --git a/e2e/nested-router-tab-view/app/player/player-detail.component.html b/e2e/nested-router-tab-view/app/player/player-detail.component.html index b4039955c..ea76ab3cd 100644 --- a/e2e/nested-router-tab-view/app/player/player-detail.component.html +++ b/e2e/nested-router-tab-view/app/player/player-detail.component.html @@ -1,4 +1,6 @@ - + + + diff --git a/e2e/nested-router-tab-view/app/player/players.component.html b/e2e/nested-router-tab-view/app/player/players.component.html index 780282771..7dc729f6d 100644 --- a/e2e/nested-router-tab-view/app/player/players.component.html +++ b/e2e/nested-router-tab-view/app/player/players.component.html @@ -1,11 +1,11 @@ - + + + - - - + + - + \ No newline at end of file diff --git a/e2e/nested-router-tab-view/app/player/players.component.ts b/e2e/nested-router-tab-view/app/player/players.component.ts index 677168612..b24ef18bd 100644 --- a/e2e/nested-router-tab-view/app/player/players.component.ts +++ b/e2e/nested-router-tab-view/app/player/players.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit, ViewContainerRef } from "@angular/core"; import { DataService, DataItem } from "../data.service"; -import { RouterExtensions } from "nativescript-angular/router"; import { ModalDialogService, ModalDialogOptions } from "nativescript-angular/directives/dialogs"; import { ModalRouterComponent } from "../modal/modal-router/modal-router.component"; @@ -12,7 +11,10 @@ import { ModalRouterComponent } from "../modal/modal-router/modal-router.compone export class PlayerComponent implements OnInit { items: DataItem[]; - constructor(private modal: ModalDialogService, private itemService: DataService, private router: RouterExtensions, private vcRef: ViewContainerRef, ) { } + constructor( + private modal: ModalDialogService, + private itemService: DataService, + private vcRef: ViewContainerRef, ) { } ngOnInit(): void { this.items = this.itemService.getPlayers(); diff --git a/e2e/nested-router-tab-view/app/team/team-detail.component.html b/e2e/nested-router-tab-view/app/team/team-detail.component.html index 13f4b87b7..18086c638 100644 --- a/e2e/nested-router-tab-view/app/team/team-detail.component.html +++ b/e2e/nested-router-tab-view/app/team/team-detail.component.html @@ -1,4 +1,6 @@ - + + + diff --git a/e2e/nested-router-tab-view/app/team/teams.component.html b/e2e/nested-router-tab-view/app/team/teams.component.html index 4126e1d3b..4af94ca66 100644 --- a/e2e/nested-router-tab-view/app/team/teams.component.html +++ b/e2e/nested-router-tab-view/app/team/teams.component.html @@ -1,4 +1,6 @@ - + + + diff --git a/e2e/nested-router-tab-view/e2e/custom-tabs.e2e-spec.ts b/e2e/nested-router-tab-view/e2e/custom-tabs.e2e-spec.ts new file mode 100644 index 000000000..e41b858e3 --- /dev/null +++ b/e2e/nested-router-tab-view/e2e/custom-tabs.e2e-spec.ts @@ -0,0 +1,72 @@ +import { AppiumDriver, createDriver } from "nativescript-dev-appium"; +import { Screen } from "./screen" +import { + testPlayerNavigated, + testTeamNavigated, + testPlayerNextNavigated, + testTeamNextNavigated, +} from "./shared.e2e-spec" + +describe("custom-tabs:", () => { + let driver: AppiumDriver; + let screen: Screen; + + before(async () => { + driver = await createDriver(); + screen = new Screen(driver); + }); + + after(async () => { + await driver.quit(); + console.log("Quit driver!"); + }); + + afterEach(async function () { + if (this.currentTest.state === "failed") { + await driver.logTestArtifacts(this.currentTest.title); + } + }); + + it("loaded custom tab component and tabs", async () => { + await screen.navigateCustomTabsPage(); + await screen.loadedCustomTabsPage(); + await screen.loadedPlayersList(); + await gotoTeamsTab(driver); + await screen.loadedTeamList(); + }); + + it("navigate back to login and again to custom tabs", async () => { + await backRoot(driver); + await screen.loadedLogin(); + await screen.navigateCustomTabsPage(); + await screen.loadedCustomTabsPage(); + await screen.loadedPlayersList(); + await gotoTeamsTab(driver); + await screen.loadedTeamList(); + }); + + it("navigate back to login and again to custom tabs", async () => { + await gotoPlayersTab(driver); + await testPlayerNavigated(screen, screen.playerOne); + await gotoTeamsTab(driver); + await screen.loadedTeamList(); + await testTeamNavigated(screen, screen.teamOne); + await backRoot(driver); + await screen.loadedLogin(); + }); +}); + +async function backRoot(driver: AppiumDriver) { + const btnBackRoot = await driver.findElementByAutomationText("Root Back"); + await btnBackRoot.tap(); +} + +async function gotoPlayersTab(driver: AppiumDriver) { + const btnTabTeams = await driver.findElementByAutomationText("Players Tab"); + await btnTabTeams.tap(); +} + +async function gotoTeamsTab(driver: AppiumDriver) { + const btnTabTeams = await driver.findElementByAutomationText("Teams Tab"); + await btnTabTeams.tap(); +} diff --git a/e2e/nested-router-tab-view/e2e/screen.ts b/e2e/nested-router-tab-view/e2e/screen.ts index 870fdf9bf..f880d33c8 100644 --- a/e2e/nested-router-tab-view/e2e/screen.ts +++ b/e2e/nested-router-tab-view/e2e/screen.ts @@ -1,6 +1,7 @@ import { AppiumDriver, SearchOptions } from "nativescript-dev-appium"; import { assert } from "chai"; +const customTabs = "Custom Tabs Component"; const home = "Home Component"; const about = "About Component"; const aboutNested = "Nested About Component"; @@ -18,6 +19,7 @@ const gotoNextTeam = "next team"; const gotoTeams = "teams"; const gotoHomePage = "Go To Home Page"; +const gotoCustomTabPage = "Go To Lazy Custom Tabs"; const gotoAboutPage = "Go To About Page"; const gotoTabsPage = "Go To Tabs Page"; const confirmDialog = "Ok"; @@ -68,6 +70,12 @@ export class Screen { console.log(home + " loaded!"); } + loadedCustomTabsPage= async () => { + const lblCustomTabs = await this._driver.findElementByAutomationText(customTabs); + assert.isTrue(await lblCustomTabs.isDisplayed()); + console.log(home + " loaded!"); + } + loadedAbout= async () => { const lblAbout = await this._driver.findElementByAutomationText(about); assert.isTrue(await lblAbout.isDisplayed()); @@ -128,6 +136,11 @@ export class Screen { await btnNavToHomePage.tap(); } + navigateCustomTabsPage = async () => { + const btnNavToHomePage = await this._driver.findElementByAutomationText(gotoCustomTabPage); + await btnNavToHomePage.tap(); + } + navigateToAboutPage = async () => { const btnNavToAboutPage = await this._driver.findElementByAutomationText(gotoAboutPage); await btnNavToAboutPage.tap(); diff --git a/nativescript-angular/dom-adapter.ts b/nativescript-angular/dom-adapter.ts index 7ddffb681..9d47b2fe7 100644 --- a/nativescript-angular/dom-adapter.ts +++ b/nativescript-angular/dom-adapter.ts @@ -1,27 +1,15 @@ /* tslint:disable */ import { Type } from "@angular/core"; -import { ɵDomAdapter } from "@angular/platform-browser"; +import { ɵDomAdapter, ɵsetRootDomAdapter } from "@angular/platform-browser"; import { rendererLog, isLogEnabled } from "./trace"; export class NativeScriptDomAdapter implements ɵDomAdapter { static makeCurrent() { + if (isLogEnabled()) { + rendererLog("Setting root DOM adapter..."); + } - // Don't register when bundling (likely AoT setup). - if (!global.TNS_WEBPACK) { - try { - const privateAPI = global.require("@angular/platform-browser"); - const setRootDomAdapter = privateAPI.ɵsetRootDomAdapter; - - if (isLogEnabled()) { - rendererLog("Setting root DOM adapter..."); - } - setRootDomAdapter(new NativeScriptDomAdapter()); - } catch (e) { - if (isLogEnabled()) { - rendererLog("@angular/platform-browser package not present. NOT setting root DOM adapter..."); - } - } - } + ɵsetRootDomAdapter(new NativeScriptDomAdapter()); } hasProperty(_element: any, _name: string) { @@ -44,8 +32,8 @@ export class NativeScriptDomAdapter implements ɵDomAdapter { logGroupEnd(): void { } - get attrToPropMap(): {[key: string]: string} { throw new Error("Not implemented!"); }; - set attrToPropMap(_value: {[key: string]: string}) { throw new Error("Not implemented!"); }; + get attrToPropMap(): { [key: string]: string } { throw new Error("Not implemented!"); }; + set attrToPropMap(_value: { [key: string]: string }) { throw new Error("Not implemented!"); }; public resourceLoaderType: Type = null; setProperty(_el: Element, _name: string, _value: any): any /** TODO #9100 */ { throw new Error("Not implemented!") } @@ -58,11 +46,11 @@ export class NativeScriptDomAdapter implements ɵDomAdapter { querySelector(_el: any /** TODO #9100 */, _selector: string): HTMLElement { throw new Error("Not implemented!") } querySelectorAll(_el: any /** TODO #9100 */, _selector: string): any[] { throw new Error("Not implemented!") } on( - _el: any /** TODO #9100 */, _evt: any /** TODO #9100 */, _listener: any /** TODO #9100 */): any + _el: any /** TODO #9100 */, _evt: any /** TODO #9100 */, _listener: any /** TODO #9100 */): any /** TODO #9100 */ { throw new Error("Not implemented!") } onAndCancel( - _el: any /** TODO #9100 */, _evt: any /** TODO #9100 */, - _listener: any /** TODO #9100 */): Function { throw new Error("Not implemented!") } + _el: any /** TODO #9100 */, _evt: any /** TODO #9100 */, + _listener: any /** TODO #9100 */): Function { throw new Error("Not implemented!") } dispatchEvent(_el: any /** TODO #9100 */, _evt: any /** TODO #9100 */): any /** TODO #9100 */ { throw new Error("Not implemented!") } createMouseEvent(_eventType: any /** TODO #9100 */): any { throw new Error("Not implemented!") } @@ -88,8 +76,8 @@ export class NativeScriptDomAdapter implements ɵDomAdapter { removeChild(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any /** TODO #9100 */ { throw new Error("Not implemented!") } replaceChild( - _el: any /** TODO #9100 */, _newNode: any /** TODO #9100 */, - _oldNode: any /** TODO #9100 */): any /** TODO #9100 */ { throw new Error("Not implemented!") } + _el: any /** TODO #9100 */, _newNode: any /** TODO #9100 */, + _oldNode: any /** TODO #9100 */): any /** TODO #9100 */ { throw new Error("Not implemented!") } remove(_el: any /** TODO #9100 */): Node { throw new Error("Not implemented!") } insertBefore(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any /** TODO #9100 */ { throw new Error("Not implemented!") } @@ -111,13 +99,13 @@ export class NativeScriptDomAdapter implements ɵDomAdapter { createElementNS(_ns: string, _tagName: string, _doc?: any /** TODO #9100 */): Element { throw new Error("Not implemented!") } createTextNode(_text: string, _doc?: any /** TODO #9100 */): Text { throw new Error("Not implemented!") } createScriptTag(_attrName: string, _attrValue: string, _doc?: any /** TODO #9100 */): - HTMLElement { throw new Error("Not implemented!") } + HTMLElement { throw new Error("Not implemented!") } createStyleElement(_css: string, _doc?: any /** TODO #9100 */): HTMLStyleElement { throw new Error("Not implemented!") } createShadowRoot(_el: any /** TODO #9100 */): any { throw new Error("Not implemented!") } getShadowRoot(_el: any /** TODO #9100 */): any { throw new Error("Not implemented!") } getHost(_el: any /** TODO #9100 */): any { throw new Error("Not implemented!") } getDistributedNodes(_el: any /** TODO #9100 */): Node[] { throw new Error("Not implemented!") } - clone /**/ (_node: Node /*T*/): Node /*T*/ { throw new Error("Not implemented!") } + clone /**/(_node: Node /*T*/): Node /*T*/ { throw new Error("Not implemented!") } getElementsByClassName(_element: any /** TODO #9100 */, _name: string): HTMLElement[] { throw new Error("Not implemented!") } getElementsByTagName(_element: any /** TODO #9100 */, _name: string): HTMLElement[] { throw new Error("Not implemented!") } classList(_element: any /** TODO #9100 */): any[] { throw new Error("Not implemented!") } @@ -129,7 +117,7 @@ export class NativeScriptDomAdapter implements ɵDomAdapter { removeStyle(_element: any /** TODO #9100 */, _styleName: string): any /** TODO #9100 */ { throw new Error("Not implemented!") } getStyle(_element: any /** TODO #9100 */, _styleName: string): string { throw new Error("Not implemented!") } hasStyle(_element: any /** TODO #9100 */, _styleName: string, _styleValue?: string): - boolean { throw new Error("Not implemented!") } + boolean { throw new Error("Not implemented!") } tagName(_element: any /** TODO #9100 */): string { throw new Error("Not implemented!") } attributeMap(_element: any /** TODO #9100 */): Map { throw new Error("Not implemented!") } hasAttribute(_element: any /** TODO #9100 */, _attribute: string): boolean { throw new Error("Not implemented!") } @@ -139,7 +127,7 @@ export class NativeScriptDomAdapter implements ɵDomAdapter { setAttribute(_element: any /** TODO #9100 */, _name: string, _value: string): any /** TODO #9100 */ { throw new Error("Not implemented!") } setAttributeNS(_element: any /** TODO #9100 */, _ns: string, _name: string, _value: string): - any /** TODO #9100 */ { throw new Error("Not implemented!") } + any /** TODO #9100 */ { throw new Error("Not implemented!") } removeAttribute(_element: any /** TODO #9100 */, _attribute: string): any /** TODO #9100 */ { throw new Error("Not implemented!") } removeAttributeNS(_element: any /** TODO #9100 */, _ns: string, _attribute: string): any @@ -158,8 +146,8 @@ export class NativeScriptDomAdapter implements ɵDomAdapter { isElementNode(_node: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") } hasShadowRoot(_node: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") } isShadowRoot(_node: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") } - importIntoDoc /**/ (_node: Node /*T*/): Node /*T*/ { throw new Error("Not implemented!") } - adoptNode /**/ (_node: Node /*T*/): Node /*T*/ { throw new Error("Not implemented!") } + importIntoDoc /**/(_node: Node /*T*/): Node /*T*/ { throw new Error("Not implemented!") } + adoptNode /**/(_node: Node /*T*/): Node /*T*/ { throw new Error("Not implemented!") } getHref(_element: any /** TODO #9100 */): string { throw new Error("Not implemented!") } getEventKey(_event: any /** TODO #9100 */): string { throw new Error("Not implemented!") } resolveAndSetHref(_element: any /** TODO #9100 */, _baseUrl: string, _href: string): any @@ -183,7 +171,7 @@ export class NativeScriptDomAdapter implements ɵDomAdapter { getTransitionEnd(): string { throw new Error("Not implemented!") } supportsAnimation(): boolean { throw new Error("Not implemented!") } - supportsCookies(): boolean { return false; } + supportsCookies(): boolean { return false; } getCookie(_name: string): string { throw new Error("Not implemented!") } setCookie(_name: string, _value: string): any /** TODO #9100 */ { throw new Error("Not implemented!") } } diff --git a/nativescript-angular/platform-common.ts b/nativescript-angular/platform-common.ts index 505ec91b2..2f7e958cd 100644 --- a/nativescript-angular/platform-common.ts +++ b/nativescript-angular/platform-common.ts @@ -6,6 +6,7 @@ import "./zone-js/dist/zone-nativescript"; import "./polyfills/array"; import "./polyfills/console"; import { profile, uptime } from "tns-core-modules/profiling"; +import "./dom-adapter"; import { Type, diff --git a/nativescript-angular/platform.ts b/nativescript-angular/platform.ts index 9194a40a3..8919d34fd 100644 --- a/nativescript-angular/platform.ts +++ b/nativescript-angular/platform.ts @@ -40,9 +40,6 @@ if ((global).___TS_UNUSED) { (() => MissingTranslationStrategy)(); } -// Register DOM adapter, if possible. Dynamic platform only! -import "./dom-adapter"; - import { NativeScriptElementSchemaRegistry } from "./schema-registry"; import { FileSystemResourceLoader } from "./resource-loader"; diff --git a/nativescript-angular/router/ns-location-strategy.ts b/nativescript-angular/router/ns-location-strategy.ts index fa6b1c488..7ac6331ba 100644 --- a/nativescript-angular/router/ns-location-strategy.ts +++ b/nativescript-angular/router/ns-location-strategy.ts @@ -1,6 +1,6 @@ import { Injectable } from "@angular/core"; import { LocationStrategy } from "@angular/common"; -import { DefaultUrlSerializer, UrlSegmentGroup, UrlTree } from "@angular/router"; +import { DefaultUrlSerializer, UrlSegmentGroup, UrlTree, ActivatedRouteSnapshot } from "@angular/router"; import { routerLog, routerError, isLogEnabled } from "../trace"; import { NavigationTransition, Frame } from "tns-core-modules/ui/frame"; import { isPresent } from "../lang-facade"; @@ -88,6 +88,7 @@ export interface LocationState { segmentGroup: UrlSegmentGroup; isRootSegmentGroup: boolean; isPageNavigation: boolean; + frame?: Frame; } @Injectable() @@ -162,7 +163,7 @@ export class NSLocationStrategy extends LocationStrategy { if (!Object.keys(urlTreeRoot.children).length) { const segmentGroup = this.currentUrlTree && this.currentUrlTree.root; const outletKey = this.getOutletKey(this.getSegmentGroupFullPath(segmentGroup), "primary"); - const outlet = this.findOutletByKey(outletKey); + const outlet = this.findOutlet(outletKey); if (outlet && this.updateStates(outlet, segmentGroup)) { this.currentOutlet = outlet; // If states updated @@ -186,12 +187,12 @@ export class NSLocationStrategy extends LocationStrategy { const outletPath = this.getSegmentGroupFullPath(currentTree); let outletKey = this.getOutletKey(outletPath, outletName); - let outlet = this.findOutletByKey(outletKey); + let outlet = this.findOutlet(outletKey); const parentOutletName = currentTree.outlet || ""; const parentOutletPath = this.getSegmentGroupFullPath(currentTree.parent); const parentOutletKey = this.getOutletKey(parentOutletPath, parentOutletName); - const parentOutlet = this.findOutletByKey(parentOutletKey); + const parentOutlet = this.findOutlet(parentOutletKey); const containsLastState = outlet && outlet.containsTopState(currentSegmentGroup.toString()); if (!outlet) { @@ -237,7 +238,7 @@ export class NSLocationStrategy extends LocationStrategy { throw new Error("NSLocationStrategy.forward() - not implemented"); } - back(outlet?: Outlet): void { + back(outlet?: Outlet, frame?: Frame): void { this.currentOutlet = outlet || this.currentOutlet; if (this.currentOutlet.isPageNavigationBack) { @@ -247,6 +248,13 @@ export class NSLocationStrategy extends LocationStrategy { let state = states.pop(); let count = 1; + if (frame) { + while (state.frame && state.frame !== frame) { + state = states.pop(); + count++; + } + } + while (!state.isPageNavigation) { state = states.pop(); count++; @@ -447,7 +455,13 @@ export class NSLocationStrategy extends LocationStrategy { return this.outlets; } - updateOutletFrame(outlet: Outlet, frame: Frame) { + updateOutletFrame(outlet: Outlet, frame: Frame, isEmptyOutletFrame: boolean) { + const lastState = outlet.peekState(); + + if (lastState && !lastState.frame && !isEmptyOutletFrame) { + lastState.frame = frame; + } + if (!outlet.containsFrame(frame)) { outlet.frames.push(frame); } @@ -547,12 +561,17 @@ export class NSLocationStrategy extends LocationStrategy { }); } - findOutletByOutletPath(pathByOutlets: string): Outlet { - return this.outlets.find((outlet) => outlet.pathByOutlets === pathByOutlets); - } + findOutlet(outletKey: string, activatedRouteSnapshot?: ActivatedRouteSnapshot): Outlet { + let outlet: Outlet = this.outlets.find((currentOutlet) => currentOutlet.outletKeys.indexOf(outletKey) > -1); - findOutletByKey(outletKey: string): Outlet { - return this.outlets.find((outlet) => outlet.outletKeys.indexOf(outletKey) > -1); + // No Outlet with the given outletKey could happen when using nested unnamed p-r-o + // primary -> primary -> prymary + if (!outlet && activatedRouteSnapshot) { + const pathByOutlets = this.getPathByOutlets(activatedRouteSnapshot); + outlet = this.outlets.find((currentOutlet) => currentOutlet.pathByOutlets === pathByOutlets); + } + + return outlet; } private getOutletByFrame(frame: Frame): Outlet { diff --git a/nativescript-angular/router/ns-route-reuse-strategy.ts b/nativescript-angular/router/ns-route-reuse-strategy.ts index 0fa0f3e03..4de61062d 100644 --- a/nativescript-angular/router/ns-route-reuse-strategy.ts +++ b/nativescript-angular/router/ns-route-reuse-strategy.ts @@ -100,7 +100,7 @@ export class NSRouteReuseStrategy implements RouteReuseStrategy { route = findTopActivatedRouteNodeForOutlet(route); const outletKey = this.location.getRouteFullPath(route); - const outlet = this.location.findOutletByKey(outletKey); + const outlet = this.location.findOutlet(outletKey, route); const key = getSnapshotKey(route); const isPageActivated = route[pageRouterActivatedSymbol]; const isBack = outlet ? outlet.isPageNavigationBack : false; @@ -125,7 +125,7 @@ export class NSRouteReuseStrategy implements RouteReuseStrategy { route = findTopActivatedRouteNodeForOutlet(route); const outletKey = this.location.getRouteFullPath(route); - const outlet = this.location.findOutletByKey(outletKey); + const outlet = this.location.findOutlet(outletKey, route); const cache = this.cacheByOutlet[outletKey]; if (!cache) { return false; @@ -185,7 +185,7 @@ export class NSRouteReuseStrategy implements RouteReuseStrategy { route = findTopActivatedRouteNodeForOutlet(route); const outletKey = this.location.getRouteFullPath(route); - const outlet = this.location.findOutletByKey(outletKey); + const outlet = this.location.findOutlet(outletKey, route); const cache = this.cacheByOutlet[outletKey]; if (!cache) { return null; diff --git a/nativescript-angular/router/page-router-outlet.ts b/nativescript-angular/router/page-router-outlet.ts index cd105c52a..381ff52bd 100644 --- a/nativescript-angular/router/page-router-outlet.ts +++ b/nativescript-angular/router/page-router-outlet.ts @@ -287,7 +287,7 @@ export class PageRouterOutlet implements OnDestroy { // tslint:disable-line:dire } this.outlet.isNSEmptyOutlet = this.isEmptyOutlet; - this.locationStrategy.updateOutletFrame(this.outlet, this.frame); + this.locationStrategy.updateOutletFrame(this.outlet, this.frame, this.isEmptyOutlet); if (this.outlet && this.outlet.isPageNavigationBack) { if (isLogEnabled()) { @@ -355,7 +355,7 @@ export class PageRouterOutlet implements OnDestroy { // tslint:disable-line:dire page.on(Page.navigatedFromEvent, (global).Zone.current.wrap((args: NavigatedData) => { if (args.isBackNavigation) { this.locationStrategy._beginBackPageNavigation(this.frame); - this.locationStrategy.back(); + this.locationStrategy.back(null, this.frame); } })); @@ -394,14 +394,14 @@ export class PageRouterOutlet implements OnDestroy { // tslint:disable-line:dire queue.push(childRoute); }); - const nodeToMark = findTopActivatedRouteNodeForOutlet(currentRoute); - let outletKeyForRoute = this.locationStrategy.getRouteFullPath(nodeToMark); - let outlet = this.locationStrategy.findOutletByKey(outletKeyForRoute); + const topActivatedRoute = findTopActivatedRouteNodeForOutlet(currentRoute); + let outletKey = this.locationStrategy.getRouteFullPath(topActivatedRoute); + let outlet = this.locationStrategy.findOutlet(outletKey, topActivatedRoute); if (outlet && outlet.frames.length) { - nodeToMark[pageRouterActivatedSymbol] = true; + topActivatedRoute[pageRouterActivatedSymbol] = true; if (isLogEnabled()) { - log("Activated route marked as page: " + routeToString(nodeToMark)); + log("Activated route marked as page: " + routeToString(topActivatedRoute)); } } @@ -429,20 +429,17 @@ export class PageRouterOutlet implements OnDestroy { // tslint:disable-line:dire if (modalNavigation > 0) { // Modal with 'primary' p-r-o outlet = this.locationStrategy.findOutletByModal(modalNavigation); } else { - outlet = this.locationStrategy.findOutletByKey(outletKey); + outlet = this.locationStrategy.findOutlet(outletKey, topActivatedRoute); } // Named lazy loaded outlet. if (!outlet && this.isEmptyOutlet) { const parentOutletKey = this.locationStrategy.getRouteFullPath(topActivatedRoute.parent); - outlet = this.locationStrategy.findOutletByKey(parentOutletKey); + outlet = this.locationStrategy.findOutlet(parentOutletKey, topActivatedRoute.parent); if (outlet) { outlet.outletKeys.push(outletKey); } - } else if (!outlet) { - const pathByOutlets = this.locationStrategy.getPathByOutlets(topActivatedRoute); - outlet = this.locationStrategy.findOutletByOutletPath(pathByOutlets); } return outlet; diff --git a/nativescript-angular/router/router-extensions.ts b/nativescript-angular/router/router-extensions.ts index 9f2379488..5959e95dc 100644 --- a/nativescript-angular/router/router-extensions.ts +++ b/nativescript-angular/router/router-extensions.ts @@ -126,7 +126,7 @@ export class RouterExtensions { const currentRouteSnapshop = findTopActivatedRouteNodeForOutlet(currentRoute.snapshot); const outletKey = this.locationStrategy.getRouteFullPath(currentRouteSnapshop); - outlet = this.locationStrategy.findOutletByKey(outletKey); + outlet = this.locationStrategy.findOutlet(outletKey, currentRouteSnapshop); return outlet; } diff --git a/tests/app/tests/ns-location-strategy.ts b/tests/app/tests/ns-location-strategy.ts index 2f7208903..1c1ee221d 100644 --- a/tests/app/tests/ns-location-strategy.ts +++ b/tests/app/tests/ns-location-strategy.ts @@ -142,7 +142,7 @@ function simulatePageNavigation(strategy: NSLocationStrategy, url: string, frame outletName = outletName || "primary"; strategy.pushState(null, null, url, null); - const outlet: Outlet = strategy.findOutletByOutletPath(outletName); + const outlet: Outlet = strategy.findOutlet(outletName); outlet.frames.push(frame); strategy._beginPageNavigation(frame); } @@ -281,7 +281,7 @@ describe("NSLocationStrategy", () => { strategy.pushState(null, null, "/internal", null); expectedStates.push(createState("/internal", outletName)); - const outlet: Outlet = strategy.findOutletByOutletPath(outletName); + const outlet: Outlet = strategy.findOutlet(outletName); assertStatesEqual(outlet.states, expectedStates); }); @@ -306,8 +306,8 @@ describe("NSLocationStrategy", () => { strategy.pushState(null, null, "/(test1:internal//test2:test2)", null); expectedStatesTest1.push(createState("/(test1:internal//test2:test2)", outletName)); - const outlet: Outlet = strategy.findOutletByOutletPath(outletName); - const outlet2: Outlet = strategy.findOutletByOutletPath(outletName2); + const outlet: Outlet = strategy.findOutlet(outletName); + const outlet2: Outlet = strategy.findOutlet(outletName2); assertStatesEqual(outlet.states, expectedStatesTest1); assertStatesEqual(outlet2.states, expectedStatesTest2); @@ -329,7 +329,7 @@ describe("NSLocationStrategy", () => { }); simulatePageNavigation(strategy, "/page", currentFrame, outletName); - const outlet: Outlet = strategy.findOutletByOutletPath(outletName); + const outlet: Outlet = strategy.findOutlet(outletName); assert.equal(frameBackCount, 0); assert.equal(popCount, 0); @@ -360,8 +360,8 @@ describe("NSLocationStrategy", () => { const currentFrame = frameService.getFrame(); simulatePageNavigation(strategy, "/(test1:page//test2:test2)", frame, outletName); simulatePageNavigation(strategy, "/(test1:page//test2:test2)", currentFrame, outletName2); - const outlet: Outlet = strategy.findOutletByOutletPath(outletName); - const outlet2: Outlet = strategy.findOutletByOutletPath(outletName2); + const outlet: Outlet = strategy.findOutlet(outletName); + const outlet2: Outlet = strategy.findOutlet(outletName2); assert.equal(frameBackCount, 0); assert.equal(popCount, 0); @@ -392,7 +392,7 @@ describe("NSLocationStrategy", () => { }); simulatePageNavigation(strategy, "/page", currentFrame, outletName); - const outlet: Outlet = strategy.findOutletByOutletPath(outletName); + const outlet: Outlet = strategy.findOutlet(outletName); assert.equal(frameBackCount, 0); assert.equal(popCount, 0); @@ -424,8 +424,8 @@ describe("NSLocationStrategy", () => { simulatePageNavigation(strategy, "/(test1:page//test2:test2)", frame, outletName); simulatePageNavigation(strategy, "/(test1:page//test2:test2)", frame2, outletName2); - const outlet: Outlet = strategy.findOutletByOutletPath(outletName); - const outlet2: Outlet = strategy.findOutletByOutletPath(outletName2); + const outlet: Outlet = strategy.findOutlet(outletName); + const outlet2: Outlet = strategy.findOutlet(outletName2); assert.equal(frameBackCount, 0); assert.equal(popCount, 0); @@ -449,7 +449,7 @@ describe("NSLocationStrategy", () => { // Act strategy._setNavigationOptions({ clearHistory: true }); simulatePageNavigation(strategy, "/cleared", frame, outletName); - const outlet: Outlet = strategy.findOutletByOutletPath(outletName); + const outlet: Outlet = strategy.findOutlet(outletName); // Assert assertStatesEqual(outlet.states, [createState("/cleared", outletName, true)]); }); @@ -472,8 +472,8 @@ describe("NSLocationStrategy", () => { createState("/(test1:cleared//test2:test2)", outletName2, true) ]; - const outlet: Outlet = strategy.findOutletByOutletPath(outletName); - const outlet2: Outlet = strategy.findOutletByOutletPath(outletName2); + const outlet: Outlet = strategy.findOutlet(outletName); + const outlet2: Outlet = strategy.findOutlet(outletName2); // Assert assertStatesEqual(outlet.states, expectedStatesTest1);