From 451d115c80816d9456e5ab2789cb246194486da6 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Mon, 13 Jun 2016 19:05:03 +0300 Subject: [PATCH 1/3] PageRouterOutlet for @angular/router v.3 --- .travis.yml | 1 + .../common/detached-loader.ts | 15 +- nativescript-angular/package.json | 1 + nativescript-angular/router-deprecated.ts | 5 + .../router-deprecated/ns-router-deprecated.ts | 19 + .../router-deprecated/ns-router-link.ts | 61 +++ .../router-deprecated/page-router-outlet.ts | 316 +++++++++++++++ nativescript-angular/router.ts | 6 +- .../router/ns-location-strategy.ts | 11 +- .../router/ns-platform-location.ts | 68 ++++ nativescript-angular/router/ns-router-link.ts | 73 ++-- nativescript-angular/router/ns-router.ts | 24 +- .../router/page-router-outlet.ts | 365 ++++++------------ ng-sample/.vscode/launch.json | 166 ++++---- ng-sample/app/app.ts | 36 +- .../login-test.ts | 0 .../nav-component.ts | 0 .../navigation-test.ts | 0 .../router-outlet-test.css | 0 .../router-outlet-test.ts | 0 .../router/page-router-outlet-nested-test.ts | 149 +++++++ .../router/page-router-outlet-test.ts | 135 +++++++ .../app/examples/router/router-outlet-test.ts | 74 ++++ ng-sample/app/examples/router/styles.css | 50 +++ ng-sample/package.json | 21 +- 25 files changed, 1195 insertions(+), 401 deletions(-) create mode 100644 nativescript-angular/router-deprecated.ts create mode 100644 nativescript-angular/router-deprecated/ns-router-deprecated.ts create mode 100644 nativescript-angular/router-deprecated/ns-router-link.ts create mode 100644 nativescript-angular/router-deprecated/page-router-outlet.ts create mode 100644 nativescript-angular/router/ns-platform-location.ts rename ng-sample/app/examples/{navigation => router-deprecated}/login-test.ts (100%) rename ng-sample/app/examples/{navigation => router-deprecated}/nav-component.ts (100%) rename ng-sample/app/examples/{navigation => router-deprecated}/navigation-test.ts (100%) rename ng-sample/app/examples/{navigation => router-deprecated}/router-outlet-test.css (100%) rename ng-sample/app/examples/{navigation => router-deprecated}/router-outlet-test.ts (100%) create mode 100644 ng-sample/app/examples/router/page-router-outlet-nested-test.ts create mode 100644 ng-sample/app/examples/router/page-router-outlet-test.ts create mode 100644 ng-sample/app/examples/router/router-outlet-test.ts create mode 100644 ng-sample/app/examples/router/styles.css diff --git a/.travis.yml b/.travis.yml index 704747505..ce375f07c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,7 @@ cache: install: - nvm install 4 + - npm install -g typings - npm install -g nativescript --ignore-scripts - tns usage-reporting disable - tns error-reporting disable diff --git a/nativescript-angular/common/detached-loader.ts b/nativescript-angular/common/detached-loader.ts index 1734ceefc..62af15209 100644 --- a/nativescript-angular/common/detached-loader.ts +++ b/nativescript-angular/common/detached-loader.ts @@ -1,4 +1,5 @@ -import {ComponentRef, ViewContainerRef, Component, Type, ViewChild, ComponentResolver, ChangeDetectorRef, Host} from '@angular/core'; +import {ComponentRef, ComponentFactory, ViewContainerRef, Component, + Type, ViewChild, ComponentResolver, ChangeDetectorRef, Host} from '@angular/core'; import trace = require("trace"); type AnyComponentRef = ComponentRef; @@ -22,12 +23,12 @@ function log(message: string) { template: `` }) export class DetachedLoader { - @ViewChild('loader', { read: ViewContainerRef }) containerRef: ViewContainerRef; + @ViewChild('loader', { read: ViewContainerRef }) childContainerRef: ViewContainerRef; private viewLoaded = false; private pendingLoads: PendingLoadEntry[] = []; - constructor(private compiler: ComponentResolver, private changeDetector: ChangeDetectorRef) { } + constructor(private compiler: ComponentResolver, private changeDetector: ChangeDetectorRef, private containerRef: ViewContainerRef) { } public ngAfterViewInit() { log("DetachedLoader.ngAfterViewInit"); @@ -42,7 +43,7 @@ export class DetachedLoader { private loadInLocation(componentType: Type): Promise> { return this.compiler.resolveComponent(componentType).then((componentFactory) => { - return this.containerRef.createComponent(componentFactory, this.containerRef.length, this.containerRef.parentInjector, null); + return this.childContainerRef.createComponent(componentFactory, this.childContainerRef.length, this.childContainerRef.parentInjector, null); }).then((compRef) => { log("DetachedLoader.loadInLocation component loaded -> markForCheck"); // Component is created, buit may not be checked if we are loading @@ -66,7 +67,7 @@ export class DetachedLoader { // so that loading can conitionue. log("DetachedLoader.loadComponent -> markForCheck(with setTimeout())") setTimeout(() => this.changeDetector.markForCheck(), 0); - + return new Promise((resolve, reject) => { this.pendingLoads.push({ componentType: componentType, @@ -75,4 +76,8 @@ export class DetachedLoader { }); } } + + public loadWithFactory(factory: ComponentFactory): ComponentRef { + return this.containerRef.createComponent(factory, this.containerRef.length, this.containerRef.parentInjector, null); + } } diff --git a/nativescript-angular/package.json b/nativescript-angular/package.json index ac31a45ca..21f33adfe 100644 --- a/nativescript-angular/package.json +++ b/nativescript-angular/package.json @@ -26,6 +26,7 @@ "@angular/platform-browser-dynamic": "2.0.0-rc.2", "@angular/platform-server": "2.0.0-rc.2", "@angular/router-deprecated": "2.0.0-rc.2", + "@angular/router": "3.0.0-alpha.6", "rxjs": "5.0.0-beta.6", "zone.js": "^0.6.12", "reflect-metadata": "^0.1.3", diff --git a/nativescript-angular/router-deprecated.ts b/nativescript-angular/router-deprecated.ts new file mode 100644 index 000000000..d88f42a64 --- /dev/null +++ b/nativescript-angular/router-deprecated.ts @@ -0,0 +1,5 @@ +export { + NS_ROUTER_PROVIDERS, + NS_ROUTER_DIRECTIVES, + routerTraceCategory +} from "./router-deprecated/ns-router-deprecated"; diff --git a/nativescript-angular/router-deprecated/ns-router-deprecated.ts b/nativescript-angular/router-deprecated/ns-router-deprecated.ts new file mode 100644 index 000000000..0e6655417 --- /dev/null +++ b/nativescript-angular/router-deprecated/ns-router-deprecated.ts @@ -0,0 +1,19 @@ +import {Type} from '@angular/core/src/facade/lang'; +import {NSRouterLink} from './ns-router-link'; +import {PageRouterOutlet} from './page-router-outlet'; +import {NSLocationStrategy} from '../router/ns-location-strategy'; +import {ROUTER_PROVIDERS} from '@angular/router-deprecated'; +import {LocationStrategy} from '@angular/common'; +import {provide} from '@angular/core'; +export {routerTraceCategory} from "../trace"; + +export const NS_ROUTER_PROVIDERS: any[] = [ + ROUTER_PROVIDERS, + NSLocationStrategy, + provide(LocationStrategy, {useExisting: NSLocationStrategy}), +]; + +export const NS_ROUTER_DIRECTIVES: Type[] = [ + NSRouterLink, + PageRouterOutlet +]; diff --git a/nativescript-angular/router-deprecated/ns-router-link.ts b/nativescript-angular/router-deprecated/ns-router-link.ts new file mode 100644 index 000000000..77cc90177 --- /dev/null +++ b/nativescript-angular/router-deprecated/ns-router-link.ts @@ -0,0 +1,61 @@ +import {Directive, Input} from '@angular/core'; +import {isString} from '@angular/core/src/facade/lang'; +import {Router, Instruction} from '@angular/router-deprecated'; +import {routerLog} from "../trace"; + +/** + * The NSRouterLink directive lets you link to specific parts of your app. + * + * Consider the following route configuration: + * ``` + * @RouteConfig([ + * { path: '/user', component: UserCmp, as: 'User' } + * ]); + * class MyComp {} + * ``` + * + * When linking to this `User` route, you can write: + * + * ``` + * link to user component + * ``` + * + * RouterLink expects the value to be an array of route names, followed by the params + * for that level of routing. For instance `['/Team', {teamId: 1}, 'User', {userId: 2}]` + * means that we want to generate a link for the `Team` route with params `{teamId: 1}`, + * and with a child route `User` with params `{userId: 2}`. + * + * The first route name should be prepended with `/`, `./`, or `../`. + * If the route begins with `/`, the router will look up the route from the root of the app. + * If the route begins with `./`, the router will instead look in the current component's + * children for the route. And if the route begins with `../`, the router will look at the + * current component's parent. + */ +@Directive({ + selector: '[nsRouterLink]', + inputs: ['params: nsRouterLink'], + host: { + '(tap)': 'onTap()', + '[class.router-link-active]': 'isRouteActive' + } +}) +export class NSRouterLink { + private _routeParams: any[]; + + // the instruction passed to the router to navigate + private _navigationInstruction: Instruction; + + constructor(private _router: Router) { } + + get isRouteActive(): boolean { return this._router.isRouteActive(this._navigationInstruction); } + + set params(changes: any[]) { + this._routeParams = changes; + this._navigationInstruction = this._router.generate(this._routeParams); + } + + onTap(): void { + routerLog("NSRouterLink onTap() instruction: " + JSON.stringify(this._navigationInstruction)) + this._router.navigateByInstruction(this._navigationInstruction); + } +} diff --git a/nativescript-angular/router-deprecated/page-router-outlet.ts b/nativescript-angular/router-deprecated/page-router-outlet.ts new file mode 100644 index 000000000..bfc5c804b --- /dev/null +++ b/nativescript-angular/router-deprecated/page-router-outlet.ts @@ -0,0 +1,316 @@ +import {PromiseWrapper} from '@angular/core/src/facade/async'; +import {isBlank, isPresent} from '@angular/core/src/facade/lang'; +import {StringMapWrapper} from '@angular/core/src/facade/collection'; + +import { + Attribute, ComponentRef, + ViewContainerRef, ViewChild, ElementRef, + ReflectiveInjector, provide, Type, + Component, Inject, DynamicComponentLoader, ComponentResolver +} from '@angular/core'; + +import * as routerHooks from '@angular/router-deprecated/src/lifecycle/lifecycle_annotations'; +import {hasLifecycleHook} from '@angular/router-deprecated/src/lifecycle/route_lifecycle_reflector'; + +import {Router, RouterOutlet, RouteData, RouteParams, ComponentInstruction, + OnActivate, OnDeactivate, OnReuse, CanReuse} from '@angular/router-deprecated'; +import {LocationStrategy} from '@angular/common'; +import {topmost} from "ui/frame"; +import {Page, NavigatedData} from "ui/page"; +import {DEVICE} from "../platform-providers"; +import {Device} from "platform"; +import {routerLog} from "../trace"; +import {NSLocationStrategy} from "../router/ns-location-strategy"; +import {DetachedLoader} from "../common/detached-loader"; +import {ViewUtil} from "../view-util"; + +let _resolveToTrue = PromiseWrapper.resolve(true); + +interface CacheItem { + componentRef: ComponentRef; + loaderRef?: ComponentRef; + router: Router; +} + +/** + * Reference Cache + */ +class RefCache { + private cache: Array = new Array(); + + public push(comp: ComponentRef, router: Router, loaderRef?: ComponentRef) { + this.cache.push({ componentRef: comp, router: router, loaderRef: loaderRef }); + } + + public pop(): CacheItem { + return this.cache.pop(); + } + + public peek(): CacheItem { + return this.cache[this.cache.length - 1]; + } +} + +/** + * A router outlet that does page navigation in NativeScript + * + * ## Use + * + * ``` + * + * ``` + */ +@Component({ + selector: 'page-router-outlet', + template: ` + + + ` +}) +export class PageRouterOutlet extends RouterOutlet { + private isInitalPage: boolean = true; + private refCache: RefCache = new RefCache(); + + private componentRef: ComponentRef = null; + private currentInstruction: ComponentInstruction = null; + private viewUtil: ViewUtil; + @ViewChild('loader', { read: ViewContainerRef }) childContainerRef: ViewContainerRef; + + constructor( + private containerRef: ViewContainerRef, + private compiler: ComponentResolver, + private parentRouter: Router, + @Attribute('name') nameAttr: string, + private location: NSLocationStrategy, + loader: DynamicComponentLoader, + @Inject(DEVICE) device: Device + ) { + super(containerRef, loader, parentRouter, nameAttr); + this.viewUtil = new ViewUtil(device); + } + + /** + * Called by the Router to instantiate a new component during the commit phase of a navigation. + * This method in turn is responsible for calling the `routerOnActivate` hook of its child. + */ + activate(nextInstruction: ComponentInstruction): Promise { + this.log("activate", nextInstruction); + let previousInstruction = this.currentInstruction; + this.currentInstruction = nextInstruction; + + if (this.location.isPageNavigatingBack()) { + return this.activateOnGoBack(nextInstruction, previousInstruction); + } else { + return this.activateOnGoForward(nextInstruction, previousInstruction); + } + } + + private activateOnGoBack(nextInstruction: ComponentInstruction, previousInstruction: ComponentInstruction): Promise { + routerLog("PageRouterOutlet.activate() - Back naviation, so load from cache: " + nextInstruction.componentType.name); + + this.location.finishBackPageNavigation(); + + // Get Component form ref and just call the activate hook + let cacheItem = this.refCache.peek(); + this.componentRef = cacheItem.componentRef; + this.replaceChildRouter(cacheItem.router); + + if (hasLifecycleHook(routerHooks.routerOnActivate, this.componentRef.componentType)) { + return (this.componentRef.instance) + .routerOnActivate(nextInstruction, previousInstruction); + } + } + + private activateOnGoForward(nextInstruction: ComponentInstruction, previousInstruction: ComponentInstruction): Promise { + let componentType = nextInstruction.componentType; + let resultPromise: Promise; + let loaderRef: ComponentRef = undefined; + const childRouter = this.parentRouter.childRouter(componentType); + + const providersArray = [ + provide(RouteData, { useValue: nextInstruction.routeData }), + provide(RouteParams, { useValue: new RouteParams(nextInstruction.params) }), + provide(Router, { useValue: childRouter }), + ]; + + if (this.isInitalPage) { + routerLog("PageRouterOutlet.activate() inital page - just load component: " + componentType.name); + this.isInitalPage = false; + resultPromise = this.compiler.resolveComponent(componentType).then((componentFactory) => { + const childInjector = ReflectiveInjector.resolveAndCreate(providersArray, this.containerRef.parentInjector); + return this.containerRef.createComponent(componentFactory, this.containerRef.length, childInjector, null); + }); + } else { + routerLog("PageRouterOutlet.activate() forward navigation - create detached loader in the loader container: " + componentType.name); + + const page = new Page(); + providersArray.push(provide(Page, { useValue: page })); + const childInjector = ReflectiveInjector.resolveAndCreate(providersArray, this.containerRef.parentInjector); + + resultPromise = this.compiler.resolveComponent(DetachedLoader).then((componentFactory) => { + loaderRef = this.childContainerRef.createComponent(componentFactory, this.childContainerRef.length, childInjector, null); + + return (loaderRef.instance).loadComponent(componentType) + .then((actualCoponenetRef) => { + return this.loadComponentInPage(page, actualCoponenetRef); + }); + }); + } + + return resultPromise.then((componentRef) => { + this.componentRef = componentRef; + this.refCache.push(componentRef, childRouter, loaderRef); + + if (hasLifecycleHook(routerHooks.routerOnActivate, componentType)) { + return (this.componentRef.instance) + .routerOnActivate(nextInstruction, previousInstruction); + } + }); + } + + + private loadComponentInPage(page: Page, componentRef: ComponentRef): Promise> { + //Component loaded. Find its root native view. + const componentView = componentRef.location.nativeElement; + //Remove it from original native parent. + this.viewUtil.removeChild(componentView.parent, componentView); + //Add it to the new page + page.content = componentView; + + this.location.navigateToNewPage(); + return new Promise((resolve, reject) => { + page.on('navigatingTo', () => { + // Finish activation when page navigation has started + resolve(componentRef) + }); + + page.on('navigatedFrom', (global).Zone.current.wrap((args: NavigatedData) => { + if (args.isBackNavigation) { + this.location.beginBackPageNavigation(); + this.location.back(); + } + })); + + topmost().navigate({ + animated: true, + create: () => { return page; } + }); + }); + } + + /** + * Called by the {@link Router} when an outlet disposes of a component's contents. + * This method in turn is responsible for calling the `routerOnDeactivate` hook of its child. + */ + deactivate(nextInstruction: ComponentInstruction): Promise { + this.log("deactivate", nextInstruction); + var instruction = this.currentInstruction; + + var next = _resolveToTrue; + if (isPresent(this.componentRef) && + isPresent(instruction) && + hasLifecycleHook(routerHooks.routerOnDeactivate, this.componentRef.componentType)) { + next = PromiseWrapper.resolve( + (this.componentRef.instance).routerOnDeactivate(nextInstruction, this.currentInstruction)); + } + + if (this.location.isPageNavigatingBack()) { + routerLog("PageRouterOutlet.deactivate() while going back - should destroy: " + instruction.componentType.name) + return next.then((_) => { + const popedItem = this.refCache.pop(); + const popedRef = popedItem.componentRef; + + if (this.componentRef !== popedRef) { + throw new Error("Current componentRef is different for cached componentRef"); + } + + if (isPresent(this.componentRef)) { + this.componentRef.destroy(); + this.componentRef = null; + } + + if (isPresent(popedItem.loaderRef)) { + popedItem.loaderRef.destroy(); + } + }); + } else { + return next; + } + } + + /** + * Called by the {@link Router} during recognition phase of a navigation. + * PageRouterOutlet will aways return true as cancelling navigation + * is currently not supported in NativeScript. + */ + routerCanDeactivate(nextInstruction: ComponentInstruction): Promise { + this.log("routerCanDeactivate", nextInstruction); + + return _resolveToTrue; + } + + /** + * Called by the {@link Router} during recognition phase of a navigation. + * + * If the new child component has a different Type than the existing child component, + * this will resolve to `false`. You can't reuse an old component when the new component + * is of a different Type. + * + * Otherwise, this method delegates to the child component's `routerCanReuse` hook if it exists, + * or resolves to true if the hook is not present and params are equal. + */ + routerCanReuse(nextInstruction: ComponentInstruction): Promise { + this.log("routerCanReuse", nextInstruction); + + var result; + + if (isBlank(this.currentInstruction) || this.currentInstruction.componentType != nextInstruction.componentType) { + result = false; + } else if (hasLifecycleHook(routerHooks.routerCanReuse, this.currentInstruction.componentType)) { + result = (this.componentRef.instance) + .routerCanReuse(nextInstruction, this.currentInstruction); + } else { + result = nextInstruction == this.currentInstruction || + (isPresent(nextInstruction.params) && isPresent(this.currentInstruction.params) && + StringMapWrapper.equals(nextInstruction.params, this.currentInstruction.params)); + } + + routerLog("PageRouterOutlet.routerCanReuse(): " + result); + return PromiseWrapper.resolve(result); + } + + /** + * Called by the {@link Router} during recognition phase of a navigation. + * + * If this resolves to `false`, the given navigation is cancelled. + * + * This method delegates to the child component's `routerCanDeactivate` hook if it exists, + * and otherwise resolves to true. + */ + reuse(nextInstruction: ComponentInstruction): Promise { + var previousInstruction = this.currentInstruction; + this.currentInstruction = nextInstruction; + + if (isBlank(this.componentRef)) { + throw new Error(`Cannot reuse an outlet that does not contain a component.`); + } + + return PromiseWrapper.resolve( + hasLifecycleHook(routerHooks.routerOnReuse, this.componentRef.componentType) ? + (this.componentRef.instance).routerOnReuse(nextInstruction, previousInstruction) : true); + } + + private replaceChildRouter(childRouter: Router) { + // HACKY HACKY HACKY + // When navigationg back - we need to set the child router of + // our router - with the one we have created for the previosus page. + // Otherwise router-outlets inside that page wont't work. + // Curretly there is no other way to do that (parentRouter.childRouter() will create ne router). + + this.parentRouter["_childRouter"] = childRouter; + } + + private log(method: string, nextInstruction: ComponentInstruction) { + routerLog("PageRouterOutlet." + method + " isBack: " + this.location.isPageNavigatingBack() + " nextUrl: " + nextInstruction.urlPath); + } +} diff --git a/nativescript-angular/router.ts b/nativescript-angular/router.ts index e4cbce4d9..5f2d3e006 100644 --- a/nativescript-angular/router.ts +++ b/nativescript-angular/router.ts @@ -1,5 +1 @@ -export { - NS_ROUTER_PROVIDERS, - NS_ROUTER_DIRECTIVES, - routerTraceCategory -} from "./router/ns-router"; +export * from "./router/ns-router"; diff --git a/nativescript-angular/router/ns-location-strategy.ts b/nativescript-angular/router/ns-location-strategy.ts index 402a93d5a..a4017082b 100644 --- a/nativescript-angular/router/ns-location-strategy.ts +++ b/nativescript-angular/router/ns-location-strategy.ts @@ -19,6 +19,11 @@ export class NSLocationStrategy extends LocationStrategy { private _isPageNavigationgBack = false; private _isPageNavigatingForward: boolean = false; + constructor() { + super(); + routerLog("NSLocationStrategy.constructor()"); + } + path(): string { routerLog("NSLocationStrategy.path()"); let state = this.peekState(); @@ -32,16 +37,14 @@ export class NSLocationStrategy extends LocationStrategy { pushState(state: any, title: string, url: string, queryParams: string): void { - routerLog(`NSLocationStrategy.pushState state: ${state}, title: ${title}, url: ${url}, queryParams: ${queryParams}`); + routerLog(`NSLocationStrategy.pushState state: ${state}, title: ${title}, url: ${url}, queryParams: ${queryParams}, isPageNavigation: ${this._isPageNavigatingForward}`); this.pushStateInternal(state, title, url, queryParams); } pushStateInternal(state: any, title: string, url: string, queryParams: string): void { - routerLog(`NSLocationStrategy.pushState state: ${state}, title: ${title}, url: ${url}, queryParams: ${queryParams}`); - let isNewPage = this._isPageNavigatingForward; - this._isPageNavigatingForward = false; + this._isPageNavigatingForward = false; this.states.push({ state: state, title: title, diff --git a/nativescript-angular/router/ns-platform-location.ts b/nativescript-angular/router/ns-platform-location.ts new file mode 100644 index 000000000..900ba4e23 --- /dev/null +++ b/nativescript-angular/router/ns-platform-location.ts @@ -0,0 +1,68 @@ +import {NSLocationStrategy} from './ns-location-strategy'; +import {PlatformLocation, UrlChangeListener} from '@angular/common'; +import {Injectable} from '@angular/core'; +import {routerLog} from "../trace"; + + +@Injectable() +export class NativescriptPlatformLocation extends PlatformLocation { + + constructor(private locationStartegy: NSLocationStrategy) { + super(); + routerLog("NativescriptPlatformLocation.constructor()"); + } + + getBaseHrefFromDOM(): string { + return "/"; + } + + onPopState(fn: UrlChangeListener): void { + routerLog("NativescriptPlatformLocation.onPopState()"); + this.locationStartegy.onPopState(fn); + } + + onHashChange(fn: UrlChangeListener): void { + routerLog("NativescriptPlatformLocation.onHashChange()"); + } + + get search(): string { + routerLog("NativescriptPlatformLocation.get search()"); + + return ""; + } + get hash(): string { + routerLog("NativescriptPlatformLocation.get hash()"); + + return ""; + } + get pathname(): string { + routerLog("NativescriptPlatformLocation.get pathname()"); + return this.locationStartegy.path(); + } + set pathname(newPath: string) { + routerLog("NativescriptPlatformLocation.set pathname(): " + newPath); + } + + pushState(state: any, title: string, url: string): void { + routerLog("NativescriptPlatformLocation.pushState()"); + + this.locationStartegy.pushState(state, title, url, null); + } + + replaceState(state: any, title: string, url: string): void { + routerLog("NativescriptPlatformLocation.replaceState()"); + this.locationStartegy.replaceState(state, title, url, null); + } + + forward(): void { + routerLog("NativescriptPlatformLocation.forward()"); + + throw new Error("NativescriptPlatformLocation.forward() not implemend"); + } + + back(): void { + routerLog("NativescriptPlatformLocation.back()"); + + this.locationStartegy.back(); + } +} diff --git a/nativescript-angular/router/ns-router-link.ts b/nativescript-angular/router/ns-router-link.ts index 77cc90177..c2797134d 100644 --- a/nativescript-angular/router/ns-router-link.ts +++ b/nativescript-angular/router/ns-router-link.ts @@ -1,61 +1,54 @@ -import {Directive, Input} from '@angular/core'; -import {isString} from '@angular/core/src/facade/lang'; -import {Router, Instruction} from '@angular/router-deprecated'; -import {routerLog} from "../trace"; +import {Directive, HostBinding, HostListener, Input} from '@angular/core'; + +import {Router, ActivatedRoute} from '@angular/router'; /** - * The NSRouterLink directive lets you link to specific parts of your app. + * The RouterLink directive lets you link to specific parts of your app. * * Consider the following route configuration: + * ``` - * @RouteConfig([ - * { path: '/user', component: UserCmp, as: 'User' } - * ]); - * class MyComp {} + * [{ path: '/user', component: UserCmp }] * ``` * * When linking to this `User` route, you can write: * * ``` - * link to user component + * link to user component * ``` * - * RouterLink expects the value to be an array of route names, followed by the params - * for that level of routing. For instance `['/Team', {teamId: 1}, 'User', {userId: 2}]` - * means that we want to generate a link for the `Team` route with params `{teamId: 1}`, - * and with a child route `User` with params `{userId: 2}`. + * RouterLink expects the value to be an array of path segments, followed by the params + * for that level of routing. For instance `['/team', {teamId: 1}, 'user', {userId: 2}]` + * means that we want to generate a link to `/team;teamId=1/user;userId=2`. * - * The first route name should be prepended with `/`, `./`, or `../`. - * If the route begins with `/`, the router will look up the route from the root of the app. - * If the route begins with `./`, the router will instead look in the current component's - * children for the route. And if the route begins with `../`, the router will look at the - * current component's parent. + * The first segment name can be prepended with `/`, `./`, or `../`. + * If the segment begins with `/`, the router will look up the route from the root of the app. + * If the segment begins with `./`, or doesn't begin with a slash, the router will + * instead look in the current component's children for the route. + * And if the segment begins with `../`, the router will go up one level. */ -@Directive({ - selector: '[nsRouterLink]', - inputs: ['params: nsRouterLink'], - host: { - '(tap)': 'onTap()', - '[class.router-link-active]': 'isRouteActive' - } -}) +@Directive({selector: '[nsRouterLink]'}) export class NSRouterLink { - private _routeParams: any[]; + @Input() queryParams: {[k: string]: any}; + @Input() fragment: string; - // the instruction passed to the router to navigate - private _navigationInstruction: Instruction; + private commands: any[] = []; - constructor(private _router: Router) { } + constructor(private router: Router, private route: ActivatedRoute) {} - get isRouteActive(): boolean { return this._router.isRouteActive(this._navigationInstruction); } - - set params(changes: any[]) { - this._routeParams = changes; - this._navigationInstruction = this._router.generate(this._routeParams); + @Input("nsRouterLink") + set params(data: any[]|string) { + if (Array.isArray(data)) { + this.commands = data; + } else { + this.commands = [data]; } + } - onTap(): void { - routerLog("NSRouterLink onTap() instruction: " + JSON.stringify(this._navigationInstruction)) - this._router.navigateByInstruction(this._navigationInstruction); - } + @HostListener("tap") + onTap() { + this.router.navigate( + this.commands, + {relativeTo: this.route, queryParams: this.queryParams, fragment: this.fragment}); + } } diff --git a/nativescript-angular/router/ns-router.ts b/nativescript-angular/router/ns-router.ts index 0e8d3b2ef..81f26b65e 100644 --- a/nativescript-angular/router/ns-router.ts +++ b/nativescript-angular/router/ns-router.ts @@ -1,19 +1,33 @@ import {Type} from '@angular/core/src/facade/lang'; +import {provide, Injectable} from '@angular/core'; +import {LocationStrategy, PlatformLocation, UrlChangeListener} from '@angular/common'; +import { RouterConfig } from '@angular/router'; +import { provideRouter, ExtraOptions } from '@angular/router/common_router_providers'; + import {NSRouterLink} from './ns-router-link'; import {PageRouterOutlet} from './page-router-outlet'; import {NSLocationStrategy} from './ns-location-strategy'; -import {ROUTER_PROVIDERS} from '@angular/router-deprecated'; -import {LocationStrategy} from '@angular/common'; -import {provide} from '@angular/core'; +import {NativescriptPlatformLocation} from './ns-platform-location'; +import {routerLog} from "../trace"; + export {routerTraceCategory} from "../trace"; export const NS_ROUTER_PROVIDERS: any[] = [ - ROUTER_PROVIDERS, NSLocationStrategy, - provide(LocationStrategy, {useExisting: NSLocationStrategy}), + provide(LocationStrategy, { useExisting: NSLocationStrategy }), + + NativescriptPlatformLocation, + provide(PlatformLocation, { useClass: NativescriptPlatformLocation }), ]; export const NS_ROUTER_DIRECTIVES: Type[] = [ NSRouterLink, PageRouterOutlet ]; + +export function nsProvideRouter(config: RouterConfig, opts: ExtraOptions): any[] { + return [ + ...NS_ROUTER_PROVIDERS, + ...provideRouter(config, opts) + ] +}; \ No newline at end of file diff --git a/nativescript-angular/router/page-router-outlet.ts b/nativescript-angular/router/page-router-outlet.ts index cf8b002aa..0d919be66 100644 --- a/nativescript-angular/router/page-router-outlet.ts +++ b/nativescript-angular/router/page-router-outlet.ts @@ -1,35 +1,24 @@ -import {PromiseWrapper} from '@angular/core/src/facade/async'; +import { + Attribute, ComponentFactory, ComponentRef, Directive, + ReflectiveInjector, ResolvedReflectiveProvider, ViewContainerRef, + Inject, ComponentResolver, provide} from '@angular/core'; + import {isBlank, isPresent} from '@angular/core/src/facade/lang'; -import {StringMapWrapper} from '@angular/core/src/facade/collection'; -import { - Attribute, ComponentRef, - ViewContainerRef, ViewChild, ElementRef, - ReflectiveInjector, provide, Type, - Component, Inject, DynamicComponentLoader, ComponentResolver -} from '@angular/core'; - -import * as routerHooks from '@angular/router-deprecated/src/lifecycle/lifecycle_annotations'; -import {hasLifecycleHook} from '@angular/router-deprecated/src/lifecycle/route_lifecycle_reflector'; - -import {Router, RouterOutlet, RouteData, RouteParams, ComponentInstruction, - OnActivate, OnDeactivate, OnReuse, CanReuse} from '@angular/router-deprecated'; -import {LocationStrategy} from '@angular/common'; -import {topmost} from "ui/frame"; -import {Page, NavigatedData} from "ui/page"; +import {RouterOutletMap, ActivatedRoute, PRIMARY_OUTLET} from '@angular/router'; +import {RouterOutlet} from '@angular/router/directives/router_outlet'; +import {NSLocationStrategy} from "./ns-location-strategy"; import {DEVICE} from "../platform-providers"; import {Device} from "platform"; import {routerLog} from "../trace"; -import {NSLocationStrategy} from "./ns-location-strategy"; import {DetachedLoader} from "../common/detached-loader"; import {ViewUtil} from "../view-util"; - -let _resolveToTrue = PromiseWrapper.resolve(true); +import {topmost} from "ui/frame"; +import {Page, NavigatedData} from "ui/page"; interface CacheItem { componentRef: ComponentRef; loaderRef?: ComponentRef; - router: Router; } /** @@ -38,8 +27,8 @@ interface CacheItem { class RefCache { private cache: Array = new Array(); - public push(comp: ComponentRef, router: Router, loaderRef?: ComponentRef) { - this.cache.push({ componentRef: comp, router: router, loaderRef: loaderRef }); + public push(comp: ComponentRef, loaderRef?: ComponentRef) { + this.cache.push({ componentRef: comp, loaderRef: loaderRef }); } public pop(): CacheItem { @@ -51,125 +40,134 @@ class RefCache { } } -/** - * A router outlet that does page navigation in NativeScript - * - * ## Use - * - * ``` - * - * ``` - */ -@Component({ - selector: 'page-router-outlet', - template: ` - - - ` -}) + + +@Directive({ selector: 'page-router-outlet' }) export class PageRouterOutlet extends RouterOutlet { - private isInitalPage: boolean = true; + private viewUtil: ViewUtil; private refCache: RefCache = new RefCache(); + private isInitalPage: boolean = true; + private detachedLoaderFactory: ComponentFactory; - private componentRef: ComponentRef = null; - private currentInstruction: ComponentInstruction = null; - private viewUtil: ViewUtil; - @ViewChild('loader', { read: ViewContainerRef }) childContainerRef: ViewContainerRef; + private currnetActivatedComp: ComponentRef; + private currentActivatedRoute: ActivatedRoute; + + get isActivated(): boolean { + return !!this.currnetActivatedComp; + } + + get component(): Object { + if (!this.currnetActivatedComp) { + throw new Error('Outlet is not activated'); + } + + return this.currnetActivatedComp.instance; + } + get activatedRoute(): ActivatedRoute { + if (!this.currnetActivatedComp) { + throw new Error('Outlet is not activated'); + } + + return this.currentActivatedRoute; + } constructor( + parentOutletMap: RouterOutletMap, private containerRef: ViewContainerRef, - private compiler: ComponentResolver, - private parentRouter: Router, - @Attribute('name') nameAttr: string, - private location: NSLocationStrategy, - loader: DynamicComponentLoader, - @Inject(DEVICE) device: Device - ) { - super(containerRef, loader, parentRouter, nameAttr); + @Attribute('name') name: string, + private locationStrategy: NSLocationStrategy, + compiler: ComponentResolver, + @Inject(DEVICE) device: Device) { + super(parentOutletMap, containerRef, name) + this.viewUtil = new ViewUtil(device); + compiler.resolveComponent(DetachedLoader).then((detachedLoaderFactory) => { + log("DetachedLoaderFactory leaded"); + this.detachedLoaderFactory = detachedLoaderFactory; + }) } - /** - * Called by the Router to instantiate a new component during the commit phase of a navigation. - * This method in turn is responsible for calling the `routerOnActivate` hook of its child. - */ - activate(nextInstruction: ComponentInstruction): Promise { - this.log("activate", nextInstruction); - let previousInstruction = this.currentInstruction; - this.currentInstruction = nextInstruction; + deactivate(): void { + if (this.locationStrategy.isPageNavigatingBack()) { + log("PageRouterOutlet.deactivate() while going back - should destroy"); + const popedItem = this.refCache.pop(); + const popedRef = popedItem.componentRef; + + if (this.currnetActivatedComp !== popedRef) { + throw new Error("Current componentRef is different for cached componentRef"); + } + + if (isPresent(this.currnetActivatedComp)) { + this.currnetActivatedComp.destroy(); + this.currnetActivatedComp = null; + } - if (this.location.isPageNavigatingBack()) { - return this.activateOnGoBack(nextInstruction, previousInstruction); + if (isPresent(popedItem.loaderRef)) { + popedItem.loaderRef.destroy(); + } } else { - return this.activateOnGoForward(nextInstruction, previousInstruction); + log("PageRouterOutlet.deactivate() while going foward - do nothing"); } } - private activateOnGoBack(nextInstruction: ComponentInstruction, previousInstruction: ComponentInstruction): Promise { - routerLog("PageRouterOutlet.activate() - Back naviation, so load from cache: " + nextInstruction.componentType.name); - - this.location.finishBackPageNavigation(); + /** + * Called by the Router to instantiate a new component during the commit phase of a navigation. + * This method in turn is responsible for calling the `routerOnActivate` hook of its child. + */ + activate( + factory: ComponentFactory, + activatedRoute: ActivatedRoute, + providers: ResolvedReflectiveProvider[], + outletMap: RouterOutletMap): void { - // Get Component form ref and just call the activate hook - let cacheItem = this.refCache.peek(); - this.componentRef = cacheItem.componentRef; - this.replaceChildRouter(cacheItem.router); + this.outletMap = outletMap; + this.currentActivatedRoute = activatedRoute; - if (hasLifecycleHook(routerHooks.routerOnActivate, this.componentRef.componentType)) { - return (this.componentRef.instance) - .routerOnActivate(nextInstruction, previousInstruction); + if (this.locationStrategy.isPageNavigatingBack()) { + this.activateOnGoBack(factory, activatedRoute, providers); + } else { + this.activateOnGoForward(factory, activatedRoute, providers); } } - private activateOnGoForward(nextInstruction: ComponentInstruction, previousInstruction: ComponentInstruction): Promise { - let componentType = nextInstruction.componentType; - let resultPromise: Promise; - let loaderRef: ComponentRef = undefined; - const childRouter = this.parentRouter.childRouter(componentType); - - const providersArray = [ - provide(RouteData, { useValue: nextInstruction.routeData }), - provide(RouteParams, { useValue: new RouteParams(nextInstruction.params) }), - provide(Router, { useValue: childRouter }), - ]; + private activateOnGoForward( + factory: ComponentFactory, + activatedRoute: ActivatedRoute, + providers: ResolvedReflectiveProvider[]): void { if (this.isInitalPage) { - routerLog("PageRouterOutlet.activate() inital page - just load component: " + componentType.name); + log("PageRouterOutlet.activate() inital page - just load component: " + activatedRoute.component); this.isInitalPage = false; - resultPromise = this.compiler.resolveComponent(componentType).then((componentFactory) => { - const childInjector = ReflectiveInjector.resolveAndCreate(providersArray, this.containerRef.parentInjector); - return this.containerRef.createComponent(componentFactory, this.containerRef.length, childInjector, null); - }); + const inj = ReflectiveInjector.fromResolvedProviders(providers, this.containerRef.parentInjector); + this.currnetActivatedComp = this.containerRef.createComponent(factory, this.containerRef.length, inj, []); + this.refCache.push(this.currnetActivatedComp, null); + } else { - routerLog("PageRouterOutlet.activate() forward navigation - create detached loader in the loader container: " + componentType.name); + log("PageRouterOutlet.activate() forward navigation - create detached loader in the loader container: " + activatedRoute.component); const page = new Page(); - providersArray.push(provide(Page, { useValue: page })); - const childInjector = ReflectiveInjector.resolveAndCreate(providersArray, this.containerRef.parentInjector); - - resultPromise = this.compiler.resolveComponent(DetachedLoader).then((componentFactory) => { - loaderRef = this.childContainerRef.createComponent(componentFactory, this.childContainerRef.length, childInjector, null); + const pageResolvedProvider = ReflectiveInjector.resolve([provide(Page, { useValue: page })]) + const childInjector = ReflectiveInjector.fromResolvedProviders([...providers, ...pageResolvedProvider], this.containerRef.parentInjector); + const loaderRef = this.containerRef.createComponent(this.detachedLoaderFactory, this.containerRef.length, childInjector, []); - return (loaderRef.instance).loadComponent(componentType) - .then((actualCoponenetRef) => { - return this.loadComponentInPage(page, actualCoponenetRef); - }); - }); + this.currnetActivatedComp = loaderRef.instance.loadWithFactory(factory); + this.loadComponentInPage(page, this.currnetActivatedComp); + this.refCache.push(this.currnetActivatedComp, loaderRef); } + } - return resultPromise.then((componentRef) => { - this.componentRef = componentRef; - this.refCache.push(componentRef, childRouter, loaderRef); + private activateOnGoBack(factory: ComponentFactory, + activatedRoute: ActivatedRoute, + providers: ResolvedReflectiveProvider[]): void { + log("PageRouterOutlet.activate() - Back naviation, so load from cache: " + activatedRoute.component); - if (hasLifecycleHook(routerHooks.routerOnActivate, componentType)) { - return (this.componentRef.instance) - .routerOnActivate(nextInstruction, previousInstruction); - } - }); - } + this.locationStrategy.finishBackPageNavigation(); + let cacheItem = this.refCache.peek(); + this.currnetActivatedComp = cacheItem.componentRef; + } - private loadComponentInPage(page: Page, componentRef: ComponentRef): Promise> { + private loadComponentInPage(page: Page, componentRef: ComponentRef): void { //Component loaded. Find its root native view. const componentView = componentRef.location.nativeElement; //Remove it from original native parent. @@ -177,140 +175,25 @@ export class PageRouterOutlet extends RouterOutlet { //Add it to the new page page.content = componentView; - this.location.navigateToNewPage(); - return new Promise((resolve, reject) => { - page.on('navigatingTo', () => { - // Finish activation when page navigation has started - resolve(componentRef) - }); - - page.on('navigatedFrom', (global).Zone.current.wrap((args: NavigatedData) => { - if (args.isBackNavigation) { - this.location.beginBackPageNavigation(); - this.location.back(); - } - })); - - topmost().navigate({ - animated: true, - create: () => { return page; } - }); - }); - } - - /** - * Called by the {@link Router} when an outlet disposes of a component's contents. - * This method in turn is responsible for calling the `routerOnDeactivate` hook of its child. - */ - deactivate(nextInstruction: ComponentInstruction): Promise { - this.log("deactivate", nextInstruction); - var instruction = this.currentInstruction; - - var next = _resolveToTrue; - if (isPresent(this.componentRef) && - isPresent(instruction) && - hasLifecycleHook(routerHooks.routerOnDeactivate, this.componentRef.componentType)) { - next = PromiseWrapper.resolve( - (this.componentRef.instance).routerOnDeactivate(nextInstruction, this.currentInstruction)); - } - - if (this.location.isPageNavigatingBack()) { - routerLog("PageRouterOutlet.deactivate() while going back - should destroy: " + instruction.componentType.name) - return next.then((_) => { - const popedItem = this.refCache.pop(); - const popedRef = popedItem.componentRef; - - if (this.componentRef !== popedRef) { - throw new Error("Current componentRef is different for cached componentRef"); - } - - if (isPresent(this.componentRef)) { - this.componentRef.destroy(); - this.componentRef = null; - } - - if (isPresent(popedItem.loaderRef)) { - popedItem.loaderRef.destroy(); - } - }); - } else { - return next; - } - } - - /** - * Called by the {@link Router} during recognition phase of a navigation. - * PageRouterOutlet will aways return true as cancelling navigation - * is currently not supported in NativeScript. - */ - routerCanDeactivate(nextInstruction: ComponentInstruction): Promise { - this.log("routerCanDeactivate", nextInstruction); + this.locationStrategy.navigateToNewPage(); - return _resolveToTrue; - } - - /** - * Called by the {@link Router} during recognition phase of a navigation. - * - * If the new child component has a different Type than the existing child component, - * this will resolve to `false`. You can't reuse an old component when the new component - * is of a different Type. - * - * Otherwise, this method delegates to the child component's `routerCanReuse` hook if it exists, - * or resolves to true if the hook is not present and params are equal. - */ - routerCanReuse(nextInstruction: ComponentInstruction): Promise { - this.log("routerCanReuse", nextInstruction); - - var result; - - if (isBlank(this.currentInstruction) || this.currentInstruction.componentType != nextInstruction.componentType) { - result = false; - } else if (hasLifecycleHook(routerHooks.routerCanReuse, this.currentInstruction.componentType)) { - result = (this.componentRef.instance) - .routerCanReuse(nextInstruction, this.currentInstruction); - } else { - result = nextInstruction == this.currentInstruction || - (isPresent(nextInstruction.params) && isPresent(this.currentInstruction.params) && - StringMapWrapper.equals(nextInstruction.params, this.currentInstruction.params)); - } + page.on('navigatedFrom', (global).Zone.current.wrap((args: NavigatedData) => { + if (args.isBackNavigation) { + this.locationStrategy.beginBackPageNavigation(); + this.locationStrategy.back(); + } + })); - routerLog("PageRouterOutlet.routerCanReuse(): " + result); - return PromiseWrapper.resolve(result); + topmost().navigate({ + animated: true, + create: () => { return page; } + }); } +} - /** - * Called by the {@link Router} during recognition phase of a navigation. - * - * If this resolves to `false`, the given navigation is cancelled. - * - * This method delegates to the child component's `routerCanDeactivate` hook if it exists, - * and otherwise resolves to true. - */ - reuse(nextInstruction: ComponentInstruction): Promise { - var previousInstruction = this.currentInstruction; - this.currentInstruction = nextInstruction; - - if (isBlank(this.componentRef)) { - throw new Error(`Cannot reuse an outlet that does not contain a component.`); - } - - return PromiseWrapper.resolve( - hasLifecycleHook(routerHooks.routerOnReuse, this.componentRef.componentType) ? - (this.componentRef.instance).routerOnReuse(nextInstruction, previousInstruction) : true); - } +function log(msg: string) { + routerLog(msg); +} - private replaceChildRouter(childRouter: Router) { - // HACKY HACKY HACKY - // When navigationg back - we need to set the child router of - // our router - with the one we have created for the previosus page. - // Otherwise router-outlets inside that page wont't work. - // Curretly there is no other way to do that (parentRouter.childRouter() will create ne router). - this.parentRouter["_childRouter"] = childRouter; - } - private log(method: string, nextInstruction: ComponentInstruction) { - routerLog("PageRouterOutlet." + method + " isBack: " + this.location.isPageNavigatingBack() + " nextUrl: " + nextInstruction.urlPath); - } -} diff --git a/ng-sample/.vscode/launch.json b/ng-sample/.vscode/launch.json index 9699c5943..2e88384e5 100644 --- a/ng-sample/.vscode/launch.json +++ b/ng-sample/.vscode/launch.json @@ -1,85 +1,85 @@ { - "version": "0.2.0", - "configurations": [ - { - "name": "Launch on iOS Device", - "type": "nativescript", - "platform": "ios", - "request": "launch", - "appRoot": "${workspaceRoot}", - "sourceMaps": true, - "diagnosticLogging": false, - "emulator": false - }, - { - "name": "Attach on iOS Device", - "type": "nativescript", - "platform": "ios", - "request": "attach", - "appRoot": "${workspaceRoot}", - "sourceMaps": true, - "diagnosticLogging": false, - "emulator": false - }, - { - "name": "Launch on iOS Emulator", - "type": "nativescript", - "platform": "ios", - "request": "launch", - "appRoot": "${workspaceRoot}", - "sourceMaps": true, - "diagnosticLogging": false, - "emulator": true - }, - { - "name": "Attach on iOS Emulator", - "type": "nativescript", - "platform": "ios", - "request": "attach", - "appRoot": "${workspaceRoot}", - "sourceMaps": true, - "diagnosticLogging": false, - "emulator": true - }, - { - "name": "Launch on Android Device", - "type": "nativescript", - "platform": "android", - "request": "launch", - "appRoot": "${workspaceRoot}", - "sourceMaps": true, - "diagnosticLogging": false, - "emulator": false - }, - { - "name": "Launch on Android Emulator", - "type": "nativescript", - "platform": "android", - "request": "launch", - "appRoot": "${workspaceRoot}", - "sourceMaps": true, - "diagnosticLogging": false, - "emulator": true - }, - { - "name": "Attach on Android Device", - "type": "nativescript", - "platform": "android", - "request": "attach", - "appRoot": "${workspaceRoot}", - "sourceMaps": false, - "diagnosticLogging": false, - "emulator": false - }, - { - "name": "Attach on Android Emulator", - "type": "nativescript", - "platform": "android", - "request": "attach", - "appRoot": "${workspaceRoot}", - "sourceMaps": false, - "diagnosticLogging": false, - "emulator": true - } - ] + "version": "0.2.0", + "configurations": [ + { + "name": "Launch on iOS Device", + "type": "nativescript", + "platform": "ios", + "request": "launch", + "appRoot": "${workspaceRoot}", + "sourceMaps": true, + "diagnosticLogging": false, + "emulator": false + }, + { + "name": "Attach on iOS Device", + "type": "nativescript", + "platform": "ios", + "request": "attach", + "appRoot": "${workspaceRoot}", + "sourceMaps": true, + "diagnosticLogging": false, + "emulator": false + }, + { + "name": "Launch on iOS Emulator", + "type": "nativescript", + "platform": "ios", + "request": "launch", + "appRoot": "${workspaceRoot}", + "sourceMaps": true, + "diagnosticLogging": false, + "emulator": true + }, + { + "name": "Attach on iOS Emulator", + "type": "nativescript", + "platform": "ios", + "request": "attach", + "appRoot": "${workspaceRoot}", + "sourceMaps": true, + "diagnosticLogging": false, + "emulator": true + }, + { + "name": "Launch on Android Device", + "type": "nativescript", + "platform": "android", + "request": "launch", + "appRoot": "${workspaceRoot}", + "sourceMaps": true, + "diagnosticLogging": false, + "emulator": false + }, + { + "name": "Launch on Android Emulator", + "type": "nativescript", + "platform": "android", + "request": "launch", + "appRoot": "${workspaceRoot}", + "sourceMaps": true, + "diagnosticLogging": false, + "emulator": true + }, + { + "name": "Attach on Android Device", + "type": "nativescript", + "platform": "android", + "request": "attach", + "appRoot": "${workspaceRoot}", + "sourceMaps": false, + "diagnosticLogging": false, + "emulator": false + }, + { + "name": "Attach on Android Emulator", + "type": "nativescript", + "platform": "android", + "request": "attach", + "appRoot": "${workspaceRoot}", + "sourceMaps": false, + "diagnosticLogging": false, + "emulator": true + } + ] } \ No newline at end of file diff --git a/ng-sample/app/app.ts b/ng-sample/app/app.ts index a2c35326d..096be6a84 100644 --- a/ng-sample/app/app.ts +++ b/ng-sample/app/app.ts @@ -7,13 +7,14 @@ // this import should be first in order to load some required settings (like globals and reflect-metadata) import { nativeScriptBootstrap } from "nativescript-angular/application"; +import { NS_ROUTER_PROVIDERS as NS_ROUTER_PROVIDERS_DEPRECATED } from "nativescript-angular/router-deprecated"; import { NS_ROUTER_PROVIDERS } from "nativescript-angular/router"; import { rendererTraceCategory, routerTraceCategory, listViewTraceCategory } from "nativescript-angular/trace"; import trace = require("trace"); // trace.setCategories(rendererTraceCategory); -// trace.setCategories(routerTraceCategory); -trace.setCategories(listViewTraceCategory); +trace.setCategories(routerTraceCategory); +// trace.setCategories(listViewTraceCategory); trace.enable(); import {RendererTest} from './examples/renderer-test'; @@ -22,24 +23,37 @@ import {Benchmark} from './performance/benchmark'; import {ListTest} from './examples/list/list-test'; import {ListTestAsync, ListTestFilterAsync} from "./examples/list/list-test-async"; import {ImageTest} from "./examples/image/image-test"; -import {NavigationTest} from "./examples/navigation/navigation-test"; import {ActionBarTest} from "./examples/action-bar/action-bar-test"; import {ModalTest} from "./examples/modal/modal-test"; import {PlatfromDirectivesTest} from "./examples/platform-directives/platform-directives-test"; -import {RouterOutletTest} from "./examples/navigation/router-outlet-test"; -import {LoginTest} from "./examples/navigation/login-test"; + +// router-deprecated +import {NavigationTest} from "./examples/router-deprecated/navigation-test"; +import {RouterOutletTest} from "./examples/router-deprecated/router-outlet-test"; +import {LoginTest} from "./examples/router-deprecated/login-test"; + +// new router +import { RouterOutletAppComponent, RouterOutletRouterProviders} from "./examples/router/router-outlet-test" +import { PageRouterOutletAppComponent, PageRouterOutletRouterProviders } from "./examples/router/page-router-outlet-test" +import { PageRouterOutletNestedAppComponent, PageRouterOutletNestedRouterProviders } from "./examples/router/page-router-outlet-nested-test" //nativeScriptBootstrap(RendererTest); //nativeScriptBootstrap(TabViewTest); //nativeScriptBootstrap(Benchmark); // nativeScriptBootstrap(ListTest); // nativeScriptBootstrap(ListTestAsync); -nativeScriptBootstrap(ListTestFilterAsync); //nativeScriptBootstrap(ImageTest); -//nativeScriptBootstrap(NavigationTest, [NS_ROUTER_PROVIDERS]); -//nativeScriptBootstrap(ActionBarTest, [NS_ROUTER_PROVIDERS], { startPageActionBarHidden: false }); -//nativeScriptBootstrap(ActionBarTest, [NS_ROUTER_PROVIDERS]); +//nativeScriptBootstrap(ActionBarTest, [NS_ROUTER_PROVIDERS_DEPRECATED], { startPageActionBarHidden: false }); +//nativeScriptBootstrap(ActionBarTest, [NS_ROUTER_PROVIDERS_DEPRECATED]); //nativeScriptBootstrap(ModalTest); //nativeScriptBootstrap(PlatfromDirectivesTest); -//nativeScriptBootstrap(RouterOutletTest, [NS_ROUTER_PROVIDERS]); -// nativeScriptBootstrap(LoginTest, [NS_ROUTER_PROVIDERS]); + +// new router +// nativeScriptBootstrap(RouterOutletAppComponent, [RouterOutletRouterProviders]); +// nativeScriptBootstrap(PageRouterOutletAppComponent, [PageRouterOutletRouterProviders]); +nativeScriptBootstrap(PageRouterOutletNestedAppComponent, [PageRouterOutletNestedRouterProviders]); + +// router-deprecated +// nativeScriptBootstrap(NavigationTest, [NS_ROUTER_PROVIDERS_DEPRECATED]); +// nativeScriptBootstrap(RouterOutletTest, [NS_ROUTER_PROVIDERS_DEPRECATED]); +// nativeScriptBootstrap(LoginTest, [NS_ROUTER_PROVIDERS_DEPRECATED]); diff --git a/ng-sample/app/examples/navigation/login-test.ts b/ng-sample/app/examples/router-deprecated/login-test.ts similarity index 100% rename from ng-sample/app/examples/navigation/login-test.ts rename to ng-sample/app/examples/router-deprecated/login-test.ts diff --git a/ng-sample/app/examples/navigation/nav-component.ts b/ng-sample/app/examples/router-deprecated/nav-component.ts similarity index 100% rename from ng-sample/app/examples/navigation/nav-component.ts rename to ng-sample/app/examples/router-deprecated/nav-component.ts diff --git a/ng-sample/app/examples/navigation/navigation-test.ts b/ng-sample/app/examples/router-deprecated/navigation-test.ts similarity index 100% rename from ng-sample/app/examples/navigation/navigation-test.ts rename to ng-sample/app/examples/router-deprecated/navigation-test.ts diff --git a/ng-sample/app/examples/navigation/router-outlet-test.css b/ng-sample/app/examples/router-deprecated/router-outlet-test.css similarity index 100% rename from ng-sample/app/examples/navigation/router-outlet-test.css rename to ng-sample/app/examples/router-deprecated/router-outlet-test.css diff --git a/ng-sample/app/examples/navigation/router-outlet-test.ts b/ng-sample/app/examples/router-deprecated/router-outlet-test.ts similarity index 100% rename from ng-sample/app/examples/navigation/router-outlet-test.ts rename to ng-sample/app/examples/router-deprecated/router-outlet-test.ts diff --git a/ng-sample/app/examples/router/page-router-outlet-nested-test.ts b/ng-sample/app/examples/router/page-router-outlet-nested-test.ts new file mode 100644 index 000000000..66fe01318 --- /dev/null +++ b/ng-sample/app/examples/router/page-router-outlet-nested-test.ts @@ -0,0 +1,149 @@ +import { Component, OnInit, OnDestroy } from "@angular/core"; +import { RouterConfig, ActivatedRoute, Router, ROUTER_DIRECTIVES, Event } from '@angular/router'; +import { Observable } from "rxjs"; +import { NS_ROUTER_DIRECTIVES, nsProvideRouter} from "nativescript-angular/router" +import { Location, LocationStrategy} from '@angular/common'; +import { Page } from "ui/page"; + + +@Component({ + selector: "first", + styleUrls: ["examples/router/styles.css"], + directives: [ROUTER_DIRECTIVES, NS_ROUTER_DIRECTIVES], + template: ` + + + + + + + ` +}) +class FirstComponent implements OnInit, OnDestroy { + constructor(page: Page) { + console.log("FirstComponent.constructor() page: " + page); + } + + ngOnInit() { + console.log("FirstComponent - ngOnInit()"); + } + + ngOnDestroy() { + console.log("FirstComponent - ngOnDestroy()"); + } +} + + + +@Component({ + selector: 'master', + styleUrls: ["examples/router/styles.css"], + directives: [NS_ROUTER_DIRECTIVES], + template: ` + + + + + + ` +}) +class MasterComponent { + public details: Array = [1, 2, 3]; + + constructor(private router: Router, private route: ActivatedRoute) { + console.log("MasterComponent.constructor()"); + } +} + +@Component({ + selector: 'detail', + styleUrls: ["examples/router/styles.css"], + directives: [NS_ROUTER_DIRECTIVES], + template: ` + + + + + + + + ` +}) +class DetailComponent { + public id$: Observable; + constructor(private router: Router, private route: ActivatedRoute) { + console.log("DetailComponent.constructor()"); + this.id$ = route.params.map(r => r["id"]); + } +} + +@Component({ + selector: "second", + styleUrls: ["examples/router/styles.css"], + directives: [ROUTER_DIRECTIVES, NS_ROUTER_DIRECTIVES], + template: ` + + + + + + + + + + + ` +}) +class SecondComponent implements OnInit, OnDestroy { + public depth$: Observable; + public nextDepth$: Observable; + constructor(private location: Location, route: ActivatedRoute, page: Page) { + console.log("SecondComponent.constructor() page: " + page); + this.depth$ = route.params.map(r => r["depth"]); + this.nextDepth$ = route.params.map(r => +r["depth"] + 1); + } + + ngOnInit() { + console.log("SecondComponent - ngOnInit()"); + } + + ngOnDestroy() { + console.log("SecondComponent - ngOnDestroy()"); + } + + goBack() { + this.location.back(); + } +} + + +@Component({ + selector: 'navigation-test', + directives: [ROUTER_DIRECTIVES, NS_ROUTER_DIRECTIVES], + template: `` +}) +export class PageRouterOutletNestedAppComponent { + constructor(router: Router, private location: Location) { + router.events.subscribe((e) => { + console.log("--EVENT-->: " + e.toString()); + }) + } +} + + +const routes: RouterConfig = [ + { path: "/first", component: FirstComponent }, + { path: "/", redirectTo: "/first", terminal: true }, + { + path: "/second/:depth", component: SecondComponent, + children: [ + { path: "/", redirectTo: "master", terminal: true }, + { path: "/master", component: MasterComponent }, + { path: "/detail/:id", component: DetailComponent } + ] + }, +]; + +export const PageRouterOutletNestedRouterProviders = [ + nsProvideRouter(routes, { enableTracing: false }) +]; \ No newline at end of file diff --git a/ng-sample/app/examples/router/page-router-outlet-test.ts b/ng-sample/app/examples/router/page-router-outlet-test.ts new file mode 100644 index 000000000..493baf430 --- /dev/null +++ b/ng-sample/app/examples/router/page-router-outlet-test.ts @@ -0,0 +1,135 @@ +import { Component, OnInit, OnDestroy } from "@angular/core"; +import { RouterConfig, ActivatedRoute, Router, ROUTER_DIRECTIVES, Event } from '@angular/router'; +import { Observable } from "rxjs"; +import { NS_ROUTER_DIRECTIVES, nsProvideRouter} from "nativescript-angular/router" +import { Location, LocationStrategy} from '@angular/common'; +import { Page } from "ui/page"; + + +@Component({ + selector: "first", + styleUrls: ["examples/router/styles.css"], + directives: [ROUTER_DIRECTIVES, NS_ROUTER_DIRECTIVES], + template: ` + + + + + + + + ` +}) +class FirstComponent implements OnInit, OnDestroy { + constructor(page: Page) { + console.log("FirstComponent.constructor() page: " + page); + } + + ngOnInit() { + console.log("FirstComponent - ngOnInit()"); + } + + ngOnDestroy() { + console.log("FirstComponent - ngOnDestroy()"); + } +} + +@Component({ + selector: "second", + styleUrls: ["examples/router/styles.css"], + directives: [ROUTER_DIRECTIVES, NS_ROUTER_DIRECTIVES], + template: ` + + + + + + + + + ` +}) +class SecondComponent implements OnInit, OnDestroy { + public id: Observable; + constructor(private location: Location, route: ActivatedRoute, page: Page) { + console.log("SecondComponent.constructor() page: " + page); + this.id = route.params.map(r => r["id"]); + } + + ngOnInit() { + console.log("SecondComponent - ngOnInit()"); + } + + ngOnDestroy() { + console.log("SecondComponent - ngOnDestroy()"); + } + + goBack() { + this.location.back(); + } +} + +@Component({ + selector: "third", + styleUrls: ["examples/router/styles.css"], + directives: [ROUTER_DIRECTIVES, NS_ROUTER_DIRECTIVES], + template: ` + + + + + + + + + ` +}) +class ThirdComponent implements OnInit, OnDestroy { + public id: Observable; + constructor(private location: Location, route: ActivatedRoute, page: Page) { + console.log("ThirdComponent.constructor() page: " + page); + this.id = route.params.map(r => r["id"]); + } + + ngOnInit() { + console.log("ThirdComponent - ngOnInit()"); + } + + ngOnDestroy() { + console.log("ThirdComponent - ngOnDestroy()"); + } + + goBack() { + this.location.back(); + } +} + + + + + + +@Component({ + selector: 'navigation-test', + directives: [ROUTER_DIRECTIVES, NS_ROUTER_DIRECTIVES], + template: `` +}) +export class PageRouterOutletAppComponent { + constructor(router: Router, private location: Location) { + router.events.subscribe((e) => { + console.log("--EVENT-->: " + e.toString()); + }) + } +} + + +const routes: RouterConfig = [ + { path: "/first", component: FirstComponent }, + { path: "/", redirectTo: "/first", terminal: true }, + { path: "/second/:id", component: SecondComponent }, + { path: "/third/:id", component: ThirdComponent }, +]; + +export const PageRouterOutletRouterProviders = [ + nsProvideRouter(routes, { enableTracing: false }) +]; \ No newline at end of file diff --git a/ng-sample/app/examples/router/router-outlet-test.ts b/ng-sample/app/examples/router/router-outlet-test.ts new file mode 100644 index 000000000..b90c87542 --- /dev/null +++ b/ng-sample/app/examples/router/router-outlet-test.ts @@ -0,0 +1,74 @@ +import { Component, OnInit, OnDestroy } from "@angular/core"; +import { RouterConfig, ActivatedRoute, ROUTER_DIRECTIVES } from '@angular/router'; +import { NS_ROUTER_DIRECTIVES, nsProvideRouter} from "nativescript-angular/router" + +@Component({ + selector: "first", + styleUrls: ["examples/router/styles.css"], + template: ` + + + ` +}) +class FirstComponent implements OnInit, OnDestroy { + ngOnInit() { + console.log("FirstComponent - ngOnInit()"); + } + + ngOnDestroy() { + console.log("FirstComponent - ngOnDestroy()"); + } +} + +@Component({ + selector: "second", + styleUrls: ["examples/router/styles.css"], + template: ` + + + ` +}) +class SecondComponent implements OnInit, OnDestroy { + id; + constructor(route: ActivatedRoute) { + this.id = route.params.map(r => r["id"]); + } + + ngOnInit() { + console.log("SecondComponent - ngOnInit()"); + } + + ngOnDestroy() { + console.log("SecondComponent - ngOnDestroy()"); + } +} + +@Component({ + selector: 'navigation-test', + directives: [ROUTER_DIRECTIVES, NS_ROUTER_DIRECTIVES], + styleUrls: ["examples/router/styles.css"], + template: ` + + + + + + + + + + ` +}) +export class RouterOutletAppComponent { +} + + +const routes: RouterConfig = [ + { path: "/first", component: FirstComponent}, + { path: "/", redirectTo: "/first", terminal: true }, + { path: "/second/:id", component: SecondComponent }, +]; + +export const RouterOutletRouterProviders = [ + nsProvideRouter(routes, { enableTracing: false }) +]; \ No newline at end of file diff --git a/ng-sample/app/examples/router/styles.css b/ng-sample/app/examples/router/styles.css new file mode 100644 index 000000000..3df96a326 --- /dev/null +++ b/ng-sample/app/examples/router/styles.css @@ -0,0 +1,50 @@ +.title { + font-size: 30; + margin: 16; + color: darkslategray; +} + +.subtitle { + font-size: 20; + horizontal-align: center; + margin: 10; +} + +.nav { + orientation: horizontal; + horizontal-align: center; + padding: 4; + background-color: lightblue; +} + +.link { + margin: 10 30; + horizontal-align: center; +} + +.router-link-active { + background-color: lightcoral; +} + +button { + horizontal-align: left; + margin: 0 6; +} + +.master { + margin: 10; + background-color: lightgreen; +} + +.master button { + horizontal-align: center; +} + +.detail { + margin: 10; + background-color: lightgreen; +} + +.detail button { + horizontal-align: center; +} \ No newline at end of file diff --git a/ng-sample/package.json b/ng-sample/package.json index af4278fa3..b2c289a41 100644 --- a/ng-sample/package.json +++ b/ng-sample/package.json @@ -23,13 +23,9 @@ }, "homepage": "https://github.com/NativeScript/template-hello-world", "dependencies": { - "tns-core-modules": ">=2.0.0 || >=2.1.0-2016 || 2.0.0-angular-7", - "nativescript-angular": "^0.1.6" - }, - "devDependencies": { - "nativescript-dev-typescript": "^0.3.1", - "nativescript-dev-webpack": "0.0.13", - "typescript": "^1.8.10", + "tns-core-modules": ">=2.0.0 || >=2.1.0-2016 || >=2.0.0-angular-7", + "nativescript-angular": "^0.1.6", + "nativescript-intl": "^0.0.2", "@angular/common": "2.0.0-rc.2", "@angular/compiler": "2.0.0-rc.2", "@angular/core": "2.0.0-rc.2", @@ -37,6 +33,7 @@ "@angular/platform-browser-dynamic": "2.0.0-rc.2", "@angular/platform-server": "2.0.0-rc.2", "@angular/router-deprecated": "2.0.0-rc.2", + "@angular/router": "3.0.0-alpha.6", "rxjs": "5.0.0-beta.6", "zone.js": "^0.6.12", "reflect-metadata": "^0.1.3", @@ -46,6 +43,16 @@ "url": "0.10.3", "shelljs": "^0.7.0" }, + "devDependencies": { + "babel-traverse": "6.9.0", + "babel-types": "6.10.0", + "babylon": "6.8.1", + "filewalker": "0.1.2", + "lazy": "1.0.11", + "nativescript-dev-typescript": "^0.3.1", + "nativescript-dev-webpack": "0.0.13", + "typescript": "^1.8.10" + }, "nativescript": { "id": "org.nativescript.ngsample", "tns-android": { From dfb90c56b81af732e5420eb8cae71a7be5d6d2b1 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Mon, 20 Jun 2016 13:08:40 +0300 Subject: [PATCH 2/3] fix E2E tests --- tests/app/main.ts | 2 +- tests/app/multi-page-main.component.ts | 2 +- tests/package.json | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/app/main.ts b/tests/app/main.ts index 80353f1f6..bc965dff5 100644 --- a/tests/app/main.ts +++ b/tests/app/main.ts @@ -4,7 +4,7 @@ import {AppComponent} from "./app.component"; import {GestureComponent} from "./snippets/gestures.component"; import {LayoutsComponent} from "./snippets/layouts.component"; import {IconFontComponent} from "./snippets/icon-font.component"; -import {NS_ROUTER_DIRECTIVES, NS_ROUTER_PROVIDERS} from "nativescript-angular/router/ns-router"; +import {NS_ROUTER_DIRECTIVES, NS_ROUTER_PROVIDERS} from "nativescript-angular/router-deprecated"; import {APP_ROOT_VIEW} from "nativescript-angular/platform-providers"; import {Page} from "ui/page"; import {Label} from "ui/label"; diff --git a/tests/app/multi-page-main.component.ts b/tests/app/multi-page-main.component.ts index 1d8e1bfa2..c9a474262 100644 --- a/tests/app/multi-page-main.component.ts +++ b/tests/app/multi-page-main.component.ts @@ -1,6 +1,6 @@ import {ROUTER_DIRECTIVES, Router, OnActivate, OnDeactivate, CanReuse, OnReuse, RouteParams, ComponentInstruction, RouteConfig } from '@angular/router-deprecated'; -import {NS_ROUTER_DIRECTIVES, NS_ROUTER_PROVIDERS} from "nativescript-angular/router/ns-router"; +import {NS_ROUTER_DIRECTIVES, NS_ROUTER_PROVIDERS} from "nativescript-angular/router-deprecated"; import {Component, ElementRef} from "@angular/core"; import {Location, LocationStrategy} from '@angular/common'; import {FirstComponent} from "./first.component"; diff --git a/tests/package.json b/tests/package.json index 946b17bf1..0e4cab595 100644 --- a/tests/package.json +++ b/tests/package.json @@ -36,6 +36,7 @@ "@angular/platform-browser-dynamic": "2.0.0-rc.2", "@angular/platform-server": "2.0.0-rc.2", "@angular/router-deprecated": "2.0.0-rc.2", + "@angular/router": "3.0.0-alpha.6", "rxjs": "5.0.0-beta.6", "zone.js": "^0.6.12", "reflect-metadata": "^0.1.3", From f285889eef3dcbf977257da25d687d6d9cdec633 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Tue, 21 Jun 2016 11:30:03 +0300 Subject: [PATCH 3/3] e2e test updated to use router v.3 --- tests/app/base.component.ts | 22 ++++++++++----------- tests/app/first.component.ts | 20 ++++++++----------- tests/app/main.ts | 19 +++++++++++------- tests/app/multi-page-main.component.ts | 18 ++++++++++------- tests/app/second.component.ts | 26 ++++++++++++------------- tests/app/single-page-main.component.ts | 17 ++++++++++------ tests/e2e-tests/multi-page-routing.js | 11 +++++------ tests/e2e-tests/single-page-routing.js | 15 +++++++------- 8 files changed, 77 insertions(+), 71 deletions(-) diff --git a/tests/app/base.component.ts b/tests/app/base.component.ts index 28cc943ec..6dc8a5cd7 100644 --- a/tests/app/base.component.ts +++ b/tests/app/base.component.ts @@ -1,23 +1,23 @@ -import {ROUTER_DIRECTIVES, Router, OnActivate, OnDeactivate, CanReuse, OnReuse, - RouteParams, ComponentInstruction, RouteConfig } from '@angular/router-deprecated'; -import {Component, OpaqueToken} from "@angular/core"; +import {ROUTER_DIRECTIVES, Router } from '@angular/router'; +import {Component, OpaqueToken, OnInit, OnDestroy} from "@angular/core"; export const HOOKS_LOG = new OpaqueToken("Hooks log"); +import {BehaviorSubject} from "rxjs"; -export class BaseComponent implements OnActivate, OnDeactivate { +export class BaseComponent implements OnInit, OnDestroy { protected name: string = ""; - constructor(protected hooksLog: string[]) { + constructor(protected hooksLog: BehaviorSubject>) { } - routerOnActivate(nextInstruction: ComponentInstruction, prevInstruction: ComponentInstruction): any { - this.log("activate", nextInstruction, prevInstruction); + ngOnInit() { + this.log("init"); } - routerOnDeactivate(nextInstruction: ComponentInstruction, prevInstruction: ComponentInstruction): any { - this.log("deactivate", nextInstruction, prevInstruction); + ngOnDestroy() { + this.log("destroy"); } - private log(method: string, nextInstruction: ComponentInstruction, prevInstruction: ComponentInstruction) { - this.hooksLog.push(this.name + "." + method + " " + nextInstruction.urlPath + " " + (prevInstruction ? prevInstruction.urlPath : null)); + private log(method: string) { + this.hooksLog.next([...this.hooksLog.value, this.name + "." + method]); } } diff --git a/tests/app/first.component.ts b/tests/app/first.component.ts index f1c30a9a1..22953d20a 100644 --- a/tests/app/first.component.ts +++ b/tests/app/first.component.ts @@ -1,7 +1,7 @@ -import {ROUTER_DIRECTIVES, Router, OnActivate, OnDeactivate, CanReuse, OnReuse, - RouteParams, RouteData, ComponentInstruction, RouteConfig } from '@angular/router-deprecated'; +import {ROUTER_DIRECTIVES, Router, ActivatedRoute } from '@angular/router'; import {Component, Inject} from "@angular/core"; import {HOOKS_LOG, BaseComponent} from "./base.component"; +import {BehaviorSubject} from "rxjs"; @Component({ selector: "first-comp", @@ -9,25 +9,21 @@ import {HOOKS_LOG, BaseComponent} from "./base.component"; - + ` }) export class FirstComponent extends BaseComponent { - name = "first"; + protected name = "first"; + public id: string = ""; - constructor(private router: Router, private routeData: RouteData, @Inject(HOOKS_LOG) hooksLog: string[]) { + constructor(private router: Router, private routeData: ActivatedRoute, @Inject(HOOKS_LOG) hooksLog: BehaviorSubject>) { super(hooksLog); + this.id = routeData.snapshot.params["id"]; } - ngOnInit() { - this.id = this.routeData.get('id'); - } - - public id: string = "" - gotoSecond() { - this.router.navigateByUrl("/second"); + this.router.navigateByUrl("/second/" + this.id); } } diff --git a/tests/app/main.ts b/tests/app/main.ts index bc965dff5..d4763d2c2 100644 --- a/tests/app/main.ts +++ b/tests/app/main.ts @@ -4,7 +4,7 @@ import {AppComponent} from "./app.component"; import {GestureComponent} from "./snippets/gestures.component"; import {LayoutsComponent} from "./snippets/layouts.component"; import {IconFontComponent} from "./snippets/icon-font.component"; -import {NS_ROUTER_DIRECTIVES, NS_ROUTER_PROVIDERS} from "nativescript-angular/router-deprecated"; +// import {NS_ROUTER_DIRECTIVES, NS_ROUTER_PROVIDERS} from "nativescript-angular/router-deprecated"; import {APP_ROOT_VIEW} from "nativescript-angular/platform-providers"; import {Page} from "ui/page"; import {Label} from "ui/label"; @@ -12,12 +12,14 @@ import {StackLayout} from "ui/layouts/stack-layout"; import * as application from "application"; //nativeScriptBootstrap(AppComponent, [NS_ROUTER_PROVIDERS]); import {HOOKS_LOG} from "./base.component"; -import {MultiPageMain} from "./multi-page-main.component"; -import {SinglePageMain} from "./single-page-main.component"; +import {MultiPageMain, MultiPageRouterProviders} from "./multi-page-main.component"; +import {SinglePageMain, SinglePageRouterProviders} from "./single-page-main.component"; import {provide, OpaqueToken} from "@angular/core"; import { rendererTraceCategory, routerTraceCategory } from "nativescript-angular/trace"; +import {BehaviorSubject} from "rxjs"; + import trace = require("trace"); //trace.setCategories(rendererTraceCategory + "," + routerTraceCategory); trace.enable(); @@ -40,13 +42,16 @@ application.start({ //profiling.start('ng-bootstrap'); console.log('BOOTSTRAPPING TEST APPS...'); //bootstrap(MultiPageMain, [NS_ROUTER_PROVIDERS]); + const rootViewProvider = provide(APP_ROOT_VIEW, { useValue: root }); - let singlePageHooksLog = [] + + let singlePageHooksLog = new BehaviorSubject([]); const singlePageHooksLogProvider = provide(HOOKS_LOG, { useValue: singlePageHooksLog }); - bootstrap(SinglePageMain, [rootViewProvider, singlePageHooksLogProvider, NS_ROUTER_PROVIDERS]); - let multiPageHooksLog = [] + bootstrap(SinglePageMain, [rootViewProvider, singlePageHooksLogProvider, SinglePageRouterProviders]); + + let multiPageHooksLog = new BehaviorSubject([]); const multiPageHooksLogProvider = provide(HOOKS_LOG, { useValue: multiPageHooksLog }); - bootstrap(MultiPageMain, [rootViewProvider, multiPageHooksLogProvider, NS_ROUTER_PROVIDERS]); + bootstrap(MultiPageMain, [rootViewProvider, multiPageHooksLogProvider, MultiPageRouterProviders]); } page.on('loaded', onLoadedHandler); diff --git a/tests/app/multi-page-main.component.ts b/tests/app/multi-page-main.component.ts index c9a474262..48ae5fefb 100644 --- a/tests/app/multi-page-main.component.ts +++ b/tests/app/multi-page-main.component.ts @@ -1,6 +1,5 @@ -import {ROUTER_DIRECTIVES, Router, OnActivate, OnDeactivate, CanReuse, OnReuse, - RouteParams, ComponentInstruction, RouteConfig } from '@angular/router-deprecated'; -import {NS_ROUTER_DIRECTIVES, NS_ROUTER_PROVIDERS} from "nativescript-angular/router-deprecated"; +import {ROUTER_DIRECTIVES, Router, RouterConfig } from '@angular/router'; +import {nsProvideRouter, NS_ROUTER_DIRECTIVES} from 'nativescript-angular/router'; import {Component, ElementRef} from "@angular/core"; import {Location, LocationStrategy} from '@angular/common'; import {FirstComponent} from "./first.component"; @@ -15,10 +14,6 @@ import {SecondComponent} from "./second.component"; ` }) -@RouteConfig([ - { path: '/first', name: 'First', component: FirstComponent, useAsDefault: true , data: {id: "multi-page"}}, - { path: '/second', name: 'Second', component: SecondComponent, data: {id: "multi-page"} } -]) export class MultiPageMain { constructor( public elementRef: ElementRef, @@ -26,3 +21,12 @@ export class MultiPageMain { public location: LocationStrategy) { } } + +const routes: RouterConfig = [ + { path: "/first/:id", component: FirstComponent }, + { path: "/", redirectTo: "/first/multi-page", terminal: true }, + { path: "/second/:id", component: SecondComponent }, +]; +export const MultiPageRouterProviders = [ + nsProvideRouter(routes, { enableTracing: false }) +]; \ No newline at end of file diff --git a/tests/app/second.component.ts b/tests/app/second.component.ts index 56a7e17db..1761e4f3f 100644 --- a/tests/app/second.component.ts +++ b/tests/app/second.component.ts @@ -1,32 +1,30 @@ -import {ROUTER_DIRECTIVES, Router, OnActivate, OnDeactivate, CanReuse, OnReuse, - RouteParams, RouteData, ComponentInstruction, RouteConfig } from '@angular/router-deprecated'; +import {ROUTER_DIRECTIVES, Router, ActivatedRoute } from '@angular/router'; import {Component, Inject} from "@angular/core"; +import {Location} from "@angular/common"; import {HOOKS_LOG, BaseComponent} from "./base.component"; +import {BehaviorSubject} from "rxjs"; @Component({ selector: "second-comp", template: ` - - + + ` }) export class SecondComponent extends BaseComponent { - constructor(private router: Router, private routeData: RouteData, @Inject(HOOKS_LOG) hooksLog: string[]) { - super(hooksLog); - } + protected name = "second"; + public id: string = "" - ngOnInit() { - this.id = this.routeData.get('id'); + constructor(private router: Router, private location: Location, private routeData: ActivatedRoute, @Inject(HOOKS_LOG) hooksLog: BehaviorSubject>) { + super(hooksLog); + this.id = routeData.snapshot.params["id"]; } - public id: string = "" - name = "second"; - - gotoFirst() { - this.router.navigateByUrl("/first"); + goBack() { + this.location.back(); } } diff --git a/tests/app/single-page-main.component.ts b/tests/app/single-page-main.component.ts index 4fcf18357..8f1024025 100644 --- a/tests/app/single-page-main.component.ts +++ b/tests/app/single-page-main.component.ts @@ -1,5 +1,5 @@ -import {ROUTER_DIRECTIVES, Router, OnActivate, OnDeactivate, CanReuse, OnReuse, - RouteParams, ComponentInstruction, RouteConfig } from '@angular/router-deprecated'; +import {ROUTER_DIRECTIVES, Router, RouterConfig } from '@angular/router'; +import {nsProvideRouter} from 'nativescript-angular/router'; import {Component, ElementRef} from "@angular/core"; import {Location, LocationStrategy} from '@angular/common'; import {FirstComponent} from "./first.component"; @@ -14,10 +14,6 @@ import {SecondComponent} from "./second.component"; ` }) -@RouteConfig([ - { path: '/first', name: 'First', component: FirstComponent, useAsDefault: true , data: {id: "single-page"}}, - { path: '/second', name: 'Second', component: SecondComponent, data: {id: "single-page"} } -]) export class SinglePageMain { constructor( public elementRef: ElementRef, @@ -25,3 +21,12 @@ export class SinglePageMain { public location: LocationStrategy) { } } + +const routes: RouterConfig = [ + { path: "/first/:id", component: FirstComponent }, + { path: "/", redirectTo: "/first/single-page", terminal: true }, + { path: "/second/:id", component: SecondComponent }, +]; +export const SinglePageRouterProviders = [ + nsProvideRouter(routes, { enableTracing: false }) +]; \ No newline at end of file diff --git a/tests/e2e-tests/multi-page-routing.js b/tests/e2e-tests/multi-page-routing.js index c4e4b21b2..941586fe6 100644 --- a/tests/e2e-tests/multi-page-routing.js +++ b/tests/e2e-tests/multi-page-routing.js @@ -26,11 +26,10 @@ describe("multi page routing", function () { it("navigates and returns", function () { var expectedHookLog = [ - "first.activate first null", - "first.deactivate second first", - "second.activate second first", - "second.deactivate first second", - "first.activate first second"].join(","); + "first.init", // <-- load + "second.init", // <-- forward (first component is not destroyed) + "second.destroy"] // <-- back (first component is reused) + .join(","); return driver .elementByAccessibilityId("first-navigate-multi-page") .should.eventually.exist @@ -38,7 +37,7 @@ describe("multi page routing", function () { .elementByAccessibilityId("second-multi-page") .should.eventually.exist .text().should.eventually.equal("Second: multi-page") - .elementByAccessibilityId("second-navigate-multi-page") + .elementByAccessibilityId("second-navigate-back-multi-page") .should.eventually.exist .tap() .elementByAccessibilityId("first-multi-page") diff --git a/tests/e2e-tests/single-page-routing.js b/tests/e2e-tests/single-page-routing.js index d766866a5..9bcbd306a 100644 --- a/tests/e2e-tests/single-page-routing.js +++ b/tests/e2e-tests/single-page-routing.js @@ -24,14 +24,13 @@ describe("single page routing", function () { .text().should.eventually.equal("First: single-page") }); - it("navigates, returns and fires hooks", function () { + it("navigates and returns", function () { var expectedHookLog = [ - "first.activate first null", - "first.deactivate second first", - "second.activate second first", - "second.deactivate first second", - "first.activate first second"].join(","); - + "first.init", // <--load + "first.destroy", // <--forward + "second.init", + "second.destroy", // <--back + "first.init"].join(","); return driver .elementByAccessibilityId("first-navigate-single-page") @@ -40,7 +39,7 @@ describe("single page routing", function () { .elementByAccessibilityId("second-single-page") .should.eventually.exist .text().should.eventually.equal("Second: single-page") - .elementByAccessibilityId("second-navigate-single-page") + .elementByAccessibilityId("second-navigate-back-single-page") .should.eventually.exist .tap() .elementByAccessibilityId("first-single-page")