Skip to content

Commit

Permalink
feat: introduce PageService (#2111)
Browse files Browse the repository at this point in the history
* feat: introduce PageService

* chore: fix typo
  • Loading branch information
edusperoni committed Jan 31, 2020
1 parent 966ee42 commit 362e893
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 26 deletions.
6 changes: 5 additions & 1 deletion e2e/modal-navigation-ng/app/home/home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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"]);
Expand Down
3 changes: 2 additions & 1 deletion nativescript-angular/nativescript.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -41,6 +41,7 @@ export { DetachedLoader };
],
providers: [
FrameService,
PageService,
NativeScriptRendererFactory,
SystemJsNgModuleLoader,
{ provide: APP_ROOT, useValue: true },
Expand Down
48 changes: 46 additions & 2 deletions nativescript-angular/platform-providers.ts
Original file line number Diff line number Diff line change
@@ -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<View>("App Root View");
export const DEVICE = new InjectionToken<Device>("platform device");
Expand Down Expand Up @@ -71,3 +73,45 @@ export class FrameService {
return topmostFrame;
}
}

@Injectable()
export class PageService implements OnDestroy {
private _inPage$ = new BehaviorSubject<boolean>(false);
private _pageEvents$ = new Subject<NavigatedData>();

get inPage(): boolean { return this._inPage$.value; }
get inPage$(): Observable<boolean> { return this._inPage$.pipe(distinctUntilChanged()); }
get pageEvents$(): Observable<NavigatedData> { 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:
}
}
}
57 changes: 35 additions & 22 deletions nativescript-angular/router/page-router-outlet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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";
Expand Down Expand Up @@ -48,23 +48,28 @@ export function destroyComponentRef(componentRef: ComponentRef<any>) {
}
}

class ChildInjector implements Injector {
constructor(
private providers: ProviderMap,
private parent: Injector
) { }

get<T>(token: Type<T> | InjectionToken<T>, notFoundValue?: T): T {
let localValue = this.providers.get(token);
if (localValue) {
return localValue;
class DestructibleInjector implements Injector {
private refs = new Set<any>();
constructor(private destructableProviders: ProviderSet, private parent: Injector) {
}
get<T>(token: Type<T> | InjectionToken<T>, 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<Type<any> | InjectionToken<any>, any>;
type ProviderSet = Set<Type<any> | InjectionToken<any>>;

/**
* There are cases where multiple activatedRoute nodes should be associated/handled by the same PageRouterOutlet.
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 362e893

Please sign in to comment.