Skip to content

Commit 9bb68e0

Browse files
author
vakrilov
committed
feat: NativeScript 4.0 integration
Angular application is started with `application.run()` which means the the root view of the app will be the root view of the `AppComponent`. Previous behavior: Anagular application was started with `application.start()` - this aways creates a root `Frame`. The platfrom bootstrap was always creating the inital `Page` and loading the `AppComponent` inside. The `<page-router-outlet>` will create a Frame as a native element and will create a page for each component activated in it (including the inital page).
1 parent 447a3ce commit 9bb68e0

File tree

13 files changed

+283
-226
lines changed

13 files changed

+283
-226
lines changed

nativescript-angular/common/detached-loader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export class DetachedLoader { // tslint:disable-line:component-class-suffix
3131
const componentRef = this.containerRef.createComponent(
3232
factory, this.containerRef.length, this.containerRef.parentInjector);
3333

34-
// Component is created, buit may not be checked if we are loading
34+
// Component is created, built may not be checked if we are loading
3535
// inside component with OnPush CD strategy. Mark us for check to be sure CD will reach us.
3636
// We are inside a promise here so no need for setTimeout - CD should trigger
3737
// after the promise.

nativescript-angular/element-registry.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ registerElement("Button", () => require("tns-core-modules/ui/button").Button);
166166
registerElement("ContentView", () => require("tns-core-modules/ui/content-view").ContentView);
167167
registerElement("DatePicker", () => require("tns-core-modules/ui/date-picker").DatePicker);
168168
registerElement("DockLayout", () => require("tns-core-modules/ui/layouts/dock-layout").DockLayout);
169+
registerElement("Frame", () => require("tns-core-modules/ui/frame").Frame);
169170
registerElement("GridLayout", () => require("tns-core-modules/ui/layouts/grid-layout").GridLayout);
170171
registerElement("HtmlView", () => require("tns-core-modules/ui/html-view").HtmlView);
171172
registerElement("Image", () => require("tns-core-modules/ui/image").Image);
@@ -198,3 +199,5 @@ registerElement("Span", () => require("tns-core-modules/text/span").Span);
198199

199200
registerElement("DetachedContainer", () => require("tns-core-modules/ui/proxy-view-container").ProxyViewContainer,
200201
{ skipAddToDom: true });
202+
203+
registerElement("page-router-outlet", () => require("tns-core-modules/ui/frame").Frame);

nativescript-angular/platform-common.ts

Lines changed: 122 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import "./zone-js/dist/zone-nativescript";
66
import "reflect-metadata";
77
import "./polyfills/array";
88
import "./polyfills/console";
9-
import { profile, log, uptime } from "tns-core-modules/profiling";
9+
import { profile, uptime } from "tns-core-modules/profiling";
1010

1111
import {
1212
Type,
@@ -18,27 +18,32 @@ import {
1818
EventEmitter,
1919
Sanitizer,
2020
InjectionToken,
21-
StaticProvider
21+
StaticProvider,
2222
} from "@angular/core";
2323
import { DOCUMENT } from "@angular/common";
2424

25-
import { rendererLog, rendererError } from "./trace";
26-
import {
27-
PAGE_FACTORY,
28-
PageFactory,
29-
defaultPageFactoryProvider,
30-
setRootPage
31-
} from "./platform-providers";
25+
import { bootstrapLog, bootstrapLogError } from "./trace";
26+
import { PAGE_FACTORY, PageFactory, defaultPageFactoryProvider, setRootPage } from "./platform-providers";
3227

33-
import { start, setCssFileName } from "tns-core-modules/application";
34-
import { topmost, NavigationEntry } from "tns-core-modules/ui/frame";
28+
import {
29+
setCssFileName,
30+
run as applicationRun,
31+
on,
32+
off,
33+
launchEvent,
34+
LaunchEventData,
35+
ios as iosApp,
36+
} from "tns-core-modules/application";
37+
import { NavigationEntry } from "tns-core-modules/ui/frame";
3538
import { Page } from "tns-core-modules/ui/page";
39+
import { ContentView } from "tns-core-modules/ui/content-view";
3640
import { TextView } from "tns-core-modules/ui/text-view";
3741

3842
import "nativescript-intl";
43+
import { View, Color } from "tns-core-modules/ui/core/view/view";
3944

4045
export const onBeforeLivesync = new EventEmitter<NgModuleRef<any>>();
41-
export const onAfterLivesync = new EventEmitter<NgModuleRef<any>>();
46+
export const onAfterLivesync = new EventEmitter<{ moduleRef?: NgModuleRef<any>; error?: Error }>();
4247
let lastBootstrappedModule: WeakRef<NgModuleRef<any>>;
4348
type BootstrapperAction = () => Promise<NgModuleRef<any>>;
4449

@@ -65,7 +70,7 @@ export class NativeScriptSanitizer extends Sanitizer {
6570
// Add a fake polyfill for the document object
6671
(<any>global).document = (<any>global).document || {};
6772
const doc = (<any>global).document;
68-
doc.body = Object.assign((doc.body || {}), {
73+
doc.body = Object.assign(doc.body || {}, {
6974
isOverride: true,
7075
});
7176

@@ -84,7 +89,7 @@ export const COMMON_PROVIDERS = [
8489
export class NativeScriptPlatformRef extends PlatformRef {
8590
private _bootstrapper: BootstrapperAction;
8691

87-
constructor(private platform: PlatformRef, private appOptions?: AppOptions) {
92+
constructor(private platform: PlatformRef, private appOptions: AppOptions = {}) {
8893
super();
8994
}
9095

@@ -101,7 +106,7 @@ export class NativeScriptPlatformRef extends PlatformRef {
101106
bootstrapModule<M>(
102107
moduleType: Type<M>,
103108
compilerOptions: CompilerOptions | CompilerOptions[] = []
104-
): Promise<NgModuleRef<M>> {
109+
): Promise<NgModuleRef<M>> {
105110
this._bootstrapper = () => this.platform.bootstrapModule(moduleType, compilerOptions);
106111

107112
this.bootstrapApp();
@@ -111,39 +116,15 @@ export class NativeScriptPlatformRef extends PlatformRef {
111116

112117
@profile
113118
private bootstrapApp() {
114-
(<any>global).__onLiveSyncCore = () => this.livesyncModule();
115-
116-
const mainPageEntry = this.createNavigationEntry(this._bootstrapper);
119+
(<any>global).__onLiveSyncCore = () => {
120+
this.livesync();
121+
};
117122

118123
if (this.appOptions && typeof this.appOptions.cssFile === "string") {
119-
// TODO: All exported fields in ES6 modules should be read-only
120-
// Change the case when tns-core-modules become ES6 compatible and there is a legal way to set cssFile
121124
setCssFileName(this.appOptions.cssFile);
122125
}
123-
start(mainPageEntry);
124-
}
125-
126-
livesyncModule(): void {
127-
rendererLog("ANGULAR LiveSync Started");
128-
129-
onBeforeLivesync.next(lastBootstrappedModule ? lastBootstrappedModule.get() : null);
130126

131-
const mainPageEntry = this.createNavigationEntry(
132-
this._bootstrapper,
133-
compRef => onAfterLivesync.next(compRef),
134-
error => onAfterLivesync.error(error),
135-
true
136-
);
137-
mainPageEntry.animated = false;
138-
mainPageEntry.clearHistory = true;
139-
140-
const frame = topmost();
141-
if (frame) {
142-
if (frame.currentPage && frame.currentPage.modal) {
143-
frame.currentPage.modal.closeModal();
144-
}
145-
frame.navigate(mainPageEntry);
146-
}
127+
this.bootstrapNativeScriptApp();
147128
}
148129

149130
onDestroy(callback: () => void): void {
@@ -163,75 +144,117 @@ export class NativeScriptPlatformRef extends PlatformRef {
163144
}
164145

165146
@profile
166-
private createNavigationEntry(
167-
bootstrapAction: BootstrapperAction,
168-
resolve?: (comp: NgModuleRef<any>) => void,
169-
reject?: (e: Error) => void,
170-
isLivesync: boolean = false,
171-
isReboot: boolean = false): NavigationEntry {
172-
173-
const pageFactory: PageFactory = this.platform.injector.get(PAGE_FACTORY);
174-
175-
const navEntry: NavigationEntry = {
176-
create: (): Page => {
177-
const page = pageFactory({ isBootstrap: true, isLivesync });
178-
setRootPage(page);
179-
if (this.appOptions) {
180-
page.actionBarHidden = this.appOptions.startPageActionBarHidden;
181-
}
182-
183-
const initHandlerMethodName =
184-
"nativescript-angular/platform-common.initHandler";
185-
const initHandler = profile(initHandlerMethodName, () => {
186-
page.off(Page.navigatingToEvent, initHandler);
187-
// profiling.stop("application-start");
188-
rendererLog("Page loaded");
189-
190-
// profiling.start("ng-bootstrap");
191-
rendererLog("BOOTSTRAPPING...");
192-
193-
const bootstrapMethodName =
194-
"nativescript-angular/platform-common.postBootstrapAction";
195-
bootstrapAction().then(profile(bootstrapMethodName, moduleRef => {
196-
// profiling.stop("ng-bootstrap");
197-
log(`ANGULAR BOOTSTRAP DONE. ${uptime()}`);
147+
private bootstrapNativeScriptApp() {
148+
// Create a temp page for root of the renderer
149+
const tempRootView = new RootView();
150+
setRootPage(<any>tempRootView);
151+
let rootContent: View;
152+
153+
bootstrapLog("NativeScriptPlatform bootstrap started.");
154+
const launchCallback = profile(
155+
"nativescript-angular/platform-common.launchCallback",
156+
(args: LaunchEventData) => {
157+
bootstrapLog("Application launch event fired");
158+
off(launchEvent, launchCallback);
159+
160+
let bootstrapPromiseCompleted = false;
161+
this._bootstrapper().then(
162+
moduleRef => {
163+
bootstrapPromiseCompleted = true;
164+
165+
bootstrapLog(`Angular bootstrap bootstrap done. uptime: ${uptime()}`);
166+
rootContent = tempRootView.content;
167+
tempRootView.content = null;
168+
rootContent.parentNode = tempRootView;
198169
lastBootstrappedModule = new WeakRef(moduleRef);
170+
},
171+
err => {
172+
bootstrapPromiseCompleted = true;
199173

200-
if (resolve) {
201-
resolve(moduleRef);
202-
}
203-
return moduleRef;
204-
}), err => {
205-
rendererError("ERROR BOOTSTRAPPING ANGULAR");
206174
const errorMessage = err.message + "\n\n" + err.stack;
207-
rendererError(errorMessage);
175+
bootstrapLogError("ERROR BOOTSTRAPPING ANGULAR");
176+
bootstrapLogError(errorMessage);
208177

209-
let view = new TextView();
210-
view.text = errorMessage;
211-
page.content = view;
178+
rootContent = this.createErrorUI(errorMessage);
179+
}
180+
);
212181

213-
if (reject) {
214-
reject(err);
215-
}
216-
});
182+
bootstrapLog("bootstrapAction called, draining micro tasks queue. Root: " + rootContent);
183+
(<any>global).Zone.drainMicroTaskQueue();
184+
bootstrapLog("bootstrapAction called, draining micro tasks queue finished! Root: " + rootContent);
217185

218-
(<any>global).Zone.drainMicroTaskQueue();
219-
});
186+
if (!bootstrapPromiseCompleted) {
187+
const errorMessage = "Bootstrap promise didn't resolve";
188+
bootstrapLogError(errorMessage);
189+
rootContent = this.createErrorUI(errorMessage);
190+
}
220191

221-
page.on(Page.navigatingToEvent, initHandler);
192+
args.root = rootContent;
193+
}
194+
);
195+
on(launchEvent, launchCallback);
196+
197+
applicationRun();
198+
}
199+
200+
@profile
201+
private livesync() {
202+
bootstrapLog("Angular livesync started.");
203+
204+
onBeforeLivesync.next(lastBootstrappedModule ? lastBootstrappedModule.get() : null);
222205

223-
return page;
206+
const tempRootView = new RootView();
207+
setRootPage(<any>tempRootView);
208+
let rootContent: View;
209+
210+
let bootstrapPromiseCompleted = false;
211+
this._bootstrapper().then(
212+
moduleRef => {
213+
bootstrapPromiseCompleted = true;
214+
bootstrapLog("Angular livesync done.");
215+
onAfterLivesync.next({ moduleRef });
216+
217+
rootContent = tempRootView.content;
218+
tempRootView.content = null;
219+
rootContent.parentNode = tempRootView;
220+
lastBootstrappedModule = new WeakRef(moduleRef);
224221
},
225-
animated: false
226-
};
222+
error => {
223+
bootstrapPromiseCompleted = true;
224+
bootstrapLogError("ERROR LIVESYNC BOOTSTRAPPING ANGULAR");
225+
const errorMessage = error.message + "\n\n" + error.stack;
226+
bootstrapLogError(errorMessage);
227227

228-
if (isReboot) {
229-
navEntry.clearHistory = true;
228+
rootContent = this.createErrorUI(errorMessage);
229+
230+
onAfterLivesync.next({ error });
231+
}
232+
);
233+
234+
bootstrapLog("livesync bootstrapAction called, draining micro tasks queue. Root: " + rootContent);
235+
(<any>global).Zone.drainMicroTaskQueue();
236+
bootstrapLog("livesync bootstrapAction called, draining micro tasks queue finished! Root: " + rootContent);
237+
238+
if (!bootstrapPromiseCompleted) {
239+
const errorMessage = "Livesync bootstrap promise didn't resolve";
240+
bootstrapLogError(errorMessage);
241+
rootContent = this.createErrorUI(errorMessage);
242+
243+
onAfterLivesync.next({ error: new Error(errorMessage) });
230244
}
231245

232-
return navEntry;
246+
// TODO: use application._resetRootView method
247+
if (iosApp) {
248+
(<any>iosApp).setWindowContent(rootContent);
249+
}
233250
}
234251

235-
liveSyncApp() {
252+
private createErrorUI(message: string): View {
253+
const errorTextBox = new TextView();
254+
errorTextBox.text = message;
255+
errorTextBox.color = new Color("red");
256+
return errorTextBox;
236257
}
237258
}
259+
260+
class RootView extends ContentView {}

nativescript-angular/router/ns-location-strategy.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Injectable } from "@angular/core";
22
import { LocationStrategy } from "@angular/common";
33
import { routerLog } from "../trace";
4-
import { Frame, NavigationTransition } from "tns-core-modules/ui/frame";
4+
import { Frame, NavigationTransition, topmost } from "tns-core-modules/ui/frame";
55
import { isPresent } from "../lang-facade";
66

77
export interface NavigationOptions {
@@ -33,7 +33,7 @@ export class NSLocationStrategy extends LocationStrategy {
3333

3434
constructor(private frame: Frame) {
3535
super();
36-
routerLog("NSLocationStrategy.constructor()");
36+
routerLog("NSLocationStrategy.constructor() frame: " + this.frame);
3737
}
3838

3939
path(): string {
@@ -105,7 +105,8 @@ export class NSLocationStrategy extends LocationStrategy {
105105
// This was a page navigation - so navigate through frame.
106106
routerLog("NSLocationStrategy.back() while not navigating back but top" +
107107
" state is page - will call frame.goBack()");
108-
this.frame.goBack();
108+
const frame = this.frame || topmost();
109+
frame.goBack();
109110
} else {
110111
// Nested navigation - just pop the state
111112
routerLog("NSLocationStrategy.back() while not navigating back but top" +

0 commit comments

Comments
 (0)