Skip to content

Commit 362e893

Browse files
authored
feat: introduce PageService (#2111)
* feat: introduce PageService * chore: fix typo
1 parent 966ee42 commit 362e893

File tree

4 files changed

+88
-26
lines changed

4 files changed

+88
-26
lines changed

Diff for: e2e/modal-navigation-ng/app/home/home.component.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { ModalViewComponent } from "../modal-shared/modal-view.component";
1010
import { confirm } from "tns-core-modules/ui/dialogs";
1111

1212
import { AppModule } from "../app.module";
13+
import { PageService } from "nativescript-angular";
1314

1415
@Component({
1516
moduleId: module.id,
@@ -21,7 +22,10 @@ export class HomeComponent {
2122
private modal: ModalDialogService,
2223
private vcRef: ViewContainerRef,
2324
private viewContainerRefService: ViewContainerRefService,
24-
private routerExtension: RouterExtensions) { }
25+
private pageService: PageService,
26+
private routerExtension: RouterExtensions) {
27+
this.pageService.inPage$.subscribe((inPage) => console.log("HomeComponent - inPage", inPage));
28+
}
2529

2630
onNavigateSecond() {
2731
this.routerExtension.navigate(["second"]);

Diff for: nativescript-angular/nativescript.module.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { NativeScriptCommonModule } from "./common";
2727
import { NativeScriptRendererFactory } from "./renderer";
2828
import { DetachedLoader } from "./common/detached-loader";
2929
import { throwIfAlreadyLoaded } from "./common/utils";
30-
import { FrameService } from "./platform-providers";
30+
import { FrameService, PageService } from "./platform-providers";
3131

3232
export function errorHandlerFactory() {
3333
return new ErrorHandler();
@@ -41,6 +41,7 @@ export { DetachedLoader };
4141
],
4242
providers: [
4343
FrameService,
44+
PageService,
4445
NativeScriptRendererFactory,
4546
SystemJsNgModuleLoader,
4647
{ provide: APP_ROOT, useValue: true },

Diff for: nativescript-angular/platform-providers.ts

+46-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import { InjectionToken, Injectable } from "@angular/core";
1+
import { InjectionToken, Injectable, OnDestroy } from "@angular/core";
22

3-
import { Frame } from "tns-core-modules/ui/frame";
3+
import { Frame, NavigatedData } from "tns-core-modules/ui/frame";
44
import { View } from "tns-core-modules/ui/core/view";
55
import { Page } from "tns-core-modules/ui/page";
66
import { device, Device } from "tns-core-modules/platform";
7+
import { BehaviorSubject, Subject, Observable } from "rxjs";
8+
import { distinctUntilChanged } from "rxjs/operators";
79

810
export const APP_ROOT_VIEW = new InjectionToken<View>("App Root View");
911
export const DEVICE = new InjectionToken<Device>("platform device");
@@ -71,3 +73,45 @@ export class FrameService {
7173
return topmostFrame;
7274
}
7375
}
76+
77+
@Injectable()
78+
export class PageService implements OnDestroy {
79+
private _inPage$ = new BehaviorSubject<boolean>(false);
80+
private _pageEvents$ = new Subject<NavigatedData>();
81+
82+
get inPage(): boolean { return this._inPage$.value; }
83+
get inPage$(): Observable<boolean> { return this._inPage$.pipe(distinctUntilChanged()); }
84+
get pageEvents$(): Observable<NavigatedData> { return this._pageEvents$.asObservable(); }
85+
constructor(public page: Page) {
86+
if (this.page) {
87+
this.page.on("navigatedFrom", this.pageEvent, this);
88+
this.page.on("navigatedTo", this.pageEvent, this);
89+
this.page.on("navigatingFrom", this.pageEvent, this);
90+
this.page.on("navigatingTo", this.pageEvent, this);
91+
}
92+
}
93+
94+
ngOnDestroy() {
95+
if (this.page) {
96+
this.page.off("navigatedFrom", this.pageEvent, this);
97+
this.page.off("navigatedTo", this.pageEvent, this);
98+
this.page.off("navigatingFrom", this.pageEvent, this);
99+
this.page.off("navigatingTo", this.pageEvent, this);
100+
}
101+
this._inPage$.complete();
102+
this._pageEvents$.complete();
103+
}
104+
105+
private pageEvent(evt: NavigatedData) {
106+
this._pageEvents$.next(evt);
107+
switch (evt.eventName) {
108+
case "navigatedTo":
109+
this._inPage$.next(true);
110+
break;
111+
case "navigatedFrom":
112+
this._inPage$.next(false);
113+
break;
114+
default:
115+
}
116+
}
117+
}

Diff for: nativescript-angular/router/page-router-outlet.ts

+35-22
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
ComponentFactory, ComponentFactoryResolver, ComponentRef,
44
Directive, Inject, InjectionToken, Injector,
55
OnDestroy, EventEmitter, Output,
6-
Type, ViewContainerRef, ElementRef
6+
Type, ViewContainerRef, ElementRef, InjectFlags
77
} from "@angular/core";
88
import {
99
ActivatedRoute,
@@ -19,7 +19,7 @@ import { profile } from "tns-core-modules/profiling";
1919

2020
import { BehaviorSubject } from "rxjs";
2121

22-
import { DEVICE, PAGE_FACTORY, PageFactory } from "../platform-providers";
22+
import { DEVICE, PAGE_FACTORY, PageFactory, PageService } from "../platform-providers";
2323
import { routerLog as log, routerError as error, isLogEnabled } from "../trace";
2424
import { DetachedLoader } from "../common/detached-loader";
2525
import { ViewUtil } from "../view-util";
@@ -48,23 +48,28 @@ export function destroyComponentRef(componentRef: ComponentRef<any>) {
4848
}
4949
}
5050

51-
class ChildInjector implements Injector {
52-
constructor(
53-
private providers: ProviderMap,
54-
private parent: Injector
55-
) { }
56-
57-
get<T>(token: Type<T> | InjectionToken<T>, notFoundValue?: T): T {
58-
let localValue = this.providers.get(token);
59-
if (localValue) {
60-
return localValue;
51+
class DestructibleInjector implements Injector {
52+
private refs = new Set<any>();
53+
constructor(private destructableProviders: ProviderSet, private parent: Injector) {
54+
}
55+
get<T>(token: Type<T> | InjectionToken<T>, notFoundValue?: T, flags?: InjectFlags): T {
56+
const ref = this.parent.get(token, notFoundValue, flags);
57+
if (this.destructableProviders.has(token)) {
58+
this.refs.add(ref);
6159
}
62-
63-
return this.parent.get(token, notFoundValue);
60+
return ref;
61+
}
62+
destroy() {
63+
this.refs.forEach((ref) => {
64+
if (ref.ngOnDestroy instanceof Function) {
65+
ref.ngOnDestroy();
66+
}
67+
});
68+
this.refs.clear();
6469
}
6570
}
6671

67-
type ProviderMap = Map<Type<any> | InjectionToken<any>, any>;
72+
type ProviderSet = Set<Type<any> | InjectionToken<any>>;
6873

6974
/**
7075
* 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
335340
componentType: factory.componentType,
336341
});
337342

338-
const providers = new Map();
339-
providers.set(Page, page);
340-
providers.set(Frame, this.frame);
341-
providers.set(PageRoute, new PageRoute(activatedRoute));
342-
providers.set(ActivatedRoute, activatedRoute);
343-
providers.set(ChildrenOutletContexts, this.parentContexts.getOrCreateContext(this.name).children);
343+
const destructables = new Set([PageService]);
344+
const injector = Injector.create({
345+
providers: [
346+
{ provide: PageService, useClass: PageService, deps: [Page] },
347+
{ provide: Page, useValue: page },
348+
{ provide: Frame, useValue: this.frame },
349+
{ provide: PageRoute, useValue: new PageRoute(activatedRoute) },
350+
{ provide: ActivatedRoute, useValue: activatedRoute },
351+
{ provide: ChildrenOutletContexts,
352+
useValue: this.parentContexts.getOrCreateContext(this.name).children }
353+
],
354+
parent: this.location.injector
355+
});
344356

345-
const childInjector = new ChildInjector(providers, this.location.injector);
357+
const childInjector = new DestructibleInjector(destructables, injector);
346358
const loaderRef = this.location.createComponent(
347359
this.detachedLoaderFactory, this.location.length, childInjector, []);
360+
loaderRef.onDestroy(() => childInjector.destroy());
348361
this.changeDetector.markForCheck();
349362

350363
this.activated = loaderRef.instance.loadWithFactory(factory);

0 commit comments

Comments
 (0)