diff --git a/CHANGELOG.md b/CHANGELOG.md
index 51c07e20d..a84d28a7c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,17 @@
+
+## 8.21.0 (2020-01-31)
+
+
+### Bug Fixes
+
+* wrong import path in compat package ([6984c26](https://github.com/NativeScript/nativescript-angular/commit/6984c26))
+* **hmr:** After HMR app is blank when re-launched ([#2108](https://github.com/NativeScript/nativescript-angular/issues/2108)) ([a479099](https://github.com/NativeScript/nativescript-angular/commit/a479099))
+
+
+### Features
+
+* introduce PageService ([#2111](https://github.com/NativeScript/nativescript-angular/issues/2111)) ([362e893](https://github.com/NativeScript/nativescript-angular/commit/362e893))
+
## [8.20.4](https://github.com/NativeScript/nativescript-angular/compare/8.20.3...8.20.4) (2020-01-07)
diff --git a/e2e/modal-navigation-ng/app/home/home.component.ts b/e2e/modal-navigation-ng/app/home/home.component.ts
index 72e8f889e..c40be5634 100644
--- a/e2e/modal-navigation-ng/app/home/home.component.ts
+++ b/e2e/modal-navigation-ng/app/home/home.component.ts
@@ -10,6 +10,7 @@ import { ModalViewComponent } from "../modal-shared/modal-view.component";
import { confirm } from "tns-core-modules/ui/dialogs";
import { AppModule } from "../app.module";
+import { PageService } from "nativescript-angular";
@Component({
moduleId: module.id,
@@ -21,7 +22,10 @@ export class HomeComponent {
private modal: ModalDialogService,
private vcRef: ViewContainerRef,
private viewContainerRefService: ViewContainerRefService,
- private routerExtension: RouterExtensions) { }
+ private pageService: PageService,
+ private routerExtension: RouterExtensions) {
+ this.pageService.inPage$.subscribe((inPage) => console.log("HomeComponent - inPage", inPage));
+ }
onNavigateSecond() {
this.routerExtension.navigate(["second"]);
diff --git a/nativescript-angular-package/package.json b/nativescript-angular-package/package.json
index 5cf0f21a4..9c0ce6a1d 100644
--- a/nativescript-angular-package/package.json
+++ b/nativescript-angular-package/package.json
@@ -1,6 +1,6 @@
{
"name": "nativescript-angular",
- "version": "8.20.4",
+ "version": "8.21.0",
"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",
diff --git a/nativescript-angular/nativescript.module.ts b/nativescript-angular/nativescript.module.ts
index 8a4da1ef6..a4a05d68a 100644
--- a/nativescript-angular/nativescript.module.ts
+++ b/nativescript-angular/nativescript.module.ts
@@ -27,7 +27,7 @@ import { NativeScriptCommonModule } from "./common";
import { NativeScriptRendererFactory } from "./renderer";
import { DetachedLoader } from "./common/detached-loader";
import { throwIfAlreadyLoaded } from "./common/utils";
-import { FrameService } from "./platform-providers";
+import { FrameService, PageService } from "./platform-providers";
export function errorHandlerFactory() {
return new ErrorHandler();
@@ -41,6 +41,7 @@ export { DetachedLoader };
],
providers: [
FrameService,
+ PageService,
NativeScriptRendererFactory,
SystemJsNgModuleLoader,
{ provide: APP_ROOT, useValue: true },
diff --git a/nativescript-angular/package.json b/nativescript-angular/package.json
index 47b89df37..5d42c9747 100644
--- a/nativescript-angular/package.json
+++ b/nativescript-angular/package.json
@@ -1,6 +1,6 @@
{
"name": "@nativescript/angular",
- "version": "8.20.4",
+ "version": "8.21.0",
"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",
diff --git a/nativescript-angular/platform-providers.ts b/nativescript-angular/platform-providers.ts
index ebe16e8a8..69b26af05 100644
--- a/nativescript-angular/platform-providers.ts
+++ b/nativescript-angular/platform-providers.ts
@@ -1,9 +1,11 @@
-import { InjectionToken, Injectable } from "@angular/core";
+import { InjectionToken, Injectable, OnDestroy } from "@angular/core";
-import { Frame } from "tns-core-modules/ui/frame";
+import { Frame, NavigatedData } from "tns-core-modules/ui/frame";
import { View } from "tns-core-modules/ui/core/view";
import { Page } from "tns-core-modules/ui/page";
import { device, Device } from "tns-core-modules/platform";
+import { BehaviorSubject, Subject, Observable } from "rxjs";
+import { distinctUntilChanged } from "rxjs/operators";
export const APP_ROOT_VIEW = new InjectionToken("App Root View");
export const DEVICE = new InjectionToken("platform device");
@@ -71,3 +73,45 @@ export class FrameService {
return topmostFrame;
}
}
+
+@Injectable()
+export class PageService implements OnDestroy {
+ private _inPage$ = new BehaviorSubject(false);
+ private _pageEvents$ = new Subject();
+
+ get inPage(): boolean { return this._inPage$.value; }
+ get inPage$(): Observable { return this._inPage$.pipe(distinctUntilChanged()); }
+ get pageEvents$(): Observable { return this._pageEvents$.asObservable(); }
+ constructor(public page: Page) {
+ if (this.page) {
+ this.page.on("navigatedFrom", this.pageEvent, this);
+ this.page.on("navigatedTo", this.pageEvent, this);
+ this.page.on("navigatingFrom", this.pageEvent, this);
+ this.page.on("navigatingTo", this.pageEvent, this);
+ }
+ }
+
+ ngOnDestroy() {
+ if (this.page) {
+ this.page.off("navigatedFrom", this.pageEvent, this);
+ this.page.off("navigatedTo", this.pageEvent, this);
+ this.page.off("navigatingFrom", this.pageEvent, this);
+ this.page.off("navigatingTo", this.pageEvent, this);
+ }
+ this._inPage$.complete();
+ this._pageEvents$.complete();
+ }
+
+ private pageEvent(evt: NavigatedData) {
+ this._pageEvents$.next(evt);
+ switch (evt.eventName) {
+ case "navigatedTo":
+ this._inPage$.next(true);
+ break;
+ case "navigatedFrom":
+ this._inPage$.next(false);
+ break;
+ default:
+ }
+ }
+}
diff --git a/nativescript-angular/router/page-router-outlet.ts b/nativescript-angular/router/page-router-outlet.ts
index 2e7ac3cbf..9cdd6634a 100644
--- a/nativescript-angular/router/page-router-outlet.ts
+++ b/nativescript-angular/router/page-router-outlet.ts
@@ -3,7 +3,7 @@ import {
ComponentFactory, ComponentFactoryResolver, ComponentRef,
Directive, Inject, InjectionToken, Injector,
OnDestroy, EventEmitter, Output,
- Type, ViewContainerRef, ElementRef
+ Type, ViewContainerRef, ElementRef, InjectFlags
} from "@angular/core";
import {
ActivatedRoute,
@@ -19,7 +19,7 @@ import { profile } from "tns-core-modules/profiling";
import { BehaviorSubject } from "rxjs";
-import { DEVICE, PAGE_FACTORY, PageFactory } from "../platform-providers";
+import { DEVICE, PAGE_FACTORY, PageFactory, PageService } from "../platform-providers";
import { routerLog as log, routerError as error, isLogEnabled } from "../trace";
import { DetachedLoader } from "../common/detached-loader";
import { ViewUtil } from "../view-util";
@@ -48,23 +48,28 @@ export function destroyComponentRef(componentRef: ComponentRef) {
}
}
-class ChildInjector implements Injector {
- constructor(
- private providers: ProviderMap,
- private parent: Injector
- ) { }
-
- get(token: Type | InjectionToken, notFoundValue?: T): T {
- let localValue = this.providers.get(token);
- if (localValue) {
- return localValue;
+class DestructibleInjector implements Injector {
+ private refs = new Set();
+ constructor(private destructableProviders: ProviderSet, private parent: Injector) {
+ }
+ get(token: Type | InjectionToken, notFoundValue?: T, flags?: InjectFlags): T {
+ const ref = this.parent.get(token, notFoundValue, flags);
+ if (this.destructableProviders.has(token)) {
+ this.refs.add(ref);
}
-
- return this.parent.get(token, notFoundValue);
+ return ref;
+ }
+ destroy() {
+ this.refs.forEach((ref) => {
+ if (ref.ngOnDestroy instanceof Function) {
+ ref.ngOnDestroy();
+ }
+ });
+ this.refs.clear();
}
}
-type ProviderMap = Map | InjectionToken, any>;
+type ProviderSet = Set | InjectionToken>;
/**
* There are cases where multiple activatedRoute nodes should be associated/handled by the same PageRouterOutlet.
@@ -335,16 +340,24 @@ export class PageRouterOutlet implements OnDestroy { // tslint:disable-line:dire
componentType: factory.componentType,
});
- const providers = new Map();
- providers.set(Page, page);
- providers.set(Frame, this.frame);
- providers.set(PageRoute, new PageRoute(activatedRoute));
- providers.set(ActivatedRoute, activatedRoute);
- providers.set(ChildrenOutletContexts, this.parentContexts.getOrCreateContext(this.name).children);
+ const destructables = new Set([PageService]);
+ const injector = Injector.create({
+ providers: [
+ { provide: PageService, useClass: PageService, deps: [Page] },
+ { provide: Page, useValue: page },
+ { provide: Frame, useValue: this.frame },
+ { provide: PageRoute, useValue: new PageRoute(activatedRoute) },
+ { provide: ActivatedRoute, useValue: activatedRoute },
+ { provide: ChildrenOutletContexts,
+ useValue: this.parentContexts.getOrCreateContext(this.name).children }
+ ],
+ parent: this.location.injector
+ });
- const childInjector = new ChildInjector(providers, this.location.injector);
+ const childInjector = new DestructibleInjector(destructables, injector);
const loaderRef = this.location.createComponent(
this.detachedLoaderFactory, this.location.length, childInjector, []);
+ loaderRef.onDestroy(() => childInjector.destroy());
this.changeDetector.markForCheck();
this.activated = loaderRef.instance.loadWithFactory(factory);