diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a955ef1f..b5501235f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ + +## [7.2.4](https://github.com/NativeScript/nativescript-angular/compare/7.2.3...7.2.4) (2019-04-23) + + +### Bug Fixes + +* **router:** routing services should be provided in forRoot only ([#1729](https://github.com/NativeScript/nativescript-angular/issues/1729)) ([0f6a975](https://github.com/NativeScript/nativescript-angular/commit/0f6a975)) +* on destroy remove the lastBootstrappedModule ([5e13263](https://github.com/NativeScript/nativescript-angular/commit/5e13263)) +* page might be null'ed before clearHistory's navigatedToEvent ([ea66985](https://github.com/NativeScript/nativescript-angular/commit/ea66985)) +* remove rootContent on exit ([14e787f](https://github.com/NativeScript/nativescript-angular/commit/14e787f)) +* the exit event is triggered on restart ([3dffbd5](https://github.com/NativeScript/nativescript-angular/commit/3dffbd5)) + + +### Features + +* **modal:** add ‘ios presentationStyle’ option to ModalDialogParams ([9cfa127](https://github.com/NativeScript/nativescript-angular/commit/9cfa127)) + + + ## [7.2.3](https://github.com/NativeScript/nativescript-angular/compare/7.2.2...7.2.3) (2019-03-14) diff --git a/build-docs.sh b/build-docs.sh new file mode 100755 index 000000000..a0f39ec85 --- /dev/null +++ b/build-docs.sh @@ -0,0 +1,9 @@ +set -e + +ENV="${ENV:-dev}" +DIST_DIR="nativescript-angular/bin/dist" +APIREF_DIR="$DIST_DIR/ng-api-reference" +rm -rf "$APIREF_DIR" +cd "nativescript-angular" +npm install +npm run typedoc diff --git a/e2e/modal-navigation-ng/app/navigation/basic.navigation.component.ts b/e2e/modal-navigation-ng/app/navigation/basic.navigation.component.ts index 54c818612..ad0cd0031 100644 --- a/e2e/modal-navigation-ng/app/navigation/basic.navigation.component.ts +++ b/e2e/modal-navigation-ng/app/navigation/basic.navigation.component.ts @@ -1,4 +1,4 @@ -import { Component, ViewContainerRef, Input } from "@angular/core"; +import { Component, ViewContainerRef, Input, ViewChild, ElementRef } from "@angular/core"; import { Router, NavigationEnd } from "@angular/router"; import { ModalDialogService, ModalDialogOptions } from "nativescript-angular/directives/dialogs"; import { ModalComponent } from "../modal/modal.component"; @@ -17,11 +17,13 @@ import { ModalViewComponent } from "~/modal-shared/modal-view.component"; + ` }) export class BasicsNavigationComponent { + @ViewChild("popoverButtonComp") popoverButtonComp: ElementRef; @Input() col: number; constructor( private modal: ModalDialogService, @@ -29,7 +31,7 @@ export class BasicsNavigationComponent { private vcf: ViewContainerRef, private viewContainerRefService: ViewContainerRefService) { } - + onModalNoFrame() { const options: ModalDialogOptions = { context: { @@ -74,14 +76,28 @@ export class BasicsNavigationComponent { onRootModalTap(): void { const options: ModalDialogOptions = { - viewContainerRef: this.viewContainerRefService.root, - context: {}, - fullscreen: true + viewContainerRef: this.viewContainerRefService.root, + context: {}, + fullscreen: true }; - + this.modal.showModal(ModalViewComponent, options) - .then((result: string) => { - console.log(result); - }); - } + .then((result: string) => { + console.log(result); + }); + } + + onPopoverModal() { + const options: ModalDialogOptions = { + viewContainerRef: this.viewContainerRefService.root, + context: {}, + ios: { + presentationStyle: UIModalPresentationStyle.Popover + }, + target: this.popoverButtonComp.nativeElement + }; + + this.modal.showModal(ModalViewComponent, options) + .then((result: string) => { console.log(result);}); + } } diff --git a/e2e/modal-navigation-ng/package.json b/e2e/modal-navigation-ng/package.json index 444d4b4ee..fc6209dc9 100644 --- a/e2e/modal-navigation-ng/package.json +++ b/e2e/modal-navigation-ng/package.json @@ -46,6 +46,7 @@ "nativescript-dev-appium": "next", "nativescript-dev-typescript": "next", "nativescript-dev-webpack": "next", + "tns-platform-declarations": "next", "typescript": "~3.1.1" }, "scripts": { diff --git a/e2e/modal-navigation-ng/references.d.ts b/e2e/modal-navigation-ng/references.d.ts new file mode 100644 index 000000000..b945d69c5 --- /dev/null +++ b/e2e/modal-navigation-ng/references.d.ts @@ -0,0 +1 @@ +/// \ No newline at end of file diff --git a/nativescript-angular/directives/dialogs.ts b/nativescript-angular/directives/dialogs.ts index 7c8b45739..51363c032 100644 --- a/nativescript-angular/directives/dialogs.ts +++ b/nativescript-angular/directives/dialogs.ts @@ -6,7 +6,7 @@ import { NgModuleRef, ReflectiveInjector, Type, - ViewContainerRef, + ViewContainerRef } from "@angular/core"; import { NSLocationStrategy } from "../router/ns-location-strategy"; @@ -18,14 +18,15 @@ import { DetachedLoader } from "../common/detached-loader"; import { PageFactory, PAGE_FACTORY } from "../platform-providers"; import { once } from "../common/utils"; import { topmost, Frame } from "tns-core-modules/ui/frame"; +import { ShowModalOptions } from "tns-core-modules/ui/core/view"; -export interface ModalDialogOptions { +export type BaseShowModalOptions = Pick>; + +export interface ModalDialogOptions extends BaseShowModalOptions { context?: any; - fullscreen?: boolean; - animated?: boolean; - stretched?: boolean; viewContainerRef?: ViewContainerRef; moduleRef?: NgModuleRef; + target?: View; } export class ModalDialogParams { @@ -35,13 +36,10 @@ export class ModalDialogParams { } } -interface ShowDialogOptions { +interface ShowDialogOptions extends BaseShowModalOptions { containerRef: ViewContainerRef; context: any; doneCallback; - fullscreen: boolean; - animated: boolean; - stretched: boolean; pageFactory: PageFactory; parentView: ViewBase; resolver: ComponentFactoryResolver; @@ -54,16 +52,20 @@ export class ModalDialogService { } public showModal(type: Type, - { viewContainerRef, moduleRef, context, fullscreen, animated, stretched }: ModalDialogOptions + options: ModalDialogOptions ): Promise { - if (!viewContainerRef) { + if (!options.viewContainerRef) { throw new Error( "No viewContainerRef: " + "Make sure you pass viewContainerRef in ModalDialogOptions." ); } - let parentView = viewContainerRef.element.nativeElement; + let parentView = options.viewContainerRef.element.nativeElement; + if (options.target) { + parentView = options.target; + } + if (parentView instanceof AppHostView && parentView.ngAppRoot) { parentView = parentView.ngAppRoot; } @@ -75,11 +77,11 @@ export class ModalDialogService { parentView = parentView._ngDialogRoot; } - const pageFactory: PageFactory = viewContainerRef.injector.get(PAGE_FACTORY); + const pageFactory: PageFactory = options.viewContainerRef.injector.get(PAGE_FACTORY); // resolve from particular module (moduleRef) // or from same module as parentView (viewContainerRef) - const componentContainer = moduleRef || viewContainerRef; + const componentContainer = options.moduleRef || options.viewContainerRef; const resolver = componentContainer.injector.get(ComponentFactoryResolver); let frame = parentView; @@ -93,16 +95,14 @@ export class ModalDialogService { setTimeout(() => { try { this._showDialog({ - containerRef: viewContainerRef, - context, + ...options, + containerRef: options.viewContainerRef, + context: options.context, doneCallback: resolve, - fullscreen, - animated, - stretched, pageFactory, parentView, resolver, - type, + type }); } catch (err) { reject(err); @@ -111,23 +111,12 @@ export class ModalDialogService { }); } - private _showDialog({ - containerRef, - context, - doneCallback, - fullscreen, - animated, - stretched, - pageFactory, - parentView, - resolver, - type, - }: ShowDialogOptions): void { + private _showDialog(options: ShowDialogOptions): void { let componentView: View; let detachedLoaderRef: ComponentRef; const closeCallback = once((...args) => { - doneCallback.apply(undefined, args); + options.doneCallback.apply(undefined, args); if (componentView) { componentView.closeModal(); this.location._closeModalNavigation(); @@ -136,15 +125,15 @@ export class ModalDialogService { } }); - const modalParams = new ModalDialogParams(context, closeCallback); + const modalParams = new ModalDialogParams(options.context, closeCallback); const providers = ReflectiveInjector.resolve([ { provide: ModalDialogParams, useValue: modalParams }, ]); - const childInjector = ReflectiveInjector.fromResolvedProviders(providers, containerRef.parentInjector); - const detachedFactory = resolver.resolveComponentFactory(DetachedLoader); - detachedLoaderRef = containerRef.createComponent(detachedFactory, -1, childInjector, null); - detachedLoaderRef.instance.loadComponent(type).then((compRef) => { + const childInjector = ReflectiveInjector.fromResolvedProviders(providers, options.containerRef.parentInjector); + const detachedFactory = options.resolver.resolveComponentFactory(DetachedLoader); + detachedLoaderRef = options.containerRef.createComponent(detachedFactory, -1, childInjector, null); + detachedLoaderRef.instance.loadComponent(options.type).then((compRef) => { const detachedProxy = compRef.location.nativeElement; if (detachedProxy.getChildrenCount() > 1) { @@ -157,9 +146,7 @@ export class ModalDialogService { (componentView.parent).removeChild(componentView); } - // TODO: remove cast after https://github.com/NativeScript/NativeScript/pull/5734 - // is in a published version of tns-core-modules. - (parentView).showModal(componentView, context, closeCallback, fullscreen, animated, stretched); + options.parentView.showModal(componentView, { ...options, closeCallback }); }); } } diff --git a/nativescript-angular/package.json b/nativescript-angular/package.json index f63220905..b185a77f9 100644 --- a/nativescript-angular/package.json +++ b/nativescript-angular/package.json @@ -1,6 +1,6 @@ { "name": "nativescript-angular", - "version": "7.2.3", + "version": "7.2.4", "description": "An Angular renderer that lets you build mobile apps with NativeScript.", "homepage": "https://www.nativescript.org/", "bugs": "https://github.com/NativeScript/nativescript-angular/issues", @@ -34,7 +34,8 @@ "tsc-w": "tsc -p tsconfig.json -w", "ngc": "ngc -p tsconfig.json", "prepare": "npm run ngc", - "version": "rm -rf package-lock.json && conventional-changelog -p angular -i ../CHANGELOG.md -s && git add ../CHANGELOG.md" + "version": "rm -rf package-lock.json && conventional-changelog -p angular -i ../CHANGELOG.md -s && git add ../CHANGELOG.md", + "typedoc": "typedoc --tsconfig \"./tsconfig.typedoc.json\" --out ./bin/dist/ng-api-reference --includeDeclarations --name \"NativeScript Angular\" --theme ./node_modules/nativescript-typedoc-theme --excludeExternals --externalPattern \"**/+(tns-core-modules|module|declarations).d.ts\"" }, "bin": { "update-app-ng-deps": "./bin/update-app-ng-deps" @@ -73,6 +74,8 @@ "tns-core-modules": "next", "tslint": "^5.5.0", "typescript": "~3.1.1", - "zone.js": "^0.8.4" + "zone.js": "^0.8.4", + "nativescript-typedoc-theme": "git://github.com/NativeScript/nativescript-typedoc-theme.git#master", + "typedoc": "^0.13.0" } } diff --git a/nativescript-angular/platform-common.ts b/nativescript-angular/platform-common.ts index 2f7e958cd..d6a4c557f 100644 --- a/nativescript-angular/platform-common.ts +++ b/nativescript-angular/platform-common.ts @@ -33,6 +33,8 @@ import { on, launchEvent, LaunchEventData, + exitEvent, + ApplicationEventData, } from "tns-core-modules/application"; import { TextView } from "tns-core-modules/ui/text-view"; @@ -255,7 +257,29 @@ export class NativeScriptPlatformRef extends PlatformRef { args.root = rootContent; } ); + const exitCallback = profile( + "nativescript-angular/platform-common.exitCallback", (args: ApplicationEventData) => { + const androidActivity = args.android; + if (androidActivity && !androidActivity.isFinishing()) { + // Exit event was triggered as a part of a restart of the app. + return; + } + + const lastModuleRef = lastBootstrappedModule ? lastBootstrappedModule.get() : null; + if (lastModuleRef) { + // Make sure the module is only destroyed once + lastBootstrappedModule = null; + + lastModuleRef.destroy(); + } + + if (!autoCreateFrame) { + rootContent = null; + } + } + ); on(launchEvent, launchCallback); + on(exitEvent, exitCallback); applicationRun(); } diff --git a/nativescript-angular/router/ns-platform-location.ts b/nativescript-angular/router/ns-platform-location.ts index 311faa5b8..ebd86f067 100644 --- a/nativescript-angular/router/ns-platform-location.ts +++ b/nativescript-angular/router/ns-platform-location.ts @@ -6,7 +6,7 @@ import { routerLog, isLogEnabled } from "../trace"; @Injectable() export class NativescriptPlatformLocation extends PlatformLocation { - constructor(private locationStartegy: NSLocationStrategy) { + constructor(private locationStrategy: NSLocationStrategy) { super(); if (isLogEnabled()) { routerLog("NativescriptPlatformLocation.constructor()"); @@ -18,7 +18,7 @@ export class NativescriptPlatformLocation extends PlatformLocation { } onPopState(fn: LocationChangeListener): void { - this.locationStartegy.onPopState(fn); + this.locationStrategy.onPopState(fn); } onHashChange(_fn: LocationChangeListener): void { @@ -31,18 +31,18 @@ export class NativescriptPlatformLocation extends PlatformLocation { return ""; } get pathname(): string { - return this.locationStartegy.path(); + return this.locationStrategy.path(); } set pathname(_newPath: string) { throw new Error("NativescriptPlatformLocation set pathname - not implemented"); } pushState(state: any, title: string, url: string): void { - this.locationStartegy.pushState(state, title, url, null); + this.locationStrategy.pushState(state, title, url, null); } replaceState(state: any, title: string, url: string): void { - this.locationStartegy.replaceState(state, title, url, null); + this.locationStrategy.replaceState(state, title, url, null); } forward(): void { @@ -50,6 +50,6 @@ export class NativescriptPlatformLocation extends PlatformLocation { } back(): void { - this.locationStartegy.back(); + this.locationStrategy.back(); } } diff --git a/nativescript-angular/router/page-router-outlet.ts b/nativescript-angular/router/page-router-outlet.ts index fdcd5e2b1..63f9ea4a8 100644 --- a/nativescript-angular/router/page-router-outlet.ts +++ b/nativescript-angular/router/page-router-outlet.ts @@ -352,12 +352,19 @@ export class PageRouterOutlet implements OnDestroy { // tslint:disable-line:dire // Add it to the new page page.content = componentView; - page.on(Page.navigatedFromEvent, (global).Zone.current.wrap((args: NavigatedData) => { + const navigatedFromCallback = (global).Zone.current.wrap((args: NavigatedData) => { if (args.isBackNavigation) { this.locationStrategy._beginBackPageNavigation(this.frame); this.locationStrategy.back(null, this.frame); } - })); + }); + page.on(Page.navigatedFromEvent, navigatedFromCallback); + componentRef.onDestroy(() => { + if (page) { + page.off(Page.navigatedFromEvent, navigatedFromCallback); + page = null; + } + }); const navOptions = this.locationStrategy._beginPageNavigation(this.frame); @@ -367,14 +374,15 @@ export class PageRouterOutlet implements OnDestroy { // tslint:disable-line:dire if (this.outlet) { this.routeReuseStrategy.clearCache(this.outlet.outletKeys[0]); } - page.off(Page.navigatedToEvent, clearCallback); }); - page.on(Page.navigatedToEvent, clearCallback); + page.once(Page.navigatedToEvent, clearCallback); } this.frame.navigate({ - create: () => { return page; }, + create() { + return page; + }, clearHistory: navOptions.clearHistory, animated: navOptions.animated, transition: navOptions.transition diff --git a/nativescript-angular/router/router.module.ts b/nativescript-angular/router/router.module.ts index 385b4fe1f..5246eeea0 100644 --- a/nativescript-angular/router/router.module.ts +++ b/nativescript-angular/router/router.module.ts @@ -19,32 +19,39 @@ export { NSEmptyOutletComponent } from "./ns-empty-outlet.component"; export type LocationState = LocationState; +const ROUTER_DIRECTIVES = [NSRouterLink, NSRouterLinkActive, PageRouterOutlet, NSEmptyOutletComponent]; + +const NS_ROUTER_PROVIDERS = [ + { + provide: NSLocationStrategy, + useFactory: provideLocationStrategy, + deps: [[NSLocationStrategy, new Optional(), new SkipSelf()], FrameService], + }, + { provide: LocationStrategy, useExisting: NSLocationStrategy }, + NativescriptPlatformLocation, + { provide: PlatformLocation, useExisting: NativescriptPlatformLocation }, + RouterExtensions, + NSRouteReuseStrategy, + { provide: RouteReuseStrategy, useExisting: NSRouteReuseStrategy }, +]; + @NgModule({ - declarations: [NSRouterLink, NSRouterLinkActive, PageRouterOutlet, NSEmptyOutletComponent], - providers: [ - { - provide: NSLocationStrategy, - useFactory: provideLocationStrategy, - deps: [[NSLocationStrategy, new Optional(), new SkipSelf()], FrameService], - }, - { provide: LocationStrategy, useExisting: NSLocationStrategy }, - NativescriptPlatformLocation, - { provide: PlatformLocation, useClass: NativescriptPlatformLocation }, - RouterExtensions, - NSRouteReuseStrategy, - { provide: RouteReuseStrategy, useExisting: NSRouteReuseStrategy }, - ], + declarations: ROUTER_DIRECTIVES, + entryComponents: [NSEmptyOutletComponent], imports: [RouterModule, NativeScriptCommonModule], - exports: [RouterModule, NSRouterLink, NSRouterLinkActive, PageRouterOutlet, NSEmptyOutletComponent], + exports: [RouterModule, ...ROUTER_DIRECTIVES], schemas: [NO_ERRORS_SCHEMA], }) export class NativeScriptRouterModule { - static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProviders { - return RouterModule.forRoot(routes, config); + static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProviders { + return { + ngModule: NativeScriptRouterModule, + providers: [...RouterModule.forRoot(routes, config).providers, ...NS_ROUTER_PROVIDERS] + }; } - static forChild(routes: Routes): ModuleWithProviders { - return RouterModule.forChild(routes); + static forChild(routes: Routes): ModuleWithProviders { + return { ngModule: NativeScriptRouterModule, providers: RouterModule.forChild(routes).providers }; } } diff --git a/nativescript-angular/tsconfig.json b/nativescript-angular/tsconfig.json index e05ddfed9..e8a09f283 100644 --- a/nativescript-angular/tsconfig.json +++ b/nativescript-angular/tsconfig.json @@ -16,14 +16,7 @@ "dom", "es6", "es2015.iterable" - ], - "baseUrl": ".", - "paths": { - "*": [ - "./node_modules/tns-core-modules/*", - "./node_modules/*" - ] - } + ] }, "angularCompilerOptions": { "genDir": ".", diff --git a/nativescript-angular/tsconfig.typedoc.json b/nativescript-angular/tsconfig.typedoc.json new file mode 100644 index 000000000..3074c04b8 --- /dev/null +++ b/nativescript-angular/tsconfig.typedoc.json @@ -0,0 +1,61 @@ +{"compilerOptions": { + "target": "es5", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "noImplicitUseStrict": true, + "noEmitHelpers": true, + "declaration": true, + "removeComments": false, + "noEmitOnError": true, + "noImplicitAny": false, + "lib": [ + "dom", + "es6", + "es2015.iterable" + ], + "baseUrl": ".", + "paths": { + "*": [ + "./node_modules/tns-core-modules/*", + "./node_modules/*" + ] + } + }, + "angularCompilerOptions": { + "genDir": ".", + "skipMetadataEmit": false, + "skipTemplateCodegen": true, + "strictMetadataEmit": true + }, +"exclude": [ + "./node_modules", + "tns-core-modules/references.d.ts", + "tns-core-modules/node_modules", + "tns-core-modules/ui/frame/transition-definitions.android.d.ts", + "./zone-js", + "./index.ts", + "./bin", + "./index.d.ts", + "./testing", + "./animations/index.ts", + "./animations/utils.ts", + "./app-host-view.ts", + "./common/utils.ts", + "./dom-adapter.ts", + "./file-system", + "./forms/index.ts", + "./forms/value-accessors/index.ts", + "./lang-facade.ts", + "./polyfills", + "./router/private-imports", + "./schema-registry.ts", + "./http-client/index.ts", + "./http/index.ts", + "./router/index.ts", + "" + + ] +} \ No newline at end of file diff --git a/tests/app/tests/router-module-tests.ts b/tests/app/tests/router-module-tests.ts new file mode 100644 index 000000000..7bce3f7ef --- /dev/null +++ b/tests/app/tests/router-module-tests.ts @@ -0,0 +1,74 @@ +// make sure you import mocha-config before @angular/core +import { Component, ViewChild } from "@angular/core"; +import { nsTestBedAfterEach, nsTestBedBeforeEach, nsTestBedRender } from "nativescript-angular/testing"; +import { NativeScriptRouterModule, RouterExtensions } from "nativescript-angular/router"; +import { NSRouterLink } from "nativescript-angular/router/ns-router-link"; +import { NSLocationStrategy } from "nativescript-angular/router/ns-location-strategy"; +import { assert } from "~/tests/test-config"; +import { ActivatedRoute, Router, RouteReuseStrategy } from "@angular/router"; +import { LocationStrategy, PlatformLocation } from "@angular/common"; +import { NSRouteReuseStrategy } from "nativescript-angular/router/ns-route-reuse-strategy"; + +@Component({ + template: `` +}) +class RouterTestComponent { + @ViewChild(NSRouterLink) + nsRouterLink: NSRouterLink; +} + +describe("NativeScriptRouterModule.forRoot", () => { + beforeEach(nsTestBedBeforeEach( + [RouterTestComponent], + [], + [NativeScriptRouterModule.forRoot([])], + [])); + + afterEach(nsTestBedAfterEach()); + + it("should provide nativescript routing services", () => { + return nsTestBedRender(RouterTestComponent).then((fixture) => { + const injector = fixture.componentRef.injector + + assert.instanceOf(injector.get(LocationStrategy, null), NSLocationStrategy); + assert.instanceOf(injector.get(RouterExtensions, null), RouterExtensions); + assert.instanceOf(injector.get(RouteReuseStrategy, null), NSRouteReuseStrategy); + }); + }); + + it("should provide nativescript routing directives", () => { + return nsTestBedRender(RouterTestComponent).then((fixture) => { + const linkDirective = fixture.componentRef.instance.nsRouterLink; + assert.instanceOf(linkDirective, NSRouterLink); + }); + }); +}); + +describe("NativeScriptRouterModule.forChild", () => { + beforeEach(nsTestBedBeforeEach( + [RouterTestComponent], + [ + { provide: Router, useValue: {} }, + { provide: RouterExtensions, useValue: {} }, + { provide: ActivatedRoute, useValue: {} }, + ], + [NativeScriptRouterModule.forChild([])], + [])); + afterEach(nsTestBedAfterEach()); + + it("should not provide nativescript routing services", () => { + return nsTestBedRender(RouterTestComponent).then((fixture) => { + const injector = fixture.componentRef.injector + assert.isNull(injector.get(LocationStrategy, null)); + assert.isNull(injector.get(RouteReuseStrategy, null)); + }); + }); + + it("should provide nativescript routing directives", () => { + return nsTestBedRender(RouterTestComponent).then((fixture) => { + const linkDirective = fixture.componentRef.instance.nsRouterLink; + assert.instanceOf(linkDirective, NSRouterLink); + }); + }); +}); +