diff --git a/.travis.yml b/.travis.yml
index 2c0a3c368..7f437f40b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,5 +4,5 @@ node_js:
script:
- cd nativescript-angular
- npm install
- - npm run tslint
+ - npm run format-check
- npm pack
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8d8ef7995..93c7d06b5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,23 @@
# [9.0.0](https://github.com/NativeScript/nativescript-angular/compare/8.20.4...9.0.0) (2020-06-03)
+
+### Bug Fixes
+
+* **bindable:** parent referenced expression-values now load properly using an update call ([#8670](https://github.com/NativeScript/NativeScript/issues/8670)) ([6b0028a](https://github.com/NativeScript/NativeScript/commit/6b0028afd7b554914b039cdf371e8e30f6e02dac)), closes [#8666](https://github.com/NativeScript/NativeScript/issues/8666) [#6981](https://github.com/NativeScript/NativeScript/issues/6981) [#5054](https://github.com/NativeScript/NativeScript/issues/5054)
+* **scroll-view:** android 'isScrollEnabled' will apply if changed while gesture is underway ([#8695](https://github.com/NativeScript/NativeScript/issues/8695)) ([02ec7f1](https://github.com/NativeScript/NativeScript/commit/02ec7f104d327df53df687ddd1b8ac5b1cdc04ba))
+* **snapshots:** android is not defined ([#8691](https://github.com/NativeScript/NativeScript/issues/8691)) ([a8bbd7c](https://github.com/NativeScript/NativeScript/commit/a8bbd7c1e580e77e7ad5ddc7be6845e3d8fb02de))
+* **text-view:** only reload text if hint is showing on ios ([#8662](https://github.com/NativeScript/NativeScript/issues/8662)) ([ec17727](https://github.com/NativeScript/NativeScript/commit/ec17727e91f7a3209ada2c7de0bcf59c98c4e62a))
+
+
+### Features
+
+* **connectivity:** getActiveNetworkInfo and NetworkInfo modern compliance [#8580](https://github.com/NativeScript/NativeScript/issues/8580) ([#8652](https://github.com/NativeScript/NativeScript/issues/8652)) ([635f31f](https://github.com/NativeScript/NativeScript/commit/635f31f81f7826112142c707aff2a66c2b480b0e))
+* **dialog:** ios destructive style from options ([#8676](https://github.com/NativeScript/NativeScript/issues/8676)) ([bb531ce](https://github.com/NativeScript/NativeScript/commit/bb531ce71028f9c4fd4d753df16c82104f158e35))
+* **ImageSource:** resize method ([#8678](https://github.com/NativeScript/NativeScript/issues/8678)) ([bd12baf](https://github.com/NativeScript/NativeScript/commit/bd12bafb4aae8f1c523be4c7e04fa73722092304))
+* **text-view:** allow easy subclassing on ios ([#8663](https://github.com/NativeScript/NativeScript/issues/8663)) ([7d36447](https://github.com/NativeScript/NativeScript/commit/7d364474c23e17acf7696f159d3945d8a73d63e6))
+
+
### Features
* angular 9 ivy ([fbe2450](https://github.com/NativeScript/nativescript-angular/commit/fbe2450))
diff --git a/build/pack-scripts/pack-scoped.ts b/build/pack-scripts/pack-scoped.ts
index fe72a16a4..8d5605b84 100644
--- a/build/pack-scripts/pack-scoped.ts
+++ b/build/pack-scripts/pack-scoped.ts
@@ -5,27 +5,29 @@ import { execSync } from "child_process";
console.log(`Packing @nativescript/angular package`);
const distFolderPath = path.resolve("../../dist");
-const tempFolderPath = path.resolve("./temp-scoped");
const outFileName = "nativescript-angular-scoped.tgz";
const nsAngularPackagePath = path.resolve("../../nativescript-angular");
+const nsAngularPackageDistPath = path.resolve(nsAngularPackagePath + "/dist");
-execSync(`npm install --save-exact`, {
- cwd: nsAngularPackagePath
-});
+function getFilesFromPath(path, extension) {
+ let files = fs.readdirSync( path );
+ return files.filter(file => file.match(new RegExp(`.*\.(${extension})`, 'ig')));
+}
+
+// execSync(`npm install --save-exact`, {
+// cwd: nsAngularPackagePath
+// });
// ensure empty temp and dist folders
-fs.emptyDirSync(tempFolderPath);
fs.ensureDirSync(distFolderPath);
// create .tgz in temp folder
-execSync(`npm pack ${nsAngularPackagePath}`, {
- cwd: tempFolderPath
-});
+execSync(`cd ${nsAngularPackagePath} && npm run build.pack`);
// assume we have a single file built in temp folder, take its name
-const currentFileName = fs.readdirSync(tempFolderPath)[0];
+const currentFileName = getFilesFromPath(nsAngularPackageDistPath, ".tgz")[0];
+console.log('currentFileName:', currentFileName);
// move built file and remove temp folder
-fs.moveSync(`${tempFolderPath}/${currentFileName}`, `${distFolderPath}/${outFileName}`, { overwrite: true });
-fs.removeSync(`${tempFolderPath}`);
+fs.moveSync(`${nsAngularPackageDistPath}/${currentFileName}`, `${distFolderPath}/${outFileName}`, { overwrite: true });
diff --git a/build/pack-scripts/tsconfig.json b/build/pack-scripts/tsconfig.json
index deb92c7ef..0bda36752 100644
--- a/build/pack-scripts/tsconfig.json
+++ b/build/pack-scripts/tsconfig.json
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
- "target": "es5",
+ "target": "es2015",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"noEmitHelpers": true,
diff --git a/doc/upgrading-zonejs.md b/doc/upgrading-zonejs.md
index d99dc2083..6ea5d173f 100644
--- a/doc/upgrading-zonejs.md
+++ b/doc/upgrading-zonejs.md
@@ -2,7 +2,7 @@
`nativescript-angular` uses a fork of the `zone.js` package in order to work around incompatibilities between node, browser, and mobile implementations.
-The fork resides at https://github.com/NativeScript/zone.js in the `zone-nativescript` branch. It adds a separate `lib/nativescript/nativescript.ts` entry point that is used to generate a new bundle: `dist/zone-nativescript.js`.
+The fork resides at https://github.com/NativeScript/zone.js in the `zone-nativescript` branch. It adds a separate `lib/nativescript/nativescript.ts` entry point that is used to generate a new bundle: `zone-nativescript.js`.
To upgrade to a newer release of `zone.js`:
@@ -11,5 +11,5 @@ To upgrade to a newer release of `zone.js`:
3. Rebuild: `gulp build`
4. Run the node-based smoke tests: `gulp test/nativescript`
5. Run the browser tests: `node_modules/.bin/karma start karma.conf.js --single-run` (You need to run node `test/ws-server.js` in a separate console first)
-6. Commit `dist/zone-nativescript.js`, drop the previous `dist/zone-nativescript.js` commit from the branch. Force push the new `zone-nativescript` branch to GitHub.
+6. Commit `zone-nativescript.js`, drop the previous `zone-nativescript.js` commit from the branch. Force push the new `zone-nativescript` branch to GitHub.
7. Update your copy of `nativescript-angular/zone.js/dist/zone-nativescript.js` with the bundle you just built.
diff --git a/e2e/animation-examples/app/App_Resources/iOS/build.xcconfig b/e2e/animation-examples/app/App_Resources/iOS/build.xcconfig
index 4b0118490..e77e78db9 100644
--- a/e2e/animation-examples/app/App_Resources/iOS/build.xcconfig
+++ b/e2e/animation-examples/app/App_Resources/iOS/build.xcconfig
@@ -4,4 +4,3 @@
// To build for device with Xcode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html
// DEVELOPMENT_TEAM = YOUR_TEAM_ID;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
-ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
diff --git a/e2e/animation-examples/app/animation-builder.component.scss b/e2e/animation-examples/app/animation-builder.component.scss
new file mode 100644
index 000000000..ca3407610
--- /dev/null
+++ b/e2e/animation-examples/app/animation-builder.component.scss
@@ -0,0 +1,3 @@
+.btn-primary {
+ background-color: pink;
+}
\ No newline at end of file
diff --git a/e2e/animation-examples/app/animation-builder.component.ts b/e2e/animation-examples/app/animation-builder.component.ts
index 0ea5a1bee..3713242c8 100644
--- a/e2e/animation-examples/app/animation-builder.component.ts
+++ b/e2e/animation-examples/app/animation-builder.component.ts
@@ -4,7 +4,8 @@ import { Component, ViewChild } from '@angular/core';
@Component({
template: `
- `
+ `,
+ styleUrls: ['./animation-builder.component.scss']
})
export class AnimationBuilderComponent {
@ViewChild('button', { static: false }) button;
diff --git a/e2e/animation-examples/app/app.module.ts b/e2e/animation-examples/app/app.module.ts
index 38de237d5..4a729f4b2 100644
--- a/e2e/animation-examples/app/app.module.ts
+++ b/e2e/animation-examples/app/app.module.ts
@@ -24,7 +24,7 @@ export function asyncBoot(): Function {
return (): Promise => new Promise(resolve => {
setTimeout(() => {
resolve();
- }, 2000);
+ }, 5000);
})
}
diff --git a/e2e/animation-examples/app/app.routing.ts b/e2e/animation-examples/app/app.routing.ts
index df70ca363..305e244e9 100644
--- a/e2e/animation-examples/app/app.routing.ts
+++ b/e2e/animation-examples/app/app.routing.ts
@@ -1,6 +1,6 @@
import { NgModule } from "@angular/core";
import { Routes } from "@angular/router";
-import { NativeScriptRouterModule } from "@nativescript/angular/router";
+import { NativeScriptRouterModule } from "@nativescript/angular";
import { AnimationsListComponent } from "./animations-list.component";
import { AnimationBuilderComponent } from "./animation-builder.component";
diff --git a/e2e/animation-examples/app/hero/hero-routing.module.ts b/e2e/animation-examples/app/hero/hero-routing.module.ts
index 57426f197..6f65be89b 100644
--- a/e2e/animation-examples/app/hero/hero-routing.module.ts
+++ b/e2e/animation-examples/app/hero/hero-routing.module.ts
@@ -1,6 +1,6 @@
import { NgModule } from "@angular/core";
import { Routes } from "@angular/router";
-import { NativeScriptRouterModule } from "@nativescript/angular/router";
+import { NativeScriptRouterModule } from "@nativescript/angular";
import { HeroTeamBuilderComponent } from './hero-team-builder.component';
diff --git a/e2e/animation-examples/app/hero/hero.module.ts b/e2e/animation-examples/app/hero/hero.module.ts
index 139da6013..938ec409c 100644
--- a/e2e/animation-examples/app/hero/hero.module.ts
+++ b/e2e/animation-examples/app/hero/hero.module.ts
@@ -1,5 +1,5 @@
import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
-import { NativeScriptCommonModule } from "@nativescript/angular/common";
+import { NativeScriptCommonModule } from "@nativescript/angular";
import { HeroRoutingModule, routedComponents } from "./hero-routing.module";
diff --git a/e2e/animation-examples/app/main.aot.ts b/e2e/animation-examples/app/main.aot.ts
deleted file mode 100644
index 015f6e008..000000000
--- a/e2e/animation-examples/app/main.aot.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import { platformNativeScript } from "@nativescript/angular/platform-static";
-import { AppModuleNgFactory } from "./app.module.ngfactory";
-
-platformNativeScript().bootstrapModuleFactory(AppModuleNgFactory);
diff --git a/e2e/animation-examples/app/main.ts b/e2e/animation-examples/app/main.ts
index 6d0342a02..92fc7dbca 100644
--- a/e2e/animation-examples/app/main.ts
+++ b/e2e/animation-examples/app/main.ts
@@ -1,103 +1,74 @@
-import { platformNativeScriptDynamic } from "@nativescript/angular/platform";
-import { animationsTraceCategory } from "@nativescript/angular/trace";
-import { setCategories, enable } from "@nativescript/core/trace";
-import {
- GridLayout,
- ItemSpec,
- GridUnitType,
-} from '@nativescript/core/ui/layouts/grid-layout';
-import {
- HorizontalAlignment,
- VerticalAlignment,
-} from '@nativescript/core/ui/enums/enums';
+import { platformNativeScriptDynamic, NativeScriptDebug, AppLaunchView } from "@nativescript/angular";
+import { Trace, GridLayout, GridUnitType, ItemSpec, Application } from "@nativescript/core";
import { AppModule } from "./app.module";
-setCategories(animationsTraceCategory);
-enable();
+Trace.setCategories(NativeScriptDebug.animationsTraceCategory);
+Trace.enable();
-class LaunchAnimation extends GridLayout {
+class LaunchAnimation extends GridLayout implements AppLaunchView {
circle: GridLayout;
- animatedContainer: GridLayout;
finished = false;
+ complete: () => void;
constructor() {
super();
+ this.backgroundColor = "#4caef7";
+ this.className = "w-full h-full";
- // setup container to house launch animation
- this.animatedContainer = new GridLayout();
- this.animatedContainer.style.zIndex = 100;
- this.animatedContainer.backgroundColor = '#4caef7';
- this.animatedContainer.className = 'w-full h-full';
-
- // any creative animation can be put inside
+ // construct any creative animation
this.circle = new GridLayout();
this.circle.width = 30;
this.circle.height = 30;
this.circle.borderRadius = 15;
- this.circle.horizontalAlignment = HorizontalAlignment.center;
- this.circle.verticalAlignment = VerticalAlignment.center;
- this.circle.backgroundColor = '#fff';
- this.animatedContainer.addRow(new ItemSpec(1, GridUnitType.STAR));
- this.animatedContainer.addRow(new ItemSpec(1, GridUnitType.AUTO));
- this.animatedContainer.addRow(new ItemSpec(1, GridUnitType.STAR));
- GridLayout.setRow(this.circle, 1);
- this.animatedContainer.addChild(this.circle);
+ this.circle.horizontalAlignment = "center";
+ this.circle.verticalAlignment = "middle";
+ this.circle.backgroundColor = "#fff";
- // add animation to top row since booted app will insert into bottom row
- GridLayout.setRow(this.animatedContainer, 1);
- this.addChild(this.animatedContainer);
+ this.addChild(this.circle);
}
- startAnimation() {
- this.circle
- .animate({
- scale: { x: 2, y: 2 },
- duration: 800,
- })
- .then(() => {
- this.circle
- .animate({
- scale: { x: 1, y: 1 },
- duration: 800,
- })
- .then(() => {
- if (this.finished) {
- this.circle
- .animate({
- scale: { x: 30, y: 30 },
- duration: 400,
- })
- .then(() => {
- this.fadeOut();
- });
- } else {
- // keep looping
- this.startAnimation();
- }
- });
+ async startAnimation() {
+ await this.circle.animate({
+ scale: { x: 2, y: 2 },
+ duration: 800,
+ });
+
+ await this.circle.animate({
+ scale: { x: 1, y: 1 },
+ duration: 800,
+ });
+
+ if (this.finished) {
+ await this.circle.animate({
+ scale: { x: 30, y: 30 },
+ duration: 400,
});
+ this.fadeOut();
+ } else {
+ // keep looping
+ this.startAnimation();
+ }
}
cleanup() {
- this.finished = true;
+ return new Promise((resolve) => {
+ this.complete = resolve;
+ this.finished = true;
+ });
}
- fadeOut() {
- this.animatedContainer
- .animate({
- opacity: 0,
- duration: 400,
- })
- .then(() => {
- this._removeView(this.animatedContainer);
- this.animatedContainer = null;
- this.circle = null;
- });
+ async fadeOut() {
+ await this.animate({
+ opacity: 0,
+ duration: 400,
+ });
+ this.complete();
}
}
platformNativeScriptDynamic({
launchView: new LaunchAnimation(),
- // backgroundColor: 'purple'
+ // backgroundColor: 'purple',
+ // async: true
}).bootstrapModule(AppModule);
diff --git a/e2e/animation-examples/app/vendor.ts b/e2e/animation-examples/app/vendor.ts
deleted file mode 100644
index 3dceebdfa..000000000
--- a/e2e/animation-examples/app/vendor.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-// Snapshot the ~/app.css and the theme
-const application = require("application");
-require("ui/styling/style-scope");
-const appCssContext = require.context("~/", false, /^\.\/app\.(css|scss|less|sass)$/);
-global.registerWebpackModules(appCssContext);
-application.loadAppCss();
-
-require("reflect-metadata");
-require("@angular/platform-browser");
-require("@angular/core");
-require("@angular/common");
-require("@angular/forms");
-require("@angular/router");
-
-require("nativescript-angular/platform-static");
-require("nativescript-angular/forms");
-require("nativescript-angular/router");
diff --git a/e2e/animation-examples/ngcc.config.js b/e2e/animation-examples/ngcc.config.js
deleted file mode 100644
index 2d2413164..000000000
--- a/e2e/animation-examples/ngcc.config.js
+++ /dev/null
@@ -1,19 +0,0 @@
-module.exports = {
- packages: {
- "@nativescript/angular": {
- entryPoints: {
- ".": {
- override: {
- main: "./index.js",
- typings: "./index.d.ts",
- },
- ignoreMissingDependencies: true,
- }
- },
- ignorableDeepImportMatchers: [
- /tns-core-modules\//,
- /@nativescript\/core\//,
- ]
- }
- }
-};
\ No newline at end of file
diff --git a/e2e/animation-examples/package.json b/e2e/animation-examples/package.json
index 7b511f01f..d000ce3e0 100644
--- a/e2e/animation-examples/package.json
+++ b/e2e/animation-examples/package.json
@@ -6,58 +6,57 @@
"nativescript": {
"id": "org.nativescript.ng4animations",
"tns-ios": {
- "version": "6.5.1"
+ "version": "6.5.2"
},
"tns-android": {
"version": "latest"
}
},
"dependencies": {
- "@angular/animations": "~9.1.0",
- "@angular/common": "~9.1.0",
- "@angular/compiler": "~9.1.0",
- "@angular/core": "~9.1.0",
- "@angular/forms": "~9.1.0",
- "@angular/platform-browser": "~9.1.0",
- "@angular/platform-browser-dynamic": "~9.1.0",
- "@angular/router": "~9.1.0",
+ "@angular/animations": "~10.0.0",
+ "@angular/common": "~10.0.0",
+ "@angular/compiler": "~10.0.0",
+ "@angular/core": "~10.0.0",
+ "@angular/forms": "~10.0.0",
+ "@angular/platform-browser": "~10.0.0",
+ "@angular/platform-browser-dynamic": "~10.0.0",
+ "@angular/router": "~10.0.0",
"@nativescript/angular": "file:../../dist/nativescript-angular-scoped.tgz",
"nativescript-theme-core": "~1.0.2",
"reflect-metadata": "~0.1.8",
"rxjs": "~6.5.5",
- "@nativescript/core": "next",
+ "@nativescript/core": "rc",
"zone.js": "^0.10.3"
},
"devDependencies": {
- "@angular/compiler-cli": "~9.1.0",
- "@ngtools/webpack": "~9.1.0",
- "@types/chai": "~4.1.7",
- "@types/mocha": "~5.2.5",
- "@types/node": "~10.12.18",
- "babel-traverse": "6.25.0",
- "babel-types": "6.25.0",
- "babylon": "6.17.4",
+ "@angular/compiler-cli": "~10.0.0",
+ "@ngtools/webpack": "~10.0.0",
+ "@types/chai": "~4.2.0",
+ "@types/mocha": "~7.0.0",
+ "@types/node": "~14.0.0",
+ "babel-traverse": "~6.26.0",
+ "babel-types": "~6.26.0",
+ "babylon": "~6.18.0",
"chai": "^4.2.0",
- "lazy": "1.0.11",
- "mocha": "~5.2.0",
- "mochawesome": "~3.1.2",
+ "lazy": "~1.0.11",
+ "mocha": "~8.0.1",
+ "mochawesome": "~6.1.1",
+ "node-sass": "~4.14.1",
"nativescript-css-loader": "~0.26.0",
- "nativescript-dev-appium": "^6.0.0",
- "nativescript-dev-webpack": "next",
- "typescript": "~3.8.3"
+ "@nativescript/webpack": "rc",
+ "typescript": "~3.9.0"
},
"scripts": {
"clean": "npx rimraf hooks node_modules platforms package-lock.json",
- "setup": "cd ../../nativescript-angular && npm run pack && cd ../e2e/animation-examples && npm run clean",
+ "setup": "cd ../../nativescript-angular && npm run prep.apps && cd ../e2e/animation-examples && npm run clean",
+ "ngcc": "ngcc --properties es2015 module main --first-only",
+ "postinstall": "npm run ngcc",
"u": "update-ns-webpack",
"e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
"e2e-watch": "tsc -p e2e --watch",
- "update-app-ng-deps": "update-app-ng-deps",
"ns-verify-bundle": "ns-verify-bundle",
"update-ns-webpack": "update-ns-webpack",
- "ngcc": "ngcc --properties es2015 module main --first-only",
- "postinstall": "npm run ngcc",
- "ios": "tns debug ios --env.aot --emulator --no-hmr",
- "android": "tns debug android --env.aot --emulator --no-hmr"
+ "ios": "tns debug ios --emulator --no-hmr",
+ "android": "tns debug android --emulator --no-hmr"
}
}
diff --git a/e2e/animation-examples/tsconfig.json b/e2e/animation-examples/tsconfig.json
index 1dbfa0f5b..ff6adb22e 100644
--- a/e2e/animation-examples/tsconfig.json
+++ b/e2e/animation-examples/tsconfig.json
@@ -1,7 +1,8 @@
{
"compilerOptions": {
- "module": "commonjs",
- "target": "es5",
+ "module": "ESNext",
+ "target": "es2015",
+ "moduleResolution": "node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"noEmitHelpers": true,
@@ -16,9 +17,6 @@
"paths": {
"~/*": [
"app/*"
- ],
- "*": [
- "./node_modules/*"
]
}
},
diff --git a/e2e/modal-navigation-ng/app/home/home.component.ts b/e2e/modal-navigation-ng/app/home/home.component.ts
index e14bc5111..49c26d63d 100644
--- a/e2e/modal-navigation-ng/app/home/home.component.ts
+++ b/e2e/modal-navigation-ng/app/home/home.component.ts
@@ -9,7 +9,6 @@ import { ModalViewComponent } from "../modal-shared/modal-view.component";
import { confirm } from "@nativescript/core/ui/dialogs";
import { AppModule } from "../app.module";
-import { PageService } from "@nativescript/angular";
@Component({
moduleId: module.id,
@@ -21,9 +20,8 @@ export class HomeComponent {
private modal: ModalDialogService,
private vcRef: ViewContainerRef,
private viewContainerRefService: ViewContainerRefService,
- private pageService: PageService,
private routerExtension: RouterExtensions) {
- this.pageService.inPage$.subscribe((inPage) => console.log("HomeComponent - inPage", inPage));
+
}
onNavigateSecond() {
diff --git a/e2e/modal-navigation-ng/app/main.aot.ts b/e2e/modal-navigation-ng/app/main.aot.ts
deleted file mode 100644
index bd0970ba9..000000000
--- a/e2e/modal-navigation-ng/app/main.aot.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-// this import should be first in order to load some required settings (like globals and reflect-metadata)
-import { platformNativeScript } from "@nativescript/angular/platform-static";
-import { AppModule } from "./app.module";
-import { NativeScriptPlatformRef } from "@nativescript/angular";
-// "./app.module.ngfactory" is a dynamically generated module when compiled with AoT.
-import { AppModuleNgFactory } from "./app.module.ngfactory";
-
-AppModule.platformRef = platformNativeScript();
-AppModule.platformRef.bootstrapModuleFactory(AppModuleNgFactory);
diff --git a/e2e/modal-navigation-ng/package.json b/e2e/modal-navigation-ng/package.json
index 8288caf02..0ad335400 100644
--- a/e2e/modal-navigation-ng/package.json
+++ b/e2e/modal-navigation-ng/package.json
@@ -50,7 +50,7 @@
"e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
"e2e-watch": "tsc -p e2e --watch",
"clean": "npx rimraf hooks node_modules platforms package-lock.json",
- "setup": "cd ../../nativescript-angular && npm run pack && cd ../e2e/modal-navigation-ng && npm run clean",
+ "setup": "cd ../../nativescript-angular && npm run prep.apps && cd ../e2e/modal-navigation-ng && npm run clean",
"ngcc": "ngcc --properties es2015 module main --first-only",
"postinstall": "npm run ngcc",
"ios": "tns debug ios --env.aot --emulator --no-hmr",
diff --git a/e2e/nested-router-tab-view/app/main.aot.ts b/e2e/nested-router-tab-view/app/main.aot.ts
deleted file mode 100644
index 97a9312ed..000000000
--- a/e2e/nested-router-tab-view/app/main.aot.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-// this import should be first in order to load some required settings (like globals and reflect-metadata)
-import { platformNativeScript } from "@nativescript/angular/platform-static";
-
-// "./app.module.ngfactory" is a dynamically generated module when compiled with AoT.
-import { AppModuleNgFactory } from "./app.module.ngfactory";
-
-platformNativeScript().bootstrapModuleFactory(AppModuleNgFactory);
diff --git a/e2e/nested-router-tab-view/package.json b/e2e/nested-router-tab-view/package.json
index 2739c99d4..791cbc65f 100644
--- a/e2e/nested-router-tab-view/package.json
+++ b/e2e/nested-router-tab-view/package.json
@@ -47,7 +47,7 @@
"e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
"e2e-watch": "tsc -p e2e --watch",
"clean": "npx rimraf hooks node_modules platforms package-lock.json",
- "setup": "cd ../../nativescript-angular && npm run pack && cd ../e2e/nested-router-tab-view && npm run clean",
+ "setup": "cd ../../nativescript-angular && npm run prep.apps && cd ../e2e/nested-router-tab-view && npm run clean",
"ngcc": "ngcc --properties es2015 module main --first-only",
"postinstall": "npm run ngcc"
}
diff --git a/e2e/renderer/app/main.aot.ts b/e2e/renderer/app/main.aot.ts
deleted file mode 100644
index 015f6e008..000000000
--- a/e2e/renderer/app/main.aot.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import { platformNativeScript } from "@nativescript/angular/platform-static";
-import { AppModuleNgFactory } from "./app.module.ngfactory";
-
-platformNativeScript().bootstrapModuleFactory(AppModuleNgFactory);
diff --git a/e2e/renderer/package.json b/e2e/renderer/package.json
index f6fe8e90d..3126f8f2e 100644
--- a/e2e/renderer/package.json
+++ b/e2e/renderer/package.json
@@ -41,7 +41,6 @@
},
"scripts": {
"e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
- "compile-tests-w": "tsc -p e2e --watch",
- "update-app-ng-deps": "update-app-ng-deps"
+ "compile-tests-w": "tsc -p e2e --watch"
}
}
diff --git a/e2e/routable-animations/app/app.module.ts b/e2e/routable-animations/app/app.module.ts
index d0a7e9a6d..a20ebebb1 100644
--- a/e2e/routable-animations/app/app.module.ts
+++ b/e2e/routable-animations/app/app.module.ts
@@ -7,9 +7,9 @@ import { AppRoutingModule } from './app-routing.module';
import { HomeComponent } from './home/home.component';
import { SupportComponent } from './support/support.component';
-import { animationsTraceCategory } from "@nativescript/angular/trace";
+import { NativeScriptDebug } from "@nativescript/angular/trace";
import { setCategories, enable } from "@nativescript/core/trace";
-setCategories(animationsTraceCategory);
+setCategories(NativeScriptDebug.animationsTraceCategory);
enable();
@NgModule({
diff --git a/e2e/routable-animations/app/main.aot.ts b/e2e/routable-animations/app/main.aot.ts
deleted file mode 100644
index 015f6e008..000000000
--- a/e2e/routable-animations/app/main.aot.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import { platformNativeScript } from "@nativescript/angular/platform-static";
-import { AppModuleNgFactory } from "./app.module.ngfactory";
-
-platformNativeScript().bootstrapModuleFactory(AppModuleNgFactory);
diff --git a/e2e/routable-animations/package.json b/e2e/routable-animations/package.json
index 103d69852..fb02e7d67 100644
--- a/e2e/routable-animations/package.json
+++ b/e2e/routable-animations/package.json
@@ -45,7 +45,6 @@
"ns-bundle": "ns-bundle",
"e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
"e2e-watch": "tsc -p e2e --watch",
- "update-app-ng-deps": "update-app-ng-deps",
"ns-verify-bundle": "ns-verify-bundle",
"update-ns-webpack": "update-ns-webpack"
}
diff --git a/e2e/router-tab-view/app/main.aot.ts b/e2e/router-tab-view/app/main.aot.ts
deleted file mode 100644
index 97a9312ed..000000000
--- a/e2e/router-tab-view/app/main.aot.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-// this import should be first in order to load some required settings (like globals and reflect-metadata)
-import { platformNativeScript } from "@nativescript/angular/platform-static";
-
-// "./app.module.ngfactory" is a dynamically generated module when compiled with AoT.
-import { AppModuleNgFactory } from "./app.module.ngfactory";
-
-platformNativeScript().bootstrapModuleFactory(AppModuleNgFactory);
diff --git a/e2e/router/package.json b/e2e/router/package.json
index 01537d520..b25f2ad6e 100644
--- a/e2e/router/package.json
+++ b/e2e/router/package.json
@@ -19,7 +19,6 @@
"@angular/platform-browser-dynamic": "~9.1.0",
"@angular/router": "~9.1.0",
"@nativescript/angular": "file:../../dist/nativescript-angular-scoped.tgz",
- "nativescript-intl": "^3.0.0",
"reflect-metadata": "~0.1.8",
"rxjs": "~6.5.5",
"@nativescript/core": "next",
@@ -49,8 +48,7 @@
"e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
"e2e-watch": "tsc -p e2e --watch",
"compile-tests-w": "tsc -p e2e --watch",
- "update-app-ng-deps": "update-app-ng-deps",
- "setup": "cd ../../nativescript-angular && npm run pack && cd ../e2e/router && npm run clean",
+ "setup": "cd ../../nativescript-angular && npm run prep.apps && cd ../e2e/router && npm run clean",
"u": "update-ns-webpack",
"ns-verify-bundle": "ns-verify-bundle",
"update-ns-webpack": "update-ns-webpack",
diff --git a/e2e/single-page/app/main.aot.ts b/e2e/single-page/app/main.aot.ts
deleted file mode 100644
index d0302e23e..000000000
--- a/e2e/single-page/app/main.aot.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import { platformNativeScript } from "@nativescript/angular/platform-static";
-import { AppModuleNgFactory } from "./app.module.ngfactory";
-
-platformNativeScript({ createFrameOnBootstrap: true }).bootstrapModuleFactory(AppModuleNgFactory);
diff --git a/e2e/single-page/package.json b/e2e/single-page/package.json
index 493cac330..e03922757 100644
--- a/e2e/single-page/package.json
+++ b/e2e/single-page/package.json
@@ -16,7 +16,6 @@
"@angular/platform-browser-dynamic": "~9.1.0",
"@angular/router": "~9.1.0",
"@nativescript/angular": "file:../../dist/nativescript-angular-scoped.tgz",
- "nativescript-intl": "^3.0.0",
"reflect-metadata": "~0.1.8",
"rxjs": "~6.5.5",
"@nativescript/core": "next",
@@ -43,7 +42,6 @@
},
"scripts": {
"e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
- "compile-tests-w": "tsc -p e2e --watch",
- "update-app-ng-deps": "update-app-ng-deps"
+ "compile-tests-w": "tsc -p e2e --watch"
}
}
diff --git a/e2e/tests-app-ng/app/lazy/lazy.component.ts b/e2e/tests-app-ng/app/lazy/lazy.component.ts
index 6bba0b05e..bb8d0e01c 100644
--- a/e2e/tests-app-ng/app/lazy/lazy.component.ts
+++ b/e2e/tests-app-ng/app/lazy/lazy.component.ts
@@ -1,7 +1,6 @@
import { Component } from "@angular/core";
-import { RouterExtensions } from "@nativescript/angular/router";
-import { ModalDialogParams } from "@nativescript/angular/directives/dialogs";
+import { RouterExtensions, ModalDialogParams } from "@nativescript/angular";
@Component({
selector: "ns-lazy",
diff --git a/e2e/tests-app-ng/app/lazy/lazy.module.ts b/e2e/tests-app-ng/app/lazy/lazy.module.ts
index 46bff152f..eea1ca005 100644
--- a/e2e/tests-app-ng/app/lazy/lazy.module.ts
+++ b/e2e/tests-app-ng/app/lazy/lazy.module.ts
@@ -1,6 +1,4 @@
-import { NativeScriptCommonModule } from "@nativescript/angular/common";
-import { NativeScriptRouterModule } from "@nativescript/angular/router";
-import { ModalDialogParams } from "@nativescript/angular/directives/dialogs";
+import { NativeScriptCommonModule, NativeScriptRouterModule, ModalDialogParams } from "@nativescript/angular";
import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
import { Routes } from "@angular/router";
diff --git a/e2e/tests-app-ng/app/list-view/list-view-nested-segmented-bar-page.component.ts b/e2e/tests-app-ng/app/list-view/list-view-nested-segmented-bar-page.component.ts
index ec0d9d963..74e161d7b 100644
--- a/e2e/tests-app-ng/app/list-view/list-view-nested-segmented-bar-page.component.ts
+++ b/e2e/tests-app-ng/app/list-view/list-view-nested-segmented-bar-page.component.ts
@@ -1,7 +1,5 @@
import { Component, ViewChild, ElementRef, OnInit } from "@angular/core";
-import { SegmentedBarItem, SegmentedBar } from "tns-core-modules/ui/segmented-bar/segmented-bar";
-import { ListView } from "tns-core-modules/ui/list-view/list-view";
-import { EventData } from "tns-core-modules/ui/page/page";
+import { SegmentedBarItem, SegmentedBar, ListView, EventData } from "@nativescript/core";
interface DataItem {
id: number;
diff --git a/e2e/tests-app-ng/app/main.ts b/e2e/tests-app-ng/app/main.ts
index 35bebd4d8..60b027e0c 100644
--- a/e2e/tests-app-ng/app/main.ts
+++ b/e2e/tests-app-ng/app/main.ts
@@ -1,8 +1,8 @@
-import { platformNativeScriptDynamic } from "@nativescript/angular/platform";
+import { platformNativeScriptDynamic } from "@nativescript/angular";
import { AppModule } from "./app.module";
-import { enable } from "@nativescript/core/trace";
+import { Trace } from "@nativescript/core";
-enable();
+Trace.enable();
platformNativeScriptDynamic().bootstrapModule(AppModule);
diff --git a/e2e/tests-app-ng/app/main/main-page-router-outlet.ts b/e2e/tests-app-ng/app/main/main-page-router-outlet.ts
index 7c30b1062..cf2d31525 100644
--- a/e2e/tests-app-ng/app/main/main-page-router-outlet.ts
+++ b/e2e/tests-app-ng/app/main/main-page-router-outlet.ts
@@ -58,6 +58,6 @@ export class MainComponent {
@Component({
selector: "navigation-main",
- template: ``
+ template: ``
})
export class NavigationMainPageRouterComponent { }
diff --git a/e2e/tests-app-ng/app/modal/lazy/lazy-load-modal.component.ts b/e2e/tests-app-ng/app/modal/lazy/lazy-load-modal.component.ts
index 3e36efd68..3571090ab 100644
--- a/e2e/tests-app-ng/app/modal/lazy/lazy-load-modal.component.ts
+++ b/e2e/tests-app-ng/app/modal/lazy/lazy-load-modal.component.ts
@@ -6,8 +6,7 @@ import {
ViewContainerRef,
} from "@angular/core";
-import { NSModuleFactoryLoader } from "@nativescript/angular/router";
-import { ModalDialogService } from "@nativescript/angular/directives/dialogs";
+import { NSModuleFactoryLoader, ModalDialogService } from "@nativescript/angular";
import { LazyComponent } from "../../lazy/lazy.component";
diff --git a/e2e/tests-app-ng/app/modal/modal-dialogs/modal-dialog.component.ts b/e2e/tests-app-ng/app/modal/modal-dialogs/modal-dialog.component.ts
index a83fe6864..1c069021b 100644
--- a/e2e/tests-app-ng/app/modal/modal-dialogs/modal-dialog.component.ts
+++ b/e2e/tests-app-ng/app/modal/modal-dialogs/modal-dialog.component.ts
@@ -8,7 +8,7 @@ import {
ModalDialogService,
ModalDialogOptions,
ModalDialogParams
-} from "@nativescript/angular/directives/dialogs";
+} from "@nativescript/angular";
@Component({
selector: "modal-content",
diff --git a/e2e/tests-app-ng/app/navigation-options/navigation-options.component.ts b/e2e/tests-app-ng/app/navigation-options/navigation-options.component.ts
index 869173661..06bdc3368 100644
--- a/e2e/tests-app-ng/app/navigation-options/navigation-options.component.ts
+++ b/e2e/tests-app-ng/app/navigation-options/navigation-options.component.ts
@@ -1,5 +1,5 @@
import { Component } from "@angular/core";
-import { RouterExtensions } from "@nativescript/angular/router";
+import { RouterExtensions } from "@nativescript/angular";
@Component({
selector: "nav-options",
diff --git a/e2e/tests-app-ng/ngcc.config.js b/e2e/tests-app-ng/ngcc.config.js
deleted file mode 100644
index ec433d6f3..000000000
--- a/e2e/tests-app-ng/ngcc.config.js
+++ /dev/null
@@ -1,20 +0,0 @@
-module.exports = {
- packages: {
- "@nativescript/angular": {
- entryPoints: {
- ".": {
- override: {
- main: "./index.js",
- typings: "./index.d.ts",
- },
- ignoreMissingDependencies: true,
- }
- },
- ignorableDeepImportMatchers: [
- /zone.js\//,
- /tns-core-modules\//,
- /@nativescript\/core\//,
- ]
- }
- }
-};
\ No newline at end of file
diff --git a/e2e/tests-app-ng/package.json b/e2e/tests-app-ng/package.json
index 85782a0c2..3e222dcd2 100644
--- a/e2e/tests-app-ng/package.json
+++ b/e2e/tests-app-ng/package.json
@@ -6,55 +6,51 @@
"nativescript": {
"id": "org.nativescript.testsappng",
"tns-ios": {
- "version": "6.5.0"
+ "version": "6.5.2"
},
"tns-android": {
"version": "6.5.0"
}
},
"dependencies": {
- "@angular/animations": "~9.1.0",
- "@angular/common": "~9.1.0",
- "@angular/compiler": "~9.1.0",
- "@angular/core": "~9.1.0",
- "@angular/forms": "~9.1.0",
- "@angular/platform-browser": "~9.1.0",
- "@angular/platform-browser-dynamic": "~9.1.0",
- "@angular/router": "~9.1.0",
+ "@angular/animations": "~10.0.0",
+ "@angular/common": "~10.0.0",
+ "@angular/compiler": "~10.0.0",
+ "@angular/core": "~10.0.0",
+ "@angular/forms": "~10.0.0",
+ "@angular/platform-browser": "~10.0.0",
+ "@angular/platform-browser-dynamic": "~10.0.0",
+ "@angular/router": "~10.0.0",
"@nativescript/angular": "file:../../dist/nativescript-angular-scoped.tgz",
- "nativescript-intl": "~3.0.0",
"nativescript-theme-core": "^1.0.4",
"reflect-metadata": "~0.1.8",
"rxjs": "~6.5.5",
- "@nativescript/core": "next",
+ "@nativescript/core": "rc",
"zone.js": "^0.10.3"
},
"devDependencies": {
- "@angular/compiler-cli": "~9.1.0",
- "@ngtools/webpack": "~9.1.0",
+ "@angular/compiler-cli": "~10.0.0",
+ "@ngtools/webpack": "~10.0.0",
"babel-traverse": "6.24.1",
"babel-types": "6.24.1",
"babylon": "6.17.0",
"codelyzer": "^5.1.0",
"filewalker": "^0.1.3",
"lazy": "1.0.11",
- "nativescript-dev-webpack": "next",
- "tslint": "^5.4.3",
- "typescript": "~3.8.3"
+ "@nativescript/webpack": "rc",
+ "typescript": "~3.9.0"
},
"scripts": {
- "tslint": "tslint --config tslint.json 'app/**/*.ts'",
"clean": "npx rimraf hooks node_modules platforms package-lock.json",
- "setup": "cd ../../nativescript-angular && npm run pack && cd ../e2e/tests-app-ng && npm run clean",
+ "setup": "cd ../../nativescript-angular && npm run prep.apps && cd ../e2e/tests-app-ng && npm run clean",
+ "ngcc": "ngcc --properties es2015 module main --first-only",
+ "postinstall": "npm run ngcc",
"u": "update-ns-webpack",
"e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
"e2e-watch": "tsc -p e2e --watch",
- "update-app-ng-deps": "update-app-ng-deps",
"ns-verify-bundle": "ns-verify-bundle",
"update-ns-webpack": "update-ns-webpack",
- "ngcc": "ngcc --properties es2015 module main --first-only",
- "postinstall": "npm run ngcc",
- "ios": "tns debug ios --env.aot --emulator --no-hmr",
- "android": "tns debug android --env.aot --emulator --no-hmr"
+ "ios": "tns debug ios --emulator --no-hmr",
+ "android": "tns debug android --emulator --no-hmr"
}
}
diff --git a/e2e/tests-app-ng/tsconfig.json b/e2e/tests-app-ng/tsconfig.json
index 576a402fa..3b4231662 100644
--- a/e2e/tests-app-ng/tsconfig.json
+++ b/e2e/tests-app-ng/tsconfig.json
@@ -1,7 +1,7 @@
{
"compilerOptions": {
- "module": "commonjs",
- "target": "es5",
+ "module": "esnext",
+ "target": "es2015",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"noEmitHelpers": true,
@@ -16,9 +16,6 @@
"paths": {
"~/*": [
"app/*"
- ],
- "*": [
- "./node_modules/*"
]
}
},
diff --git a/e2e/tests-app-ng/tslint.json b/e2e/tests-app-ng/tslint.json
deleted file mode 100644
index 4f96f5c0b..000000000
--- a/e2e/tests-app-ng/tslint.json
+++ /dev/null
@@ -1,132 +0,0 @@
-{
- "rulesDirectory": [
- "node_modules/codelyzer"
- ],
- "rules":{
- "use-input-property-decorator": true,
- "use-output-property-decorator": true,
- "use-host-property-decorator": true,
- "no-attribute-parameter-decorator": true,
- "no-input-rename": true,
- "no-output-rename": true,
- "use-life-cycle-interface": true,
- "use-pipe-transform-interface": true,
- "pipe-naming": [true, "camelCase"],
- "component-class-suffix": true,
- "directive-class-suffix": true,
- "import-destructuring-spacing": true,
- "member-access": false,
- "no-any": false,
- "no-inferrable-types": false,
- "no-internal-module": true,
- "no-var-requires": false,
- "typedef": false,
- "typedef-whitespace": [
- true,
- {
- "call-signature": "nospace",
- "index-signature": "nospace",
- "parameter": "nospace",
- "property-declaration": "nospace",
- "variable-declaration": "nospace"
- },
- {
- "call-signature": "space",
- "index-signature": "space",
- "parameter": "space",
- "property-declaration": "space",
- "variable-declaration": "space"
- }
- ],
-
- "ban": false,
- "curly": true,
- "no-arg": true,
- "no-bitwise": true,
- "no-conditional-assignment": true,
- "no-console": [
- true,
- "debug",
- "info",
- "time",
- "timeEnd",
- "trace"
- ],
- "no-construct": true,
- "no-debugger": true,
- "no-duplicate-variable": true,
- "no-empty": false,
- "no-eval": true,
- "no-null-keyword": false,
- "no-shadowed-variable": true,
- "no-string-literal": false,
- "no-switch-case-fall-through": true,
- "no-unused-expression": true,
- "no-var-keyword": true,
- "radix": false,
- "switch-default": true,
- "triple-equals": [
- true,
- "allow-null-check"
- ],
- "eofline": true,
- "indent": [
- true,
- "spaces"
- ],
- "max-line-length": [
- true,
- 120
- ],
- "no-require-imports": false,
- "no-trailing-whitespace": true,
- "object-literal-sort-keys": false,
- "trailing-comma": [
- true,
- {
- "multiline": false,
- "singleline": "never"
- }
- ],
-
- "align": false,
- "class-name": true,
- "comment-format": [
- true,
- "check-space"
- ],
- "interface-name": false,
- "jsdoc-format": true,
- "no-consecutive-blank-lines": [
- true, 2
- ],
- "one-line": [
- true,
- "check-open-brace",
- "check-catch",
- "check-else",
- "check-finally",
- "check-whitespace"
- ],
- "quotemark": [
- true,
- "double",
- "avoid-escape"
- ],
- "semicolon": [true, "always"],
- "variable-name": [
- true,
- "check-format",
- "allow-leading-underscore",
- "ban-keywords"
- ],
- "whitespace": [
- true,
- "check-branch",
- "check-decl",
- "check-operator",
- "check-separator",
- "check-type"
- ]
- }
-}
diff --git a/nativescript-angular-package/.npmignore b/nativescript-angular-package/.npmignore
index cfeb641a3..728e2a64d 100644
--- a/nativescript-angular-package/.npmignore
+++ b/nativescript-angular-package/.npmignore
@@ -1,4 +1,5 @@
*.tgz
+dist
*.ts
!*.d.ts
diff --git a/nativescript-angular-package/app-host-view.ts b/nativescript-angular-package/app-host-view.ts
index 2afcaf55e..2d2419d19 100644
--- a/nativescript-angular-package/app-host-view.ts
+++ b/nativescript-angular-package/app-host-view.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/app-host-view";
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/common.ts b/nativescript-angular-package/common.ts
index 8734f5c94..2d2419d19 100644
--- a/nativescript-angular-package/common.ts
+++ b/nativescript-angular-package/common.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/common";
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/dom-adapter.ts b/nativescript-angular-package/dom-adapter.ts
index 46373fefc..2d2419d19 100644
--- a/nativescript-angular-package/dom-adapter.ts
+++ b/nativescript-angular-package/dom-adapter.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/dom-adapter";
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/element-registry.ts b/nativescript-angular-package/element-registry.ts
index 46a37d420..2d2419d19 100644
--- a/nativescript-angular-package/element-registry.ts
+++ b/nativescript-angular-package/element-registry.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/element-registry";
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/forms/index.ts b/nativescript-angular-package/forms/index.ts
index 4c8c3c933..2d2419d19 100644
--- a/nativescript-angular-package/forms/index.ts
+++ b/nativescript-angular-package/forms/index.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/forms"
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/http-client/index.ts b/nativescript-angular-package/http-client/index.ts
index 939d93e6d..2d2419d19 100644
--- a/nativescript-angular-package/http-client/index.ts
+++ b/nativescript-angular-package/http-client/index.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/http-client"
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/index.ts b/nativescript-angular-package/index.ts
index 03145e814..053585d6a 100644
--- a/nativescript-angular-package/index.ts
+++ b/nativescript-angular-package/index.ts
@@ -1,9 +1 @@
export * from "@nativescript/angular";
-export * from "@nativescript/angular/forms";
-export * from "@nativescript/angular/router";
-export * from "@nativescript/angular/file-system/ns-file-system";
-export * from "@nativescript/angular/modal-dialog";
-export * from "@nativescript/angular/forms/value-accessors/base-value-accessor";
-export * from "@nativescript/angular/trace";
-export * from "@nativescript/angular/renderer";
-export * from "@nativescript/angular/view-util";
diff --git a/nativescript-angular-package/lang-facade.ts b/nativescript-angular-package/lang-facade.ts
index 994364fba..2d2419d19 100644
--- a/nativescript-angular-package/lang-facade.ts
+++ b/nativescript-angular-package/lang-facade.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/lang-facade";
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/modal-dialog.ts b/nativescript-angular-package/modal-dialog.ts
index 9dad19c5e..2d2419d19 100644
--- a/nativescript-angular-package/modal-dialog.ts
+++ b/nativescript-angular-package/modal-dialog.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/modal-dialog"
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/nativescript.module.ts b/nativescript-angular-package/nativescript.module.ts
index fc5956f1c..2d2419d19 100644
--- a/nativescript-angular-package/nativescript.module.ts
+++ b/nativescript-angular-package/nativescript.module.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/nativescript.module";
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/package.json b/nativescript-angular-package/package.json
index 210549b7a..0a75675e8 100644
--- a/nativescript-angular-package/package.json
+++ b/nativescript-angular-package/package.json
@@ -1,7 +1,7 @@
{
"name": "nativescript-angular",
- "version": "9.0.0",
- "description": "An Angular renderer that lets you build mobile apps with NativeScript.",
+ "version": "10.0.0",
+ "description": "Compatibility with old style nativescript-angular imports.",
"homepage": "https://www.nativescript.org/",
"bugs": "https://github.com/NativeScript/nativescript-angular/issues",
"author": {
@@ -22,24 +22,38 @@
"type": "git",
"url": "https://github.com/NativeScript/nativescript-angular.git"
},
- "dependencies": {
- "@nativescript/angular": "file:../nativescript-angular"
+ "ngPackage": {
+ "lib": {
+ "entryFile": "index.ts",
+ "umdModuleIds": {
+ "@nativescript/core": "ns-core",
+ "@nativescript/angular": "ns-angular"
+ }
+ },
+ "whitelistedNonPeerDependencies": [
+ "."
+ ]
},
"devDependencies": {
- "@angular/animations": "~9.1.0",
- "@angular/common": "~9.1.0",
- "@angular/compiler": "~9.1.0",
- "@angular/compiler-cli": "~9.1.0",
- "@angular/core": "~9.1.0",
- "@angular/forms": "~9.1.0",
- "@angular/platform-browser": "~9.1.0",
- "@angular/platform-browser-dynamic": "~9.1.0",
- "@angular/router": "~9.1.0",
+ "@angular/animations": "~10.0.0",
+ "@angular/common": "~10.0.0",
+ "@angular/compiler": "~10.0.0",
+ "@angular/compiler-cli": "~10.0.0",
+ "@angular/core": "~10.0.0",
+ "@angular/forms": "~10.0.0",
+ "@angular/platform-browser": "~10.0.0",
+ "@angular/platform-browser-dynamic": "~10.0.0",
+ "@angular/router": "~10.0.0",
+ "@nativescript/angular": "~10.0.0",
+ "@nativescript/core": "rc",
+ "ng-packagr": "^10.0.1",
"rxjs": "~6.5.5",
- "@nativescript/core": "next",
- "typescript": "~3.8.3"
+ "typescript": "~3.9.0"
},
"scripts": {
+ "setup": "npx rimraf hooks node_modules package-lock.json && npm i",
+ "build": "ng-packagr -p package.json",
+ "build.pack": "npm run tsc && npm run build && cd dist && npm pack",
"ngc": "ngc -p tsconfig.json",
"tsc": "tsc",
"pack-with-scoped-version": "cd ../build/pack-scripts && npm i && npx ts-node pack-compat.ts"
diff --git a/nativescript-angular-package/platform-common.ts b/nativescript-angular-package/platform-common.ts
index 6d8ac7743..2d2419d19 100644
--- a/nativescript-angular-package/platform-common.ts
+++ b/nativescript-angular-package/platform-common.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/platform-common";
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/platform-providers.ts b/nativescript-angular-package/platform-providers.ts
index dcc447e8d..2d2419d19 100644
--- a/nativescript-angular-package/platform-providers.ts
+++ b/nativescript-angular-package/platform-providers.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/platform-providers";
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/platform-static.ts b/nativescript-angular-package/platform-static.ts
deleted file mode 100644
index e63c475ec..000000000
--- a/nativescript-angular-package/platform-static.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from "@nativescript/angular/platform-static";
\ No newline at end of file
diff --git a/nativescript-angular-package/platform.ts b/nativescript-angular-package/platform.ts
index 7e8e49eee..2d2419d19 100644
--- a/nativescript-angular-package/platform.ts
+++ b/nativescript-angular-package/platform.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/platform";
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/renderer.ts b/nativescript-angular-package/renderer.ts
index 6dd053d7a..2d2419d19 100644
--- a/nativescript-angular-package/renderer.ts
+++ b/nativescript-angular-package/renderer.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/renderer";
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/resource-loader.ts b/nativescript-angular-package/resource-loader.ts
index e9e2794a8..2d2419d19 100644
--- a/nativescript-angular-package/resource-loader.ts
+++ b/nativescript-angular-package/resource-loader.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/resource-loader";
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/router/index.ts b/nativescript-angular-package/router/index.ts
index 0959f3730..2d2419d19 100644
--- a/nativescript-angular-package/router/index.ts
+++ b/nativescript-angular-package/router/index.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/router";
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/router/router.module.ts b/nativescript-angular-package/router/router.module.ts
index 1c7b8ed2b..50344ad36 100644
--- a/nativescript-angular-package/router/router.module.ts
+++ b/nativescript-angular-package/router/router.module.ts
@@ -1,2 +1 @@
-export { LocationState } from "@nativescript/angular/router/ns-location-strategy";
export * from "@nativescript/angular/router/router.module";
\ No newline at end of file
diff --git a/nativescript-angular-package/schema-registry.ts b/nativescript-angular-package/schema-registry.ts
index 1b76567a7..2d2419d19 100644
--- a/nativescript-angular-package/schema-registry.ts
+++ b/nativescript-angular-package/schema-registry.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/schema-registry";
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/testing/index.ts b/nativescript-angular-package/testing/index.ts
index 3939f9dd3..af69144d0 100644
--- a/nativescript-angular-package/testing/index.ts
+++ b/nativescript-angular-package/testing/index.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/testing"
\ No newline at end of file
+export * from "@nativescript/angular/testing";
\ No newline at end of file
diff --git a/nativescript-angular-package/testing/src/nativescript_test_component_renderer.ts b/nativescript-angular-package/testing/src/nativescript_test_component_renderer.ts
index f06c592a4..af69144d0 100644
--- a/nativescript-angular-package/testing/src/nativescript_test_component_renderer.ts
+++ b/nativescript-angular-package/testing/src/nativescript_test_component_renderer.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/testing/src/nativescript_test_component_renderer";
\ No newline at end of file
+export * from "@nativescript/angular/testing";
\ No newline at end of file
diff --git a/nativescript-angular-package/testing/src/util.ts b/nativescript-angular-package/testing/src/util.ts
index 42234d6e5..af69144d0 100644
--- a/nativescript-angular-package/testing/src/util.ts
+++ b/nativescript-angular-package/testing/src/util.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/testing/src/util";
+export * from "@nativescript/angular/testing";
\ No newline at end of file
diff --git a/nativescript-angular-package/trace.ts b/nativescript-angular-package/trace.ts
index 87044475c..2d2419d19 100644
--- a/nativescript-angular-package/trace.ts
+++ b/nativescript-angular-package/trace.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/trace";
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/tsconfig.json b/nativescript-angular-package/tsconfig.json
index 96c60bde6..85c737c2b 100644
--- a/nativescript-angular-package/tsconfig.json
+++ b/nativescript-angular-package/tsconfig.json
@@ -1,7 +1,7 @@
{
"compilerOptions": {
- "target": "es5",
- "module": "commonjs",
+ "target": "es2017",
+ "module": "esnext",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
@@ -9,9 +9,10 @@
"noImplicitUseStrict": true,
"noEmitHelpers": true,
"declaration": true,
- "removeComments": false,
+ "removeComments": true,
"noEmitOnError": true,
"skipLibCheck": true,
+ "skipDefaultLibCheck": true,
"noImplicitAny": false,
"lib": [
"es2017",
@@ -24,12 +25,14 @@
"genDir": ".",
"skipMetadataEmit": false,
"skipTemplateCodegen": true,
- "strictMetadataEmit": true
+ "strictMetadataEmit": true,
+ "enableIvy": true
},
"include": [
"**/*.ts"
],
- "exclude":[
- "node_modules/*"
+ "exclude": [
+ "node_modules",
+ "dist"
]
-}
+}
\ No newline at end of file
diff --git a/nativescript-angular-package/value-accessors/base-value-accessor.ts b/nativescript-angular-package/value-accessors/base-value-accessor.ts
index 7c5fd2186..aa3cfbdb5 100644
--- a/nativescript-angular-package/value-accessors/base-value-accessor.ts
+++ b/nativescript-angular-package/value-accessors/base-value-accessor.ts
@@ -1,3 +1,2 @@
// This file is only for compatibility with pre 4.4.0 releases.
-// Please use "nativescript-angular/forms/value-accessors/base-value-accessor"
-export * from "@nativescript/angular/forms/value-accessors/base-value-accessor";
+export * from "@nativescript/angular";
diff --git a/nativescript-angular-package/view-util.ts b/nativescript-angular-package/view-util.ts
index 53ecbb10e..2d2419d19 100644
--- a/nativescript-angular-package/view-util.ts
+++ b/nativescript-angular-package/view-util.ts
@@ -1 +1 @@
-export * from "@nativescript/angular/view-util";
\ No newline at end of file
+export * from "@nativescript/angular";
\ No newline at end of file
diff --git a/nativescript-angular-package/zone-js/testing.jasmine.ts b/nativescript-angular-package/zone-js/testing.jasmine.ts
index 06c6899df..b52f2e881 100644
--- a/nativescript-angular-package/zone-js/testing.jasmine.ts
+++ b/nativescript-angular-package/zone-js/testing.jasmine.ts
@@ -1,3 +1,4 @@
// Bootstrap helper module for jasmine spec tests
import "@nativescript/angular/platform";
-import "@nativescript/angular/zone-js/dist/zone-nativescript.jasmine.js";
+// import "@nativescript/angular/zone-js/dist/zone-nativescript.jasmine.js";
+import '@nativescript/zone-js';
diff --git a/nativescript-angular-package/zone-js/testing.mocha.ts b/nativescript-angular-package/zone-js/testing.mocha.ts
index f8ec59c8a..ce59f0e7b 100644
--- a/nativescript-angular-package/zone-js/testing.mocha.ts
+++ b/nativescript-angular-package/zone-js/testing.mocha.ts
@@ -1,2 +1,3 @@
import "@nativescript/angular/platform";
-import "@nativescript/angular/zone-js/dist/zone-nativescript.mocha.js";
+// import "@nativescript/angular/zone-js/dist/zone-nativescript.mocha.js";
+import '@nativescript/zone-js';
diff --git a/nativescript-angular/.npmignore b/nativescript-angular/.npmignore
index 9e43d16dc..6f87c2653 100644
--- a/nativescript-angular/.npmignore
+++ b/nativescript-angular/.npmignore
@@ -1,4 +1,5 @@
*.tgz
+dist
*.ts
!*.d.ts
diff --git a/nativescript-angular/.prettierignore b/nativescript-angular/.prettierignore
new file mode 100644
index 000000000..3cd5b3e47
--- /dev/null
+++ b/nativescript-angular/.prettierignore
@@ -0,0 +1,14 @@
+.github
+*.yml
+.vscode
+build
+dist
+doc
+e2e
+tests
+coverage
+platforms
+temp
+*.md
+*.json
+*.js
\ No newline at end of file
diff --git a/nativescript-angular/.prettierrc.json b/nativescript-angular/.prettierrc.json
new file mode 100644
index 000000000..098f6bab3
--- /dev/null
+++ b/nativescript-angular/.prettierrc.json
@@ -0,0 +1,6 @@
+{
+ "useTabs": true,
+ "printWidth": 600,
+ "tabWidth": 2,
+ "singleQuote": true
+}
\ No newline at end of file
diff --git a/nativescript-angular/README.md b/nativescript-angular/README.md
index 8f87f938c..b6d86f1a1 100644
--- a/nativescript-angular/README.md
+++ b/nativescript-angular/README.md
@@ -1 +1,45 @@
-[Get started with Angular and NativeScript.](http://docs.nativescript.org/angular/start/introduction.html)
+# NativeScript Angular
+[](https://travis-ci.org/NativeScript/nativescript-angular)
+
+This repository contains the code for integration of NativeScript with Angular.
+
+[NativeScript](https://www.nativescript.org/) is a framework which enables developers to write truly native mobile applications for Android and iOS using JavaScript and CSS. [Angular](https://angular.io/) is one of the most popular open source JavaScript frameworks for application development. We [worked closely with developers at Google](http://angularjs.blogspot.bg/2015/12/building-mobile-apps-with-angular-2-and.html) to make Angular in NativeScript a reality. The result is a software architecture that allows you to build mobile apps using the same framework—and in some cases the same code—that you use to build Angular web apps, with the performance you’d expect from native code. [Read more about building truly native mobile apps with NativeScript and Angular](https://docs.nativescript.org/tutorial/ng-chapter-0).
+
+
+
+
+- [NativeScript Angular](#nativescript-angular)
+ - [Watch the video explaining Angular and NativeScript](#watch-the-video-explaining-angular-and-nativescript)
+ - [Explore the examples](#explore-the-examples)
+ - [Contribute](#contribute)
+ - [Known issues](#known-issues)
+ - [Get Help](#get-help)
+
+
+
+
+## Watch the video explaining Angular and NativeScript
+[NativeScript session on AngularConnect conference](https://www.youtube.com/watch?v=4SbiiyRSIwo)
+
+## Explore the examples
+
+The `e2e` apps are meant for testing stuff. You can take a look at these additional sample apps that use the published builds from npm:
+
+* [Hello world starter](https://github.com/NativeScript/nativescript-app-templates/tree/master/packages/template-hello-world-ng)
+* [Master-detail template](https://github.com/NativeScript/nativescript-app-templates/tree/master/packages/template-master-detail-ng)
+* [Drawer navigation template](https://github.com/NativeScript/nativescript-app-templates/tree/master/packages/template-drawer-navigation-ng)
+* [TabView navigation template](https://github.com/NativeScript/nativescript-app-templates/tree/master/packages/template-tab-navigation-ng)
+* [NativeScript Angular SDK examples](https://github.com/NativeScript/nativescript-sdk-examples-ng)
+
+## Contribute
+We love PRs! Check out the [contributing guidelines](CONTRIBUTING.md) and [development workflow for local setup](DevelopmentWorkflow.md). If you want to contribute, but you are not sure where to start - look for [issues labeled `help wanted`](https://github.com/NativeScript/nativescript-angular/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22).
+
+## Known issues
+
+1. There are certain issues with the Parse5DomAdapter and we'll likely need to provide our own later on:
+ * Self-closing elements (``) get parsed wrong (in this case Button gets parsed as a Label child.
+
+## Get Help
+Please, use [github issues](https://github.com/NativeScript/nativescript-angular/issues) strictly for [reporting bugs](CONTRIBUTING.md#reporting-bugs) or [requesting features](CONTRIBUTING.md#requesting-new-features). For general questions and support, check out [Stack Overflow](https://stackoverflow.com/questions/tagged/nativescript) or ask our experts in [NativeScript community Slack channel](http://developer.telerik.com/wp-login.php?action=slack-invitation).
+
+
diff --git a/nativescript-angular/animations/animation-driver.ts b/nativescript-angular/animations/animation-driver.ts
index 897763e34..b534ba950 100644
--- a/nativescript-angular/animations/animation-driver.ts
+++ b/nativescript-angular/animations/animation-driver.ts
@@ -1,211 +1,159 @@
-import { AnimationPlayer } from "@angular/animations";
-import { AnimationDriver } from "@angular/animations/browser";
-import { createSelector, SelectorCore } from "@nativescript/core/ui/styling/css-selector";
-import { CssAnimationProperty } from "@nativescript/core/ui/core/properties";
-import { eachDescendant } from "@nativescript/core/ui/core/view";
-import { ProxyViewContainer } from "@nativescript/core/ui/proxy-view-container";
-
-import { NativeScriptAnimationPlayer } from "./animation-player";
-import {
- Keyframe,
- dashCaseToCamelCase,
-} from "./utils";
-import { NgView, InvisibleNode } from "../element-registry";
-import { animationsLog as traceLog } from "../trace";
+import { AnimationPlayer } from '@angular/animations';
+import { AnimationDriver } from '@angular/animations/browser';
+import { ProxyViewContainer, eachDescendant, CssAnimationProperty, CSSHelper } from '@nativescript/core';
+import { NativeScriptAnimationPlayer } from './animation-player';
+import { Keyframe, dashCaseToCamelCase } from './utils';
+import { NgView, InvisibleNode } from '../element-registry';
+import { NativeScriptDebug } from '../trace';
interface ViewMatchResult {
- found: boolean;
+ found: boolean;
}
interface ViewMatchParams {
- originalView: NgView;
+ originalView: NgView;
}
interface QueryParams {
- selector: Selector;
- multi: boolean;
+ selector: Selector;
+ multi: boolean;
}
interface QueryResult {
- matches: NgView[];
+ matches: NgView[];
}
class Selector {
- private nsSelectors: SelectorCore[];
- private classSelectors: string[];
-
- constructor(rawSelector: string) {
- this.parse(rawSelector);
- }
-
- match(element: NgView): boolean {
- return this.nsSelectorMatch(element) || this.classSelectorsMatch(element);
- }
-
- private parse(rawSelector: string) {
- const selectors = rawSelector.split(",").map(s => s.trim());
-
- this.nsSelectors = selectors.map(createSelector);
- this.classSelectors = selectors
- .filter(s => s.startsWith("."))
- .map(s => s.substring(1));
- }
-
- private nsSelectorMatch(element: NgView) {
- return this.nsSelectors.some(s => s.match(element));
- }
-
- private classSelectorsMatch(element: NgView) {
- return this.classSelectors.some(s => this.hasClass(element, s));
- }
-
- // we're using that instead of match for classes
- // that are dynamically added by the animation engine
- // such as .ng-trigger, that's added for every :enter view
- private hasClass(element: NgView, cls: string) {
- return element && element["$$classes"] && element["$$classes"][cls];
- }
+ private nsSelectors: Array;
+ private classSelectors: string[];
+
+ constructor(rawSelector: string) {
+ this.parse(rawSelector);
+ }
+
+ match(element: NgView): boolean {
+ return this.nsSelectorMatch(element) || this.classSelectorsMatch(element);
+ }
+
+ private parse(rawSelector: string) {
+ const selectors = rawSelector.split(',').map((s) => s.trim());
+
+ this.nsSelectors = selectors.map(CSSHelper.createSelector);
+ this.classSelectors = selectors.filter((s) => s.startsWith('.')).map((s) => s.substring(1));
+ }
+
+ private nsSelectorMatch(element: NgView) {
+ return this.nsSelectors.some((s) => s.match(element));
+ }
+
+ private classSelectorsMatch(element: NgView) {
+ return this.classSelectors.some((s) => this.hasClass(element, s));
+ }
+
+ // we're using that instead of match for classes
+ // that are dynamically added by the animation engine
+ // such as .ng-trigger, that's added for every :enter view
+ private hasClass(element: NgView, cls: string) {
+ return element && element['$$classes'] && element['$$classes'][cls];
+ }
}
export class NativeScriptAnimationDriver implements AnimationDriver {
- private static validProperties = [
- ...CssAnimationProperty._getPropertyNames(),
- "transform",
- ];
-
- validateStyleProperty(property: string): boolean {
- traceLog(`CssAnimationProperty.validateStyleProperty: ${property}`);
- return NativeScriptAnimationDriver.validProperties.indexOf(property) !== -1;
- }
-
- matchesElement(element: NgView, rawSelector: string): boolean {
- traceLog(
- `NativeScriptAnimationDriver.matchesElement ` +
- `element: ${element}, selector: ${rawSelector}`
- );
-
- const selector = this.makeSelector(rawSelector);
- return selector.match(element);
- }
-
-
- containsElement(elm1: NgView, elm2: NgView): boolean {
- traceLog(
- `NativeScriptAnimationDriver.containsElement ` +
- `element1: ${elm1}, element2: ${elm2}`
- );
-
- // Checking if the parent is our fake body object
- if (elm1["isOverride"]) {
- return true;
- }
-
- const params: ViewMatchParams = { originalView: elm2 };
- const result: ViewMatchResult = this.visitDescendants(elm1, viewMatches, params);
-
- return result.found;
- }
-
- query(element: NgView, rawSelector: string, multi: boolean): NgView[] {
- traceLog(
- `NativeScriptAnimationDriver.query ` +
- `element: ${element}, selector: ${rawSelector} ` +
- `multi: ${multi}`
- );
-
- const selector = this.makeSelector(rawSelector);
- const params: QueryParams = { selector, multi };
- const result: QueryResult = this.visitDescendants(element, queryDescendants, params);
-
- return result.matches || [];
- }
-
- computeStyle(element: NgView, prop: string): string {
- traceLog(
- `NativeScriptAnimationDriver.computeStyle ` +
- `element: ${element}, prop: ${prop}`
- );
-
- const camelCaseProp = dashCaseToCamelCase(prop);
- return element.style[camelCaseProp];
- }
-
- animate(
- element: NgView,
- keyframes: Keyframe[],
- duration: number,
- delay: number,
- easing: string
- ): AnimationPlayer {
- traceLog(
- `NativeScriptAnimationDriver.animate ` +
- `element: ${element}, keyframes: ${keyframes} ` +
- `duration: ${duration}, delay: ${delay} ` +
- `easing: ${easing}`
- );
-
- return new NativeScriptAnimationPlayer(
- element, keyframes, duration, delay, easing);
- }
-
- private makeSelector(rawSelector: string): Selector {
- return new Selector(rawSelector);
- }
-
- private visitDescendants(
- element: NgView,
- cb: (child: NgView, result: any, params: any) => boolean,
- cbParams: any): any {
-
- const result = {};
- // fill the result obj with the result from the callback function
- eachDescendant(element, (child: NgView) => cb(child, result, cbParams));
-
- return result;
- }
-}
+ private static validProperties = [...CssAnimationProperty._getPropertyNames(), 'transform'];
+
+ validateStyleProperty(property: string): boolean {
+ NativeScriptDebug.animationsLog(`CssAnimationProperty.validateStyleProperty: ${property}`);
+ return NativeScriptAnimationDriver.validProperties.indexOf(property) !== -1;
+ }
+
+ matchesElement(element: NgView, rawSelector: string): boolean {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationDriver.matchesElement ` + `element: ${element}, selector: ${rawSelector}`);
+
+ const selector = this.makeSelector(rawSelector);
+ return selector.match(element);
+ }
+
+ containsElement(elm1: NgView, elm2: NgView): boolean {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationDriver.containsElement ` + `element1: ${elm1}, element2: ${elm2}`);
+
+ // Checking if the parent is our fake body object
+ if (elm1['isOverride']) {
+ return true;
+ }
+
+ const params: ViewMatchParams = { originalView: elm2 };
+ const result: ViewMatchResult = this.visitDescendants(elm1, viewMatches, params);
+
+ return result.found;
+ }
+
+ query(element: NgView, rawSelector: string, multi: boolean): NgView[] {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationDriver.query ` + `element: ${element}, selector: ${rawSelector} ` + `multi: ${multi}`);
-function viewMatches(
- element: NgView,
- result: ViewMatchResult,
- params: ViewMatchParams
-): boolean {
+ const selector = this.makeSelector(rawSelector);
+ const params: QueryParams = { selector, multi };
+ const result: QueryResult = this.visitDescendants(element, queryDescendants, params);
+
+ return result.matches || [];
+ }
+
+ computeStyle(element: NgView, prop: string): string {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationDriver.computeStyle ` + `element: ${element}, prop: ${prop}`);
+
+ const camelCaseProp = dashCaseToCamelCase(prop);
+ return element.style[camelCaseProp];
+ }
+
+ animate(element: NgView, keyframes: Keyframe[], duration: number, delay: number, easing: string): AnimationPlayer {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationDriver.animate ` + `element: ${element}, keyframes: ${keyframes} ` + `duration: ${duration}, delay: ${delay} ` + `easing: ${easing}`);
+
+ return new NativeScriptAnimationPlayer(element, keyframes, duration, delay, easing);
+ }
+
+ private makeSelector(rawSelector: string): Selector {
+ return new Selector(rawSelector);
+ }
+
+ private visitDescendants(element: NgView, cb: (child: NgView, result: any, params: any) => boolean, cbParams: any): any {
+ const result = {};
+ // fill the result obj with the result from the callback function
+ eachDescendant(element, (child: NgView) => cb(child, result, cbParams));
+
+ return result;
+ }
+}
- if (element === params.originalView) {
- result.found = true;
- }
+function viewMatches(element: NgView, result: ViewMatchResult, params: ViewMatchParams): boolean {
+ if (element === params.originalView) {
+ result.found = true;
+ }
- return !result.found;
+ return !result.found;
}
-function queryDescendants(
- element: NgView,
- result: QueryResult,
- params: QueryParams
-): boolean {
-
- if (!result.matches) {
- result.matches = [];
- }
-
- const { selector, multi } = params;
-
- // skip comment and text nodes
- // because they are not actual Views
- // and cannot be animated
- if (element instanceof InvisibleNode || !selector.match(element)) {
- return true;
- }
-
- if (element instanceof ProxyViewContainer) {
- element.eachChild((child: NgView) => {
- result.matches.push(child);
- return true;
- });
- } else {
- result.matches.push(element);
- }
-
- return multi;
+function queryDescendants(element: NgView, result: QueryResult, params: QueryParams): boolean {
+ if (!result.matches) {
+ result.matches = [];
+ }
+
+ const { selector, multi } = params;
+
+ // skip comment and text nodes
+ // because they are not actual Views
+ // and cannot be animated
+ if (element instanceof InvisibleNode || !selector.match(element)) {
+ return true;
+ }
+
+ if (element instanceof ProxyViewContainer) {
+ element.eachChild((child: NgView) => {
+ result.matches.push(child);
+ return true;
+ });
+ } else {
+ result.matches.push(element);
+ }
+
+ return multi;
}
diff --git a/nativescript-angular/animations/animation-player.ts b/nativescript-angular/animations/animation-player.ts
index 831fa4517..d8fe447b7 100644
--- a/nativescript-angular/animations/animation-player.ts
+++ b/nativescript-angular/animations/animation-player.ts
@@ -1,142 +1,139 @@
-import { AnimationPlayer } from "@angular/animations";
-import { KeyframeAnimation }
- from "@nativescript/core/ui/animation/keyframe-animation";
-import { View, EventData } from "@nativescript/core/ui/core/view";
+import { AnimationPlayer } from '@angular/animations';
+import { View, EventData, KeyframeAnimation } from '@nativescript/core';
-import { Keyframe, createKeyframeAnimation } from "./utils";
-import { NgView } from "../element-registry";
-import { animationsLog as traceLog, isLogEnabled } from "../trace";
+import { Keyframe, createKeyframeAnimation } from './utils';
+import { NgView } from '../element-registry';
+import { NativeScriptDebug } from '../trace';
export class NativeScriptAnimationPlayer implements AnimationPlayer {
- public parentPlayer: AnimationPlayer = null;
-
- private _startSubscriptions: Function[] = [];
- private _doneSubscriptions: Function[] = [];
- private _finished = false;
- private _started = false;
- private animation: KeyframeAnimation;
-
- constructor(
- private target: NgView,
- keyframes: Keyframe[],
- private duration: number,
- private delay: number,
- easing: string
- ) {
- this.initKeyframeAnimation(keyframes, duration, delay, easing);
- }
-
- get totalTime(): number {
- return this.delay + this.duration;
- }
-
- init(): void {
- }
-
- hasStarted(): boolean {
- return this._started;
- }
-
- onStart(fn: Function): void { this._startSubscriptions.push(fn); }
- onDone(fn: Function): void { this._doneSubscriptions.push(fn); }
- onDestroy(fn: Function): void { this._doneSubscriptions.push(fn); }
-
- play(): void {
- if (isLogEnabled()) {
- traceLog(`NativeScriptAnimationPlayer.play`);
- }
-
- if (!this.animation) {
- return;
- }
-
- if (!this._started) {
- this._started = true;
- this._startSubscriptions.forEach(fn => fn());
- this._startSubscriptions = [];
- }
-
- // When this issue https://github.com/NativeScript/NativeScript/issues/7984 is fixes in @nativescript/core
- // we can change this fix and apply the one that is recommended in that issue.
- if (this.target.isLoaded) {
- this.playAnimation();
- } else {
- this.target.on(View.loadedEvent, this.onTargetLoaded.bind(this));
- }
- }
-
- private onTargetLoaded(args: EventData) {
- this.target.off(View.loadedEvent, this.onTargetLoaded);
- this.playAnimation();
- }
-
- private playAnimation() {
- this.animation.play(this.target)
- .then(() => this.onFinish())
- .catch((_e) => {});
- }
-
- pause(): void {
- }
-
- finish(): void {
- this.onFinish();
- }
-
- reset(): void {
- if (isLogEnabled()) {
- traceLog(`NativeScriptAnimationPlayer.reset`);
- }
-
- if (this.animation && this.animation.isPlaying) {
- this.animation.cancel();
- }
- }
-
- restart(): void {
- if (isLogEnabled()) {
- traceLog(`NativeScriptAnimationPlayer.restart`);
- }
-
- this.reset();
- this.play();
- }
-
- destroy(): void {
- if (isLogEnabled()) {
- traceLog(`NativeScriptAnimationPlayer.destroy`);
- }
- this.onFinish();
- }
-
- setPosition(_p: any): void {
- throw new Error("AnimationPlayer.setPosition method is not supported!");
- }
-
- getPosition(): number {
- return 0;
- }
-
- private initKeyframeAnimation(keyframes: Keyframe[], duration: number, delay: number, easing: string) {
- if (isLogEnabled()) {
- traceLog(`NativeScriptAnimationPlayer.initKeyframeAnimation`);
- }
-
- this.animation = createKeyframeAnimation(keyframes, duration, delay, easing);
- }
-
- private onFinish() {
- if (isLogEnabled()) {
- traceLog(`NativeScriptAnimationPlayer.onFinish`);
- }
-
- if (this._finished) {
- return;
- }
-
- this._finished = true;
- this._started = false;
- this._doneSubscriptions.forEach(fn => fn());
- this._doneSubscriptions = [];
- }
+ public parentPlayer: AnimationPlayer = null;
+
+ private _startSubscriptions: Function[] = [];
+ private _doneSubscriptions: Function[] = [];
+ private _finished = false;
+ private _started = false;
+ private animation: KeyframeAnimation;
+
+ constructor(private target: NgView, keyframes: Keyframe[], private duration: number, private delay: number, easing: string) {
+ this.initKeyframeAnimation(keyframes, duration, delay, easing);
+ }
+
+ get totalTime(): number {
+ return this.delay + this.duration;
+ }
+
+ init(): void {}
+
+ hasStarted(): boolean {
+ return this._started;
+ }
+
+ onStart(fn: Function): void {
+ this._startSubscriptions.push(fn);
+ }
+ onDone(fn: Function): void {
+ this._doneSubscriptions.push(fn);
+ }
+ onDestroy(fn: Function): void {
+ this._doneSubscriptions.push(fn);
+ }
+
+ play(): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationPlayer.play`);
+ }
+
+ if (!this.animation) {
+ return;
+ }
+
+ if (!this._started) {
+ this._started = true;
+ this._startSubscriptions.forEach((fn) => fn());
+ this._startSubscriptions = [];
+ }
+
+ // When this issue https://github.com/NativeScript/NativeScript/issues/7984 is fixes in @nativescript/core
+ // we can change this fix and apply the one that is recommended in that issue.
+ if (this.target.isLoaded) {
+ this.playAnimation();
+ } else {
+ this.target.on(View.loadedEvent, this.onTargetLoaded.bind(this));
+ }
+ }
+
+ private onTargetLoaded(args: EventData) {
+ this.target.off(View.loadedEvent, this.onTargetLoaded);
+ this.playAnimation();
+ }
+
+ private playAnimation() {
+ this.animation
+ .play(this.target)
+ .then(() => this.onFinish())
+ .catch((_e) => {});
+ }
+
+ pause(): void {}
+
+ finish(): void {
+ this.onFinish();
+ }
+
+ reset(): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationPlayer.reset`);
+ }
+
+ if (this.animation && this.animation.isPlaying) {
+ this.animation.cancel();
+ }
+ }
+
+ restart(): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationPlayer.restart`);
+ }
+
+ this.reset();
+ this.play();
+ }
+
+ destroy(): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationPlayer.destroy`);
+ }
+ this.onFinish();
+ }
+
+ setPosition(_p: any): void {
+ throw new Error('AnimationPlayer.setPosition method is not supported!');
+ }
+
+ getPosition(): number {
+ return 0;
+ }
+
+ private initKeyframeAnimation(keyframes: Keyframe[], duration: number, delay: number, easing: string) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationPlayer.initKeyframeAnimation`);
+ }
+
+ this.animation = createKeyframeAnimation(keyframes, duration, delay, easing);
+ }
+
+ private onFinish() {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.animationsLog(`NativeScriptAnimationPlayer.onFinish`);
+ }
+
+ if (this._finished) {
+ return;
+ }
+
+ this._finished = true;
+ this._started = false;
+ this._doneSubscriptions.forEach((fn) => fn());
+ this._doneSubscriptions = [];
+ }
}
diff --git a/nativescript-angular/animations/animations.module.ts b/nativescript-angular/animations/animations.module.ts
index b33503ff4..517773300 100644
--- a/nativescript-angular/animations/animations.module.ts
+++ b/nativescript-angular/animations/animations.module.ts
@@ -1,85 +1,59 @@
-import {
- NgModule,
- Injectable,
- Inject,
- NgZone,
- RendererFactory2,
- Optional,
- SkipSelf,
-} from "@angular/core";
-import { DOCUMENT } from "@angular/common";
-import { AnimationBuilder } from "@angular/animations";
+import { NgModule, Injectable, Inject, NgZone, RendererFactory2, Optional, SkipSelf } from '@angular/core';
+import { DOCUMENT } from '@angular/common';
+import { AnimationBuilder } from '@angular/animations';
-import {
- AnimationDriver,
- ɵAnimationStyleNormalizer as AnimationStyleNormalizer,
- ɵWebAnimationsStyleNormalizer as WebAnimationsStyleNormalizer,
- ɵAnimationEngine as AnimationEngine,
-} from "@angular/animations/browser";
+import { AnimationDriver, ɵAnimationStyleNormalizer as AnimationStyleNormalizer, ɵWebAnimationsStyleNormalizer as WebAnimationsStyleNormalizer, ɵAnimationEngine as AnimationEngine } from '@angular/animations/browser';
-import {
- ɵAnimationRendererFactory as AnimationRendererFactory,
- ɵBrowserAnimationBuilder as BrowserAnimationBuilder,
-} from "@angular/platform-browser/animations";
+import { ɵAnimationRendererFactory as AnimationRendererFactory, ɵBrowserAnimationBuilder as BrowserAnimationBuilder } from '@angular/platform-browser/animations';
// import { NativeScriptModule } from "../nativescript.module";
-import { NativeScriptRendererFactory } from "../renderer";
-import { NativeScriptAnimationDriver } from "./animation-driver";
-import { throwIfAlreadyLoaded } from "../common/utils";
-import { NativeScriptCommonModule } from "../common";
+import { NativeScriptRendererFactory } from '../renderer-factory';
+import { NativeScriptAnimationDriver } from './animation-driver';
+import { throwIfAlreadyLoaded } from '../common/utils';
+import { NativeScriptCommonModule } from '../common';
@Injectable()
export class InjectableAnimationEngine extends AnimationEngine {
- constructor(
- @Inject(DOCUMENT) doc: any,
- driver: AnimationDriver,
- normalizer: AnimationStyleNormalizer
- ) {
- super(doc.body, driver, normalizer);
- }
+ constructor(@Inject(DOCUMENT) doc: any, driver: AnimationDriver, normalizer: AnimationStyleNormalizer) {
+ super(doc.body, driver, normalizer);
+ }
}
export function instantiateSupportedAnimationDriver() {
- return new NativeScriptAnimationDriver();
+ return new NativeScriptAnimationDriver();
}
-export function instantiateRendererFactory(
- renderer: NativeScriptRendererFactory,
- engine: AnimationEngine,
- zone: NgZone
-) {
- return new AnimationRendererFactory(renderer, engine, zone);
+export function instantiateRendererFactory(renderer: NativeScriptRendererFactory, engine: AnimationEngine, zone: NgZone) {
+ return new AnimationRendererFactory(renderer, engine, zone);
}
export function instantiateDefaultStyleNormalizer() {
- return new WebAnimationsStyleNormalizer();
+ return new WebAnimationsStyleNormalizer();
}
@NgModule({
- imports: [NativeScriptCommonModule],
- providers: [
- {
- provide: AnimationDriver,
- useFactory: instantiateSupportedAnimationDriver,
- },
- { provide: AnimationBuilder, useClass: BrowserAnimationBuilder },
- {
- provide: AnimationStyleNormalizer,
- useFactory: instantiateDefaultStyleNormalizer,
- },
- { provide: AnimationEngine, useClass: InjectableAnimationEngine },
- {
- provide: RendererFactory2,
- useFactory: instantiateRendererFactory,
- deps: [NativeScriptRendererFactory, AnimationEngine, NgZone],
- },
- ],
+ imports: [NativeScriptCommonModule],
+ providers: [
+ {
+ provide: AnimationDriver,
+ useFactory: instantiateSupportedAnimationDriver,
+ },
+ { provide: AnimationBuilder, useClass: BrowserAnimationBuilder },
+ {
+ provide: AnimationStyleNormalizer,
+ useFactory: instantiateDefaultStyleNormalizer,
+ },
+ { provide: AnimationEngine, useClass: InjectableAnimationEngine },
+ {
+ provide: RendererFactory2,
+ useFactory: instantiateRendererFactory,
+ deps: [NativeScriptRendererFactory, AnimationEngine, NgZone],
+ },
+ ],
})
export class NativeScriptAnimationsModule {
- constructor(
- @Optional() @SkipSelf() parentModule: NativeScriptAnimationsModule
- ) {
- // Prevents NativeScriptAnimationsModule from getting imported multiple times
- throwIfAlreadyLoaded(parentModule, "NativeScriptAnimationsModule");
- }
+ constructor(@Optional() @SkipSelf() parentModule: NativeScriptAnimationsModule) {
+ // Prevents NativeScriptAnimationsModule from getting imported multiple times
+ throwIfAlreadyLoaded(parentModule, 'NativeScriptAnimationsModule');
+ }
}
diff --git a/nativescript-angular/animations/index.ts b/nativescript-angular/animations/index.ts
index 2b54a81e9..04e987533 100644
--- a/nativescript-angular/animations/index.ts
+++ b/nativescript-angular/animations/index.ts
@@ -1,4 +1,4 @@
-export * from "./animations.module";
-export * from "./animation-player";
-export * from "./animation-driver";
-export * from "./utils";
+export * from './animations.module';
+export * from './animation-player';
+export * from './animation-driver';
+export * from './utils';
diff --git a/nativescript-angular/animations/utils.ts b/nativescript-angular/animations/utils.ts
index c00593283..4eecb6fe2 100644
--- a/nativescript-angular/animations/utils.ts
+++ b/nativescript-angular/animations/utils.ts
@@ -1,59 +1,39 @@
-import {
- KeyframeAnimation,
- KeyframeAnimationInfo,
- KeyframeDeclaration,
- KeyframeInfo,
-} from "@nativescript/core/ui/animation/keyframe-animation";
-import { parseKeyframeDeclarations } from "@nativescript/core/ui/styling/css-animation-parser";
-import { animationTimingFunctionConverter } from "@nativescript/core/ui/styling/converters";
+import { KeyframeAnimation, KeyframeAnimationInfo, KeyframeDeclaration, KeyframeInfo, parseKeyframeDeclarations, animationTimingFunctionConverter } from '@nativescript/core';
export interface Keyframe {
- [key: string]: string | number;
- offset: number;
+ [key: string]: string | number;
+ offset: number;
}
const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
export function dashCaseToCamelCase(input: string): string {
- return input.replace(DASH_CASE_REGEXP, (...m: any[]) => m[1].toUpperCase());
+ return input.replace(DASH_CASE_REGEXP, (...m: any[]) => m[1].toUpperCase());
}
-export function createKeyframeAnimation(
- styles: Keyframe[],
- duration: number,
- delay: number,
- easing: string)
- : KeyframeAnimation {
-
- const info = createKeyframeAnimationInfo(styles, duration, delay, easing);
- return KeyframeAnimation.keyframeAnimationFromInfo(info);
+export function createKeyframeAnimation(styles: Keyframe[], duration: number, delay: number, easing: string): KeyframeAnimation {
+ const info = createKeyframeAnimationInfo(styles, duration, delay, easing);
+ return KeyframeAnimation.keyframeAnimationFromInfo(info);
}
-const createKeyframeAnimationInfo = (
- styles: Keyframe[],
- duration: number,
- delay: number,
- easing: string
- ): KeyframeAnimationInfo => ({
- isForwards: true,
- duration: duration || 0.01,
- delay,
- curve: getCurve(easing),
- keyframes: styles.map(parseAnimationKeyframe),
- }
-);
+const createKeyframeAnimationInfo = (styles: Keyframe[], duration: number, delay: number, easing: string): KeyframeAnimationInfo => ({
+ isForwards: true,
+ duration: duration || 0.01,
+ delay,
+ curve: getCurve(easing),
+ keyframes: styles.map(parseAnimationKeyframe),
+});
const getCurve = (value: string) => animationTimingFunctionConverter(value);
const parseAnimationKeyframe = (styles: Keyframe): KeyframeInfo => ({
- duration: getKeyframeDuration(styles),
- declarations: getDeclarations(styles),
+ duration: getKeyframeDuration(styles),
+ declarations: getDeclarations(styles),
});
const getKeyframeDuration = (styles: Keyframe): number => styles.offset;
function getDeclarations(styles: Keyframe): KeyframeDeclaration[] {
- const unparsedDeclarations: KeyframeDeclaration[] =
- Object.keys(styles).map(property => ({ property, value: styles[property] }));
+ const unparsedDeclarations: KeyframeDeclaration[] = Object.keys(styles).map((property) => ({ property, value: styles[property] }));
- return parseKeyframeDeclarations(unparsedDeclarations);
+ return parseKeyframeDeclarations(unparsedDeclarations);
}
diff --git a/nativescript-angular/app-host-view.ts b/nativescript-angular/app-host-view.ts
index 1a7b47564..55e91911c 100644
--- a/nativescript-angular/app-host-view.ts
+++ b/nativescript-angular/app-host-view.ts
@@ -1,43 +1,59 @@
-import { ContentView } from "@nativescript/core/ui/content-view";
-import { GridLayout } from "@nativescript/core/ui/layouts/grid-layout";
-import { ProxyViewContainer } from "@nativescript/core/ui/proxy-view-container";
-import { View } from "@nativescript/core/ui/core/view";
+import { ContentView, View, ProxyViewContainer, GridLayout, Color } from '@nativescript/core';
export class AppHostView extends ContentView {
+ private _ngAppRoot: View;
+ private _content: View;
+
+ constructor(backgroundColor: Color) {
+ super();
+ this.backgroundColor = backgroundColor;
+ }
+
+ get ngAppRoot(): View {
+ return this._ngAppRoot;
+ }
+
+ set ngAppRoot(value: View) {
+ this._ngAppRoot = value;
+ }
+
+ get content(): View {
+ return this._content;
+ }
+
+ set content(value: View) {
+ if (this._content) {
+ this._content.parentNode = undefined;
+ }
+
+ this._content = value;
+
+ if (value) {
+ this._content.parentNode = this;
+ }
+
+ this.ngAppRoot = value;
+
+ if (this._content instanceof ProxyViewContainer) {
+ const grid = new GridLayout();
+ grid.backgroundColor = this.backgroundColor;
+ grid.addChild(this._content);
+ this.ngAppRoot = grid;
+ }
+ }
+}
- private _ngAppRoot: View;
- private _content: View;
-
- get ngAppRoot(): View {
- return this._ngAppRoot;
- }
-
- set ngAppRoot(value: View) {
- this._ngAppRoot = value;
- }
-
- get content(): View {
- return this._content;
- }
-
- set content(value: View) {
- if (this._content) {
- this._content.parentNode = undefined;
- }
-
- this._content = value;
-
- if (value) {
- this._content.parentNode = this;
- }
-
- this.ngAppRoot = value;
+export class AppHostAsyncView extends GridLayout {
+ constructor(backgroundColor: Color) {
+ super();
+ this.backgroundColor = backgroundColor;
+ }
- if (this._content instanceof ProxyViewContainer) {
- const grid = new GridLayout();
- grid.addChild(this._content);
- this.ngAppRoot = grid;
- }
- }
+ get ngAppRoot(): View {
+ return this;
+ }
+ set ngAppRoot(value: View) {
+ // ignored
+ }
}
diff --git a/nativescript-angular/bin/update-app-ng-deps b/nativescript-angular/bin/update-app-ng-deps
deleted file mode 100755
index 043e57edf..000000000
--- a/nativescript-angular/bin/update-app-ng-deps
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/usr/bin/env node
-
-const path = require("path");
-const fs = require("fs");
-
-const binPath = __dirname;
-const pluginPath = path.dirname(binPath);
-const pluginPackageJsonPath = path.join(pluginPath, "package.json");
-const pluginPackageJson = JSON.parse(fs.readFileSync(pluginPackageJsonPath, "utf8"));
-
-const isNgDependency = name => name.startsWith("@angular") ||
- name === "rxjs" ||
- name === "zone.js" ||
- name === "typescript";
-
-function updateDeps(deps, devDeps, newDeps) {
- Object.keys(newDeps)
- .filter(isNgDependency)
- .map(dependencyName => ({
- dependencyName,
- version: newDeps[dependencyName]
- }))
- .forEach(({ dependencyName, version }) => {
- if (devDeps[dependencyName]) {
- devDeps[dependencyName] = version;
- } else {
- deps[dependencyName] = version;
- }
-
- logUpdatedDependency(dependencyName, version);
- });
-
- const ngDep = Object.keys(newDeps).find(dep => dep.startsWith("@angular"));
- const version = newDeps[ngDep];
- const animationsDependency = "@angular/animations";
- if (deps[animationsDependency]) {
- deps[animationsDependency] = version;
- logUpdatedDependency(animationsDependency, version);
- }
-
- const compilerCliDependency = "@angular/compiler-cli";
- if (devDeps[compilerCliDependency]) {
- devDeps[compilerCliDependency] = version;
- logUpdatedDependency(compilerCliDependency, version);
- }
-
- const ngToolsWebpackDependency = "@ngtools/webpack";
- if (devDeps[ngToolsWebpackDependency]) {
- devDeps[ngToolsWebpackDependency] = version;
- logUpdatedDependency(ngToolsWebpackDependency, version);
- }
-
- function logUpdatedDependency(dependencyName, version) {
- console.log(`Updated dependency ${dependencyName} to version: ${version}.`);
- }
-}
-
-const pluginDeps = pluginPackageJson.peerDependencies;
-const projectPath = process.env.INIT_CWD || path.dirname(path.dirname(path.dirname(pluginPath)));
-const appPackageJsonPath = path.join(projectPath, "package.json");
-const appPackageJson = JSON.parse(fs.readFileSync(appPackageJsonPath, "utf8"));
-
-const { dependencies, devDependencies } = appPackageJson;
-updateDeps(dependencies, devDependencies, pluginDeps);
-
-fs.writeFileSync(appPackageJsonPath, JSON.stringify(appPackageJson, null, 2));
-
-console.log("\nAngular dependencies updated. Don't forget to run `npm install`.");
diff --git a/nativescript-angular/bin/update-app-ng-deps.cmd b/nativescript-angular/bin/update-app-ng-deps.cmd
deleted file mode 100644
index a5e120718..000000000
--- a/nativescript-angular/bin/update-app-ng-deps.cmd
+++ /dev/null
@@ -1 +0,0 @@
-@node %~dp0\update-app-angular-deps %*
diff --git a/nativescript-angular/common.ts b/nativescript-angular/common.ts
index 19306bbe3..81f6dd6e2 100644
--- a/nativescript-angular/common.ts
+++ b/nativescript-angular/common.ts
@@ -1,61 +1,21 @@
-import { CommonModule } from "@angular/common";
-import { NO_ERRORS_SCHEMA, NgModule } from "@angular/core";
+import { CommonModule } from '@angular/common';
+import { NO_ERRORS_SCHEMA, NgModule } from '@angular/core';
-import { ModalDialogService } from "./directives/dialogs";
-import {
- defaultDeviceProvider,
- defaultFrameProvider,
- defaultPageProvider,
-} from "./platform-providers";
-import { ListViewComponent } from "./directives/list-view-comp";
-import { TemplateKeyDirective } from "./directives/templated-items-comp";
-import { TabViewDirective, TabViewItemDirective } from "./directives/tab-view";
-import {
- ActionBarComponent,
- ActionBarScope,
- ActionItemDirective,
- NavigationButtonDirective,
-} from "./directives/action-bar";
-import {
- AndroidFilterComponent,
- IosFilterComponent,
-} from "./directives/platform-filters";
+import { ModalDialogService } from './directives/dialogs';
+import { defaultDeviceProvider, defaultFrameProvider, defaultPageProvider } from './platform-providers';
+import { ListViewComponent } from './directives/list-view-comp';
+import { TemplateKeyDirective } from './directives/templated-items-comp';
+import { TabViewDirective, TabViewItemDirective } from './directives/tab-view';
+import { ActionBarComponent, ActionBarScope, ActionItemDirective, NavigationButtonDirective } from './directives/action-bar';
+import { AndroidFilterComponent, IosFilterComponent } from './directives/platform-filters';
@NgModule({
- declarations: [
- ListViewComponent,
- TemplateKeyDirective,
- TabViewDirective,
- TabViewItemDirective,
- ActionBarComponent,
- ActionBarScope,
- ActionItemDirective,
- NavigationButtonDirective,
- AndroidFilterComponent,
- IosFilterComponent,
- ],
- providers: [
- ModalDialogService,
- defaultDeviceProvider,
- defaultFrameProvider,
- defaultPageProvider,
- ],
- imports: [CommonModule],
- exports: [
- CommonModule,
- ListViewComponent,
- TemplateKeyDirective,
- TabViewDirective,
- TabViewItemDirective,
- ActionBarComponent,
- ActionBarScope,
- ActionItemDirective,
- NavigationButtonDirective,
- AndroidFilterComponent,
- IosFilterComponent,
- ],
- schemas: [NO_ERRORS_SCHEMA],
+ declarations: [ListViewComponent, TemplateKeyDirective, TabViewDirective, TabViewItemDirective, ActionBarComponent, ActionBarScope, ActionItemDirective, NavigationButtonDirective, AndroidFilterComponent, IosFilterComponent],
+ providers: [ModalDialogService, defaultDeviceProvider, defaultFrameProvider, defaultPageProvider],
+ imports: [CommonModule],
+ exports: [CommonModule, ListViewComponent, TemplateKeyDirective, TabViewDirective, TabViewItemDirective, ActionBarComponent, ActionBarScope, ActionItemDirective, NavigationButtonDirective, AndroidFilterComponent, IosFilterComponent],
+ schemas: [NO_ERRORS_SCHEMA],
})
export class NativeScriptCommonModule {}
-export * from "./directives";
+export * from './directives';
diff --git a/nativescript-angular/common/detached-loader.ts b/nativescript-angular/common/detached-loader.ts
index 4069b4d71..a2dab3643 100644
--- a/nativescript-angular/common/detached-loader.ts
+++ b/nativescript-angular/common/detached-loader.ts
@@ -1,57 +1,43 @@
-import {
- ComponentRef, ComponentFactory, ViewContainerRef,
- Component, Type, ComponentFactoryResolver, ChangeDetectorRef
-} from "@angular/core";
-import { write } from "@nativescript/core/trace";
-
-export const CATEGORY = "detached-loader";
-function log(message: string) {
- write(message, CATEGORY);
-}
-
-
-/**
- * Wrapper component used for loading components when navigating
- * It uses DetachedContainer as selector so that it is containerRef is not attached to
- * the visual tree.
- */
-@Component({
- selector: "DetachedContainer",
- template: ``
-})
-export class DetachedLoader { // tslint:disable-line:component-class-suffix
- constructor(
- private resolver: ComponentFactoryResolver,
- private changeDetector: ChangeDetectorRef,
- private containerRef: ViewContainerRef
- ) { }
-
- private loadInLocation(componentType: Type): Promise> {
- const factory = this.resolver.resolveComponentFactory(componentType);
- const componentRef = this.containerRef.createComponent(
- factory, this.containerRef.length, this.containerRef.injector);
-
- // Component is created, built may not be checked if we are loading
- // inside component with OnPush CD strategy. Mark us for check to be sure CD will reach us.
- // We are inside a promise here so no need for setTimeout - CD should trigger
- // after the promise.
- log("DetachedLoader.loadInLocation component loaded -> markForCheck");
-
- return Promise.resolve(componentRef);
- }
-
- public detectChanges() {
- this.changeDetector.markForCheck();
- }
-
- // TODO: change this API -- async promises not needed here anymore.
- public loadComponent(componentType: Type): Promise> {
- log("DetachedLoader.loadComponent");
- return this.loadInLocation(componentType);
- }
-
- public loadWithFactory(factory: ComponentFactory): ComponentRef {
- return this.containerRef.createComponent(factory,
- this.containerRef.length, this.containerRef.injector, null);
- }
-}
+import { ComponentRef, ComponentFactory, ViewContainerRef, Component, Type, ComponentFactoryResolver, ChangeDetectorRef } from '@angular/core';
+import { Trace } from '@nativescript/core';
+
+/**
+ * Wrapper component used for loading components when navigating
+ * It uses DetachedContainer as selector so that it is containerRef is not attached to
+ * the visual tree.
+ */
+@Component({
+ selector: 'DetachedContainer',
+ template: ``,
+})
+export class DetachedLoader {
+ // tslint:disable-line:component-class-suffix
+ constructor(private resolver: ComponentFactoryResolver, private changeDetector: ChangeDetectorRef, private containerRef: ViewContainerRef) {}
+
+ private loadInLocation(componentType: Type): Promise> {
+ const factory = this.resolver.resolveComponentFactory(componentType);
+ const componentRef = this.containerRef.createComponent(factory, this.containerRef.length, this.containerRef.injector);
+
+ // Component is created, built may not be checked if we are loading
+ // inside component with OnPush CD strategy. Mark us for check to be sure CD will reach us.
+ // We are inside a promise here so no need for setTimeout - CD should trigger
+ // after the promise.
+ Trace.write('DetachedLoader.loadInLocation component loaded -> markForCheck', 'detached-loader');
+
+ return Promise.resolve(componentRef);
+ }
+
+ public detectChanges() {
+ this.changeDetector.markForCheck();
+ }
+
+ // TODO: change this API -- async promises not needed here anymore.
+ public loadComponent(componentType: Type): Promise> {
+ Trace.write('DetachedLoader.loadComponent', 'detached-loader');
+ return this.loadInLocation(componentType);
+ }
+
+ public loadWithFactory(factory: ComponentFactory): ComponentRef {
+ return this.containerRef.createComponent(factory, this.containerRef.length, this.containerRef.injector, null);
+ }
+}
diff --git a/nativescript-angular/common/utils.ts b/nativescript-angular/common/utils.ts
index 5cee9f9ba..5525ca7da 100644
--- a/nativescript-angular/common/utils.ts
+++ b/nativescript-angular/common/utils.ts
@@ -1,21 +1,27 @@
-export function throwIfAlreadyLoaded(
- parentModule: any,
- moduleName: string,
-) {
- if (parentModule) {
- throw new Error(`${moduleName} has already been loaded. Import ${moduleName} in the AppModule only.`);
- }
+/**
+ * Utility method to ensure a NgModule is only imported once in a codebase, otherwise will throw to help prevent accidental double importing
+ * @param parentModule Parent module name
+ * @param moduleName The module name
+ */
+export function throwIfAlreadyLoaded(parentModule: any, moduleName: string) {
+ if (parentModule) {
+ throw new Error(`${moduleName} has already been loaded. Import ${moduleName} in the AppModule only.`);
+ }
}
+/**
+ * Utility method which will only fire the callback once ever
+ * @param fn callback to call only once
+ */
export function once(fn: Function) {
- let wasCalled = false;
+ let wasCalled = false;
- return function wrapper() {
- if (wasCalled) {
- return;
- }
+ return function wrapper() {
+ if (wasCalled) {
+ return;
+ }
- wasCalled = true;
- fn.apply(null, arguments);
- };
+ wasCalled = true;
+ fn.apply(null, arguments);
+ };
}
diff --git a/nativescript-angular/directives/action-bar.ts b/nativescript-angular/directives/action-bar.ts
index eb37f818d..a6e20e937 100644
--- a/nativescript-angular/directives/action-bar.ts
+++ b/nativescript-angular/directives/action-bar.ts
@@ -1,172 +1,160 @@
-import { Directive, Component, ElementRef, Optional, OnDestroy } from "@angular/core";
-import {
- ActionBar,
- ActionItem,
- ActionItems,
- NavigationButton,
-} from "@nativescript/core/ui/action-bar";
-import { Page } from "@nativescript/core/ui/page";
-
-import { isBlank } from "../lang-facade";
-import {
- NgView,
- ViewClassMeta,
- ViewExtensions,
- isInvisibleNode,
- isView,
- registerElement,
-} from "../element-registry";
+import { Directive, Component, ElementRef, Optional, OnDestroy } from '@angular/core';
+import { ActionBar, ActionItem, ActionItems, NavigationButton, Page } from '@nativescript/core';
+
+import { isBlank } from '../lang-facade';
+import { NgView, ViewClassMeta, ViewExtensions, isInvisibleNode, isView, registerElement } from '../element-registry';
export function isActionItem(view: any): view is ActionItem {
- return view instanceof ActionItem;
+ return view instanceof ActionItem;
}
export function isNavigationButton(view: any): view is NavigationButton {
- return view instanceof NavigationButton;
+ return view instanceof NavigationButton;
}
-type NgActionBar = (ActionBar & ViewExtensions);
+type NgActionBar = ActionBar & ViewExtensions;
const actionBarMeta: ViewClassMeta = {
- skipAddToDom: true,
- insertChild: (parent: NgActionBar, child: NgView, next: any) => {
- if (isInvisibleNode(child)) {
- return;
- } else if (isNavigationButton(child)) {
- parent.navigationButton = child;
- child.parentNode = parent;
- } else if (isActionItem(child)) {
- addActionItem(parent, child, next);
- child.parentNode = parent;
- } else if (isView(child)) {
- parent.titleView = child;
- }
- },
- removeChild: (parent: NgActionBar, child: NgView) => {
- if (isInvisibleNode(child)) {
- return;
- } else if (isNavigationButton(child)) {
- if (parent.navigationButton === child) {
- parent.navigationButton = null;
- }
-
- child.parentNode = null;
- } else if (isActionItem(child)) {
- parent.actionItems.removeItem(child);
- child.parentNode = null;
- } else if (isView(child) && parent.titleView && parent.titleView === child) {
- parent.titleView = null;
- }
- },
+ skipAddToDom: true,
+ insertChild: (parent: NgActionBar, child: NgView, next: any) => {
+ if (isInvisibleNode(child)) {
+ return;
+ } else if (isNavigationButton(child)) {
+ parent.navigationButton = child;
+ child.parentNode = parent;
+ } else if (isActionItem(child)) {
+ addActionItem(parent, child, next);
+ child.parentNode = parent;
+ } else if (isView(child)) {
+ parent.titleView = child;
+ }
+ },
+ removeChild: (parent: NgActionBar, child: NgView) => {
+ if (isInvisibleNode(child)) {
+ return;
+ } else if (isNavigationButton(child)) {
+ if (parent.navigationButton === child) {
+ parent.navigationButton = null;
+ }
+
+ child.parentNode = null;
+ } else if (isActionItem(child)) {
+ parent.actionItems.removeItem(child);
+ child.parentNode = null;
+ } else if (isView(child) && parent.titleView && parent.titleView === child) {
+ parent.titleView = null;
+ }
+ },
};
const addActionItem = (bar: NgActionBar, item: ActionItem, next: ActionItem) => {
- if (next) {
- insertActionItemBefore(bar, item, next);
- } else {
- appendActionItem(bar, item);
- }
+ if (next) {
+ insertActionItemBefore(bar, item, next);
+ } else {
+ appendActionItem(bar, item);
+ }
};
const insertActionItemBefore = (bar: NgActionBar, item: ActionItem, next: ActionItem) => {
- const actionItems: ActionItems = bar.actionItems;
- const actionItemsCollection: ActionItem[] = actionItems.getItems();
+ const actionItems: ActionItems = bar.actionItems;
+ const actionItemsCollection: ActionItem[] = actionItems.getItems();
- const indexToInsert = actionItemsCollection.indexOf(next);
- actionItemsCollection.splice(indexToInsert, 0, item);
+ const indexToInsert = actionItemsCollection.indexOf(next);
+ actionItemsCollection.splice(indexToInsert, 0, item);
- (actionItems).setItems(actionItemsCollection);
+ (actionItems).setItems(actionItemsCollection);
};
const appendActionItem = (bar: NgActionBar, item: ActionItem) => {
- bar.actionItems.addItem(item);
+ bar.actionItems.addItem(item);
};
-registerElement("ActionBar", () => ActionBar, actionBarMeta);
-registerElement("ActionItem", () => ActionItem);
-registerElement("NavigationButton", () => NavigationButton);
+registerElement('ActionBar', () => ActionBar, actionBarMeta);
+registerElement('ActionItem', () => ActionItem);
+registerElement('NavigationButton', () => NavigationButton);
@Component({
- selector: "ActionBar",
- template: ""
+ selector: 'ActionBar',
+ template: '',
})
export class ActionBarComponent {
- constructor(public element: ElementRef, private page: Page) {
- if (!this.page) {
- throw new Error("Inside ActionBarComponent but no Page found in DI.");
- }
-
- if (isBlank(this.page.actionBarHidden)) {
- this.page.actionBarHidden = false;
- }
- this.page.actionBar = this.element.nativeElement;
- this.page.actionBar.update();
- }
+ constructor(public element: ElementRef, private page: Page) {
+ if (!this.page) {
+ throw new Error('Inside ActionBarComponent but no Page found in DI.');
+ }
+
+ if (isBlank(this.page.actionBarHidden)) {
+ this.page.actionBarHidden = false;
+ }
+ this.page.actionBar = this.element.nativeElement;
+ this.page.actionBar.update();
+ }
}
@Component({
- selector: "ActionBarExtension",
- template: ""
+ selector: 'ActionBarExtension',
+ template: '',
})
-export class ActionBarScope { // tslint:disable-line:component-class-suffix
- constructor(private page: Page) {
- if (!this.page) {
- throw new Error("Inside ActionBarScope but no Page found in DI.");
- }
- }
-
- public onNavButtonInit(navBtn: NavigationButtonDirective) {
- this.page.actionBar.navigationButton = navBtn.element.nativeElement;
- }
-
- public onNavButtonDestroy(navBtn: NavigationButtonDirective) {
- const nav = navBtn.element.nativeElement;
- if (nav && this.page.actionBar.navigationButton === nav) {
- this.page.actionBar.navigationButton = null;
- }
- }
-
- public onActionInit(item: ActionItemDirective) {
- this.page.actionBar.actionItems.addItem(item.element.nativeElement);
- }
-
- public onActionDestroy(item: ActionItemDirective) {
- if (item.element.nativeElement.actionBar) {
- this.page.actionBar.actionItems.removeItem(item.element.nativeElement);
- }
- }
+export class ActionBarScope {
+ // tslint:disable-line:component-class-suffix
+ constructor(private page: Page) {
+ if (!this.page) {
+ throw new Error('Inside ActionBarScope but no Page found in DI.');
+ }
+ }
+
+ public onNavButtonInit(navBtn: NavigationButtonDirective) {
+ this.page.actionBar.navigationButton = navBtn.element.nativeElement;
+ }
+
+ public onNavButtonDestroy(navBtn: NavigationButtonDirective) {
+ const nav = navBtn.element.nativeElement;
+ if (nav && this.page.actionBar.navigationButton === nav) {
+ this.page.actionBar.navigationButton = null;
+ }
+ }
+
+ public onActionInit(item: ActionItemDirective) {
+ this.page.actionBar.actionItems.addItem(item.element.nativeElement);
+ }
+
+ public onActionDestroy(item: ActionItemDirective) {
+ if (item.element.nativeElement.actionBar) {
+ this.page.actionBar.actionItems.removeItem(item.element.nativeElement);
+ }
+ }
}
@Directive({
- selector: "ActionItem" // tslint:disable-line:directive-selector
+ selector: 'ActionItem', // tslint:disable-line:directive-selector
})
export class ActionItemDirective implements OnDestroy {
- constructor(public element: ElementRef, @Optional() private ownerScope: ActionBarScope) {
- if (this.ownerScope) {
- this.ownerScope.onActionInit(this);
- }
- }
-
- ngOnDestroy() {
- if (this.ownerScope) {
- this.ownerScope.onActionDestroy(this);
- }
- }
+ constructor(public element: ElementRef, @Optional() private ownerScope: ActionBarScope) {
+ if (this.ownerScope) {
+ this.ownerScope.onActionInit(this);
+ }
+ }
+
+ ngOnDestroy() {
+ if (this.ownerScope) {
+ this.ownerScope.onActionDestroy(this);
+ }
+ }
}
@Directive({
- selector: "NavigationButton" // tslint:disable-line:directive-selector
+ selector: 'NavigationButton', // tslint:disable-line:directive-selector
})
export class NavigationButtonDirective implements OnDestroy {
- constructor(public element: ElementRef, @Optional() private ownerScope: ActionBarScope) {
- if (this.ownerScope) {
- this.ownerScope.onNavButtonInit(this);
- }
- }
-
- ngOnDestroy() {
- if (this.ownerScope) {
- this.ownerScope.onNavButtonDestroy(this);
- }
- }
+ constructor(public element: ElementRef, @Optional() private ownerScope: ActionBarScope) {
+ if (this.ownerScope) {
+ this.ownerScope.onNavButtonInit(this);
+ }
+ }
+
+ ngOnDestroy() {
+ if (this.ownerScope) {
+ this.ownerScope.onNavButtonDestroy(this);
+ }
+ }
}
diff --git a/nativescript-angular/directives/dialogs.ts b/nativescript-angular/directives/dialogs.ts
index a7b8e1d62..e9280f989 100644
--- a/nativescript-angular/directives/dialogs.ts
+++ b/nativescript-angular/directives/dialogs.ts
@@ -1,151 +1,130 @@
-import {
- ComponentFactoryResolver,
- ComponentRef,
- Injectable,
- Injector,
- NgModuleRef,
- Type,
- ViewContainerRef
-} from "@angular/core";
-
-import { NSLocationStrategy } from "../router/ns-location-strategy";
-import { View, ViewBase } from "@nativescript/core/ui/core/view";
-import { ProxyViewContainer } from "@nativescript/core/ui/proxy-view-container/proxy-view-container";
-
-import { AppHostView } from "../app-host-view";
-import { DetachedLoader } from "../common/detached-loader";
-import { PageFactory, PAGE_FACTORY } from "../platform-providers";
-import { once } from "../common/utils";
-import { Frame } from "@nativescript/core/ui/frame";
-import { ShowModalOptions } from "@nativescript/core/ui/core/view";
-
-export type BaseShowModalOptions = Pick>;
+import { ComponentFactoryResolver, ComponentRef, Injectable, Injector, NgModuleRef, Type, ViewContainerRef } from '@angular/core';
+import { Frame, View, ViewBase, ProxyViewContainer, ShowModalOptions } from '@nativescript/core';
+
+import { NSLocationStrategy } from '../router/ns-location-strategy';
+import { AppHostView, AppHostAsyncView } from '../app-host-view';
+import { DetachedLoader } from '../common/detached-loader';
+import { PageFactory, PAGE_FACTORY } from '../platform-providers';
+import { once } from '../common/utils';
+
+export type BaseShowModalOptions = Pick>;
export interface ModalDialogOptions extends BaseShowModalOptions {
- context?: any;
- viewContainerRef?: ViewContainerRef;
- moduleRef?: NgModuleRef;
- target?: View;
+ context?: any;
+ viewContainerRef?: ViewContainerRef;
+ moduleRef?: NgModuleRef;
+ target?: View;
}
-export class ModalDialogParams {
- constructor(
- public context: any = {},
- public closeCallback: (...args) => any) {
- }
+export interface ShowDialogOptions extends BaseShowModalOptions {
+ containerRef: ViewContainerRef;
+ context: any;
+ doneCallback;
+ pageFactory: PageFactory;
+ parentView: ViewBase;
+ resolver: ComponentFactoryResolver;
+ type: Type;
}
-interface ShowDialogOptions extends BaseShowModalOptions {
- containerRef: ViewContainerRef;
- context: any;
- doneCallback;
- pageFactory: PageFactory;
- parentView: ViewBase;
- resolver: ComponentFactoryResolver;
- type: Type;
+export class ModalDialogParams {
+ constructor(public context: any = {}, public closeCallback: (...args) => any) {}
}
@Injectable()
export class ModalDialogService {
- constructor(private location: NSLocationStrategy) {
- }
-
- public showModal(type: Type,
- options: ModalDialogOptions
- ): Promise {
- if (!options.viewContainerRef) {
- throw new Error(
- "No viewContainerRef: " +
- "Make sure you pass viewContainerRef in ModalDialogOptions."
- );
- }
-
- let parentView = options.viewContainerRef.element.nativeElement;
- if (options.target) {
- parentView = options.target;
- }
-
- if (parentView instanceof AppHostView && parentView.ngAppRoot) {
- parentView = parentView.ngAppRoot;
- }
-
- // _ngDialogRoot is the first child of the previously detached proxy.
- // It should have 'viewController' (iOS) or '_dialogFragment' (Android) available for
- // presenting future modal views.
- if (parentView._ngDialogRoot) {
- parentView = parentView._ngDialogRoot;
- }
-
- const pageFactory: PageFactory = options.viewContainerRef.injector.get(PAGE_FACTORY);
-
- // resolve from particular module (moduleRef)
- // or from same module as parentView (viewContainerRef)
- const componentContainer = options.moduleRef || options.viewContainerRef;
- const resolver = componentContainer.injector.get(ComponentFactoryResolver);
-
- let frame = parentView;
- if (!(parentView instanceof Frame)) {
- frame = (parentView.page && parentView.page.frame) || Frame.topmost();
- }
-
- this.location._beginModalNavigation(frame);
-
- return new Promise((resolve, reject) => {
- setTimeout(() => {
- try {
- this._showDialog({
- ...options,
- containerRef: options.viewContainerRef,
- context: options.context,
- doneCallback: resolve,
- pageFactory,
- parentView,
- resolver,
- type
- });
- } catch (err) {
- reject(err);
- }
- }, 10);
- });
- }
-
- private _showDialog(options: ShowDialogOptions): void {
- let componentView: View;
- let detachedLoaderRef: ComponentRef;
-
- const closeCallback = once((...args) => {
- options.doneCallback.apply(undefined, args);
- if (componentView) {
- componentView.closeModal();
- this.location._closeModalNavigation();
- detachedLoaderRef.instance.detectChanges();
- detachedLoaderRef.destroy();
- }
- });
-
- const modalParams = new ModalDialogParams(options.context, closeCallback);
-
- const childInjector = Injector.create({
- providers: [{ provide: ModalDialogParams, useValue: modalParams }],
- parent: options.containerRef.injector
- });
- const detachedFactory = options.resolver.resolveComponentFactory(DetachedLoader);
- detachedLoaderRef = options.containerRef.createComponent(detachedFactory, 0, childInjector, null);
- detachedLoaderRef.instance.loadComponent(options.type).then((compRef) => {
- const detachedProxy = compRef.location.nativeElement;
-
- if (detachedProxy.getChildrenCount() > 1) {
- throw new Error("Modal content has more than one root view.");
- }
- componentView = detachedProxy.getChildAt(0);
-
- if (componentView.parent) {
- (componentView.parent)._ngDialogRoot = componentView;
- (componentView.parent).removeChild(componentView);
- }
-
- options.parentView.showModal(componentView, { ...options, closeCallback });
- });
- }
+ constructor(private location: NSLocationStrategy) {}
+
+ public showModal(type: Type, options: ModalDialogOptions): Promise {
+ if (!options.viewContainerRef) {
+ throw new Error('No viewContainerRef: ' + 'Make sure you pass viewContainerRef in ModalDialogOptions.');
+ }
+
+ let parentView = options.viewContainerRef.element.nativeElement;
+ if (options.target) {
+ parentView = options.target;
+ }
+
+ if ((parentView instanceof AppHostView || parentView instanceof AppHostAsyncView) && parentView.ngAppRoot) {
+ parentView = parentView.ngAppRoot;
+ }
+
+ // _ngDialogRoot is the first child of the previously detached proxy.
+ // It should have 'viewController' (iOS) or '_dialogFragment' (Android) available for
+ // presenting future modal views.
+ if (parentView._ngDialogRoot) {
+ parentView = parentView._ngDialogRoot;
+ }
+
+ const pageFactory: PageFactory = options.viewContainerRef.injector.get(PAGE_FACTORY);
+
+ // resolve from particular module (moduleRef)
+ // or from same module as parentView (viewContainerRef)
+ const componentContainer = options.moduleRef || options.viewContainerRef;
+ const resolver = componentContainer.injector.get(ComponentFactoryResolver);
+
+ let frame = parentView;
+ if (!(parentView instanceof Frame)) {
+ frame = (parentView.page && parentView.page.frame) || Frame.topmost();
+ }
+
+ this.location._beginModalNavigation(frame);
+
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ try {
+ this._showDialog({
+ ...options,
+ containerRef: options.viewContainerRef,
+ context: options.context,
+ doneCallback: resolve,
+ pageFactory,
+ parentView,
+ resolver,
+ type,
+ });
+ } catch (err) {
+ reject(err);
+ }
+ }, 10);
+ });
+ }
+
+ private _showDialog(options: ShowDialogOptions): void {
+ let componentView: View;
+ let detachedLoaderRef: ComponentRef;
+
+ const closeCallback = once((...args) => {
+ options.doneCallback.apply(undefined, args);
+ if (componentView) {
+ componentView.closeModal();
+ this.location._closeModalNavigation();
+ detachedLoaderRef.instance.detectChanges();
+ detachedLoaderRef.destroy();
+ }
+ });
+
+ const modalParams = new ModalDialogParams(options.context, closeCallback);
+
+ const childInjector = Injector.create({
+ providers: [{ provide: ModalDialogParams, useValue: modalParams }],
+ parent: options.containerRef.injector,
+ });
+ const detachedFactory = options.resolver.resolveComponentFactory(DetachedLoader);
+ detachedLoaderRef = options.containerRef.createComponent(detachedFactory, 0, childInjector, null);
+ detachedLoaderRef.instance.loadComponent(options.type).then((compRef) => {
+ const detachedProxy = compRef.location.nativeElement;
+
+ if (detachedProxy.getChildrenCount() > 1) {
+ throw new Error('Modal content has more than one root view.');
+ }
+ componentView = detachedProxy.getChildAt(0);
+
+ if (componentView.parent) {
+ (componentView.parent)._ngDialogRoot = componentView;
+ (componentView.parent).removeChild(componentView);
+ }
+
+ options.parentView.showModal(componentView, { ...options, closeCallback });
+ });
+ }
}
diff --git a/nativescript-angular/directives/index.ts b/nativescript-angular/directives/index.ts
index 4b34a877e..c6ca38326 100644
--- a/nativescript-angular/directives/index.ts
+++ b/nativescript-angular/directives/index.ts
@@ -1,41 +1,10 @@
-import { ListViewComponent } from "./list-view-comp";
-import { TemplateKeyDirective, TemplatedItemsComponent } from "./templated-items-comp";
-import { TabViewDirective, TabViewItemDirective } from "./tab-view";
-import {
- ActionBarComponent,
- ActionBarScope,
- ActionItemDirective,
- NavigationButtonDirective
-} from "./action-bar";
-import { AndroidFilterComponent, IosFilterComponent } from "./platform-filters";
-import { ModalDialogOptions, ModalDialogParams, ModalDialogService } from "./dialogs";
+import { ListViewComponent } from './list-view-comp';
+import { TemplateKeyDirective, TemplatedItemsComponent } from './templated-items-comp';
+import { TabViewDirective, TabViewItemDirective } from './tab-view';
+import { ActionBarComponent, ActionBarScope, ActionItemDirective, NavigationButtonDirective } from './action-bar';
+import { AndroidFilterComponent, IosFilterComponent } from './platform-filters';
+export { ModalDialogOptions, ModalDialogParams, ModalDialogService } from './dialogs';
-export const NS_DIRECTIVES = [
- ListViewComponent,
- TemplateKeyDirective,
- TabViewDirective,
- TabViewItemDirective,
- ActionBarComponent,
- ActionBarScope,
- ActionItemDirective,
- NavigationButtonDirective,
- AndroidFilterComponent,
- IosFilterComponent,
-];
+export const NS_DIRECTIVES = [ListViewComponent, TemplateKeyDirective, TabViewDirective, TabViewItemDirective, ActionBarComponent, ActionBarScope, ActionItemDirective, NavigationButtonDirective, AndroidFilterComponent, IosFilterComponent];
-export {
- ListViewComponent,
- TemplateKeyDirective,
- TemplatedItemsComponent,
- TabViewDirective,
- TabViewItemDirective,
- ActionBarComponent,
- ActionBarScope,
- ActionItemDirective,
- NavigationButtonDirective,
- AndroidFilterComponent,
- IosFilterComponent,
- ModalDialogOptions,
- ModalDialogParams,
- ModalDialogService
-};
+export { ListViewComponent, TemplateKeyDirective, TemplatedItemsComponent, TabViewDirective, TabViewItemDirective, ActionBarComponent, ActionBarScope, ActionItemDirective, NavigationButtonDirective, AndroidFilterComponent, IosFilterComponent };
diff --git a/nativescript-angular/directives/list-view-comp.ts b/nativescript-angular/directives/list-view-comp.ts
index f4c1fb4e2..dc357d7e9 100644
--- a/nativescript-angular/directives/list-view-comp.ts
+++ b/nativescript-angular/directives/list-view-comp.ts
@@ -1,31 +1,23 @@
-import {
- ChangeDetectionStrategy,
- Component,
- ElementRef,
- IterableDiffers,
- forwardRef
-} from "@angular/core";
-import { ListView } from "@nativescript/core/ui/list-view";
-import { TEMPLATED_ITEMS_COMPONENT, TemplatedItemsComponent } from "./templated-items-comp";
+import { ChangeDetectionStrategy, Component, ElementRef, IterableDiffers, forwardRef } from '@angular/core';
+import { ListView } from '@nativescript/core';
+import { TEMPLATED_ITEMS_COMPONENT, TemplatedItemsComponent } from './templated-items-comp';
@Component({
- selector: "ListView",
- template: `
-
-
- `,
- changeDetection: ChangeDetectionStrategy.OnPush,
- providers: [{ provide: TEMPLATED_ITEMS_COMPONENT, useExisting: forwardRef(() => ListViewComponent)}]
+ selector: 'ListView',
+ template: `
+
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ providers: [{ provide: TEMPLATED_ITEMS_COMPONENT, useExisting: forwardRef(() => ListViewComponent) }],
})
export class ListViewComponent extends TemplatedItemsComponent {
- public get nativeElement(): ListView {
- return this.templatedItemsView;
- }
+ public get nativeElement(): ListView {
+ return this.templatedItemsView;
+ }
- protected templatedItemsView: ListView;
+ protected templatedItemsView: ListView;
- constructor(_elementRef: ElementRef,
- _iterableDiffers: IterableDiffers) {
- super(_elementRef, _iterableDiffers);
- }
+ constructor(_elementRef: ElementRef, _iterableDiffers: IterableDiffers) {
+ super(_elementRef, _iterableDiffers);
+ }
}
diff --git a/nativescript-angular/directives/platform-filters.ts b/nativescript-angular/directives/platform-filters.ts
index 77b718b49..55f5aac5b 100644
--- a/nativescript-angular/directives/platform-filters.ts
+++ b/nativescript-angular/directives/platform-filters.ts
@@ -1,25 +1,24 @@
-import { Component, Inject } from "@angular/core";
-import { Device, platformNames } from "@nativescript/core/platform";
-import { DEVICE } from "../platform-providers";
-
-@Component({
- selector: "android",
- template: ``,
-})
-export class AndroidFilterComponent {
- public show: boolean;
- constructor( @Inject(DEVICE) device: Device) {
- this.show = (device.os === platformNames.android);
- }
-}
-
-@Component({
- selector: "ios",
- template: ``,
-})
-export class IosFilterComponent {
- public show: boolean;
- constructor( @Inject(DEVICE) device: Device) {
- this.show = (device.os === platformNames.ios);
- }
-}
+import { Component, Inject } from '@angular/core';
+import { isIOS, isAndroid } from '@nativescript/core';
+
+@Component({
+ selector: 'android',
+ template: ``,
+})
+export class AndroidFilterComponent {
+ public show: boolean;
+ constructor() {
+ this.show = isAndroid;
+ }
+}
+
+@Component({
+ selector: 'ios',
+ template: ``,
+})
+export class IosFilterComponent {
+ public show: boolean;
+ constructor() {
+ this.show = isIOS;
+ }
+}
diff --git a/nativescript-angular/directives/tab-view.ts b/nativescript-angular/directives/tab-view.ts
index 3c595a37f..2fa2a272d 100644
--- a/nativescript-angular/directives/tab-view.ts
+++ b/nativescript-angular/directives/tab-view.ts
@@ -1,153 +1,137 @@
-import {
- AfterViewInit,
- Directive,
- ElementRef,
- Input,
- OnInit,
- TemplateRef,
- ViewContainerRef,
-} from "@angular/core";
-import { TabView, TabViewItem } from "@nativescript/core/ui/tab-view";
-import { TextTransform } from "@nativescript/core/ui/text-base";
-
-import { InvisibleNode } from "../element-registry";
-import { rendererLog, isLogEnabled } from "../trace";
-import { isBlank } from "../lang-facade";
+import { AfterViewInit, Directive, ElementRef, Input, OnInit, TemplateRef, ViewContainerRef } from '@angular/core';
+import { TabView, TabViewItem } from '@nativescript/core';
+import { InvisibleNode } from '../element-registry';
+import { NativeScriptDebug } from '../trace';
+import { isBlank } from '../lang-facade';
+
+type TextTransform = 'initial' | 'none' | 'capitalize' | 'uppercase' | 'lowercase';
export interface TabViewItemDef {
- title?: string;
- iconSource?: string;
- textTransform?: TextTransform;
+ title?: string;
+ iconSource?: string;
+ textTransform?: TextTransform;
}
@Directive({
- selector: "TabView", // tslint:disable-line:directive-selector
+ selector: 'TabView', // tslint:disable-line:directive-selector
})
export class TabViewDirective implements AfterViewInit {
- public tabView: TabView;
- private _selectedIndex: number;
- private viewInitialized: boolean;
-
- @Input()
- get selectedIndex(): number {
- return this._selectedIndex;
- }
-
- set selectedIndex(value) {
- this._selectedIndex = value;
- if (this.viewInitialized) {
- this.tabView.selectedIndex = this._selectedIndex;
- }
- }
-
- constructor(element: ElementRef) {
- this.tabView = element.nativeElement;
- }
-
- ngAfterViewInit() {
- this.viewInitialized = true;
- if (isLogEnabled()) {
- rendererLog("this._selectedIndex: " + this._selectedIndex);
- }
- if (!isBlank(this._selectedIndex)) {
- this.tabView.selectedIndex = this._selectedIndex;
- }
- }
+ public tabView: TabView;
+ private _selectedIndex: number;
+ private viewInitialized: boolean;
+
+ @Input()
+ get selectedIndex(): number {
+ return this._selectedIndex;
+ }
+
+ set selectedIndex(value) {
+ this._selectedIndex = value;
+ if (this.viewInitialized) {
+ this.tabView.selectedIndex = this._selectedIndex;
+ }
+ }
+
+ constructor(element: ElementRef) {
+ this.tabView = element.nativeElement;
+ }
+
+ ngAfterViewInit() {
+ this.viewInitialized = true;
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog('this._selectedIndex: ' + this._selectedIndex);
+ }
+ if (!isBlank(this._selectedIndex)) {
+ this.tabView.selectedIndex = this._selectedIndex;
+ }
+ }
}
@Directive({
- selector: "[tabItem]" // tslint:disable-line:directive-selector
+ selector: '[tabItem]', // tslint:disable-line:directive-selector
})
export class TabViewItemDirective implements OnInit {
- private item: TabViewItem;
- private _config: TabViewItemDef;
-
- constructor(
- private owner: TabViewDirective,
- private templateRef: TemplateRef,
- private viewContainer: ViewContainerRef
- ) {
- }
-
- @Input("tabItem")
- set config(config: TabViewItemDef) {
- if (!this._config
- || this._config.iconSource !== config.iconSource
- || this._config.title !== config.title
- || this._config.textTransform !== config.textTransform) {
- this._config = config;
- this.applyConfig();
- }
- }
-
- get config(): TabViewItemDef { // tslint:disable-line:no-input-rename
- return this._config || {};
- }
-
- @Input()
- set title(title: string) {
- this.config = Object.assign(this.config, { title });
- }
-
- get title() {
- return this.config.title;
- }
-
- @Input()
- set iconSource(iconSource: string) {
- this.config = Object.assign(this.config, { iconSource });
- }
-
- get iconSource() {
- return this.config.iconSource;
- }
-
- @Input()
- set textTransform(textTransform: TextTransform) {
- this.config = Object.assign(this.config, { textTransform });
- }
-
- get textTransform() {
- return this.config.textTransform;
- }
-
- private ensureItem() {
- if (!this.item) {
- this.item = new TabViewItem();
- }
- }
-
- private applyConfig() {
- this.ensureItem();
-
- if (this.config.title) {
- this.item.title = this.config.title;
- }
-
- if (this.config.iconSource) {
- this.item.iconSource = this.config.iconSource;
- }
-
- // TabViewItem textTransform has a default value for Android that kick in
- // only if no value (even a null value) is set.
- if (this.config.textTransform) {
- this.item.textTransform = this.config.textTransform;
- }
- }
-
- ngOnInit() {
- this.applyConfig();
-
- const viewRef = this.viewContainer.createEmbeddedView(this.templateRef);
- // Filter out text nodes and comments
- const realViews = viewRef.rootNodes.filter(node =>
- !(node instanceof InvisibleNode));
-
- if (realViews.length > 0) {
- this.item.view = realViews[0];
-
- const newItems = (this.owner.tabView.items || []).concat([this.item]);
- this.owner.tabView.items = newItems;
- }
- }
+ private item: TabViewItem;
+ private _config: TabViewItemDef;
+
+ constructor(private owner: TabViewDirective, private templateRef: TemplateRef, private viewContainer: ViewContainerRef) {}
+
+ @Input('tabItem')
+ set config(config: TabViewItemDef) {
+ if (!this._config || this._config.iconSource !== config.iconSource || this._config.title !== config.title || this._config.textTransform !== config.textTransform) {
+ this._config = config;
+ this.applyConfig();
+ }
+ }
+
+ get config(): TabViewItemDef {
+ // tslint:disable-line:no-input-rename
+ return this._config || {};
+ }
+
+ @Input()
+ set title(title: string) {
+ this.config = Object.assign(this.config, { title });
+ }
+
+ get title() {
+ return this.config.title;
+ }
+
+ @Input()
+ set iconSource(iconSource: string) {
+ this.config = Object.assign(this.config, { iconSource });
+ }
+
+ get iconSource() {
+ return this.config.iconSource;
+ }
+
+ @Input()
+ set textTransform(textTransform: TextTransform) {
+ this.config = Object.assign(this.config, { textTransform });
+ }
+
+ get textTransform() {
+ return this.config.textTransform;
+ }
+
+ private ensureItem() {
+ if (!this.item) {
+ this.item = new TabViewItem();
+ }
+ }
+
+ private applyConfig() {
+ this.ensureItem();
+
+ if (this.config.title) {
+ this.item.title = this.config.title;
+ }
+
+ if (this.config.iconSource) {
+ this.item.iconSource = this.config.iconSource;
+ }
+
+ // TabViewItem textTransform has a default value for Android that kick in
+ // only if no value (even a null value) is set.
+ if (this.config.textTransform) {
+ this.item.textTransform = this.config.textTransform;
+ }
+ }
+
+ ngOnInit() {
+ this.applyConfig();
+
+ const viewRef = this.viewContainer.createEmbeddedView(this.templateRef);
+ // Filter out text nodes and comments
+ const realViews = viewRef.rootNodes.filter((node) => !(node instanceof InvisibleNode));
+
+ if (realViews.length > 0) {
+ this.item.view = realViews[0];
+
+ const newItems = (this.owner.tabView.items || []).concat([this.item]);
+ this.owner.tabView.items = newItems;
+ }
+ }
}
diff --git a/nativescript-angular/directives/templated-items-comp.ts b/nativescript-angular/directives/templated-items-comp.ts
index 5113e559c..302f85377 100644
--- a/nativescript-angular/directives/templated-items-comp.ts
+++ b/nativescript-angular/directives/templated-items-comp.ts
@@ -1,276 +1,237 @@
-import {
- AfterContentInit,
- ContentChild,
- Directive,
- DoCheck,
- ElementRef,
- EmbeddedViewRef,
- EventEmitter,
- Host,
- Inject,
- InjectionToken,
- Input,
- IterableDiffer,
- IterableDiffers,
- OnDestroy,
- Output,
- TemplateRef,
- ViewChild,
- ViewContainerRef,
- ɵisListLikeIterable as isListLikeIterable
-} from "@angular/core";
-// tslint:disable: max-line-length
-// TODO: refactor core module imports to allow this to work properly
-// import { View, LayoutBase, KeyedTemplate, ItemEventData, TemplatedItemsView, ObservableArray } from "@nativescript/core";
-// Ivy entry points get out of order and will cause issues like this:
-// node_modules/@nativescript/core/ui/html-view/html-view-common.js:26:0: JS ERROR TypeError: undefined is not an object (evaluating 'color_1.Color.equals') if not using deep imports like the following for the moment
-// tslint:enable: max-line-length
-import { ItemEventData, TemplatedItemsView } from "@nativescript/core/ui/list-view";
-import { View, KeyedTemplate } from "@nativescript/core/ui/core/view";
-import { LayoutBase } from "@nativescript/core/ui/layouts/layout-base";
-import { ObservableArray } from "@nativescript/core/data/observable-array";
-import { profile } from "@nativescript/core/profiling";
-
-import { getSingleViewRecursive } from "../element-registry";
-import { listViewLog, listViewError, isLogEnabled } from "../trace";
-
-const NG_VIEW = "_ngViewRef";
+import { AfterContentInit, ContentChild, Directive, DoCheck, ElementRef, EmbeddedViewRef, EventEmitter, Host, Inject, InjectionToken, Input, IterableDiffer, IterableDiffers, OnDestroy, Output, TemplateRef, ViewChild, ViewContainerRef, ɵisListLikeIterable as isListLikeIterable, Injectable } from '@angular/core';
+import { ObservableArray, View, KeyedTemplate, LayoutBase, ItemEventData, TemplatedItemsView, profile } from '@nativescript/core';
+
+import { getSingleViewRecursive } from '../element-registry';
+import { NativeScriptDebug } from '../trace';
+
+const NG_VIEW = '_ngViewRef';
export class ItemContext {
- constructor(
- public $implicit?: any,
- public item?: any,
- public index?: number,
- public even?: boolean,
- public odd?: boolean
- ) {
- }
+ constructor(public $implicit?: any, public item?: any, public index?: number, public even?: boolean, public odd?: boolean) {}
}
export interface SetupItemViewArgs {
- view: EmbeddedViewRef;
- data: any;
- index: number;
- context: ItemContext;
+ view: EmbeddedViewRef;
+ data: any;
+ index: number;
+ context: ItemContext;
}
+@Directive()
export abstract class TemplatedItemsComponent implements DoCheck, OnDestroy, AfterContentInit {
- public abstract get nativeElement(): TemplatedItemsView;
-
- protected templatedItemsView: TemplatedItemsView;
- protected _items: any;
- protected _differ: IterableDiffer;
- protected _templateMap: Map;
-
- @ViewChild("loader", { read: ViewContainerRef, static: false }) loader: ViewContainerRef;
-
- @Output() public setupItemView = new EventEmitter();
-
- @ContentChild(TemplateRef, { read: TemplateRef, static: false }) itemTemplateQuery: TemplateRef;
-
- itemTemplate: TemplateRef;
-
- @Input()
- get items() {
- return this._items;
- }
-
- set items(value: any) {
- this._items = value;
- let needDiffer = true;
- if (value instanceof ObservableArray) {
- needDiffer = false;
- }
- if (needDiffer && !this._differ && isListLikeIterable(value)) {
- this._differ = this._iterableDiffers.find(this._items)
- .create((_index, item) => { return item; });
- }
-
- this.templatedItemsView.items = this._items;
- }
-
- constructor(_elementRef: ElementRef,
- private _iterableDiffers: IterableDiffers) {
- this.templatedItemsView = _elementRef.nativeElement;
-
- this.templatedItemsView.on("itemLoading", this.onItemLoading, this);
- }
-
- ngAfterContentInit() {
- if (isLogEnabled()) {
- listViewLog("TemplatedItemsView.ngAfterContentInit()");
- }
-
- this.setItemTemplates();
- }
-
- ngOnDestroy() {
- this.templatedItemsView.off("itemLoading", this.onItemLoading, this);
- this.templatedItemsView = null;
-
- if (this._templateMap) {
- this._templateMap.clear();
- }
- }
-
- private setItemTemplates() {
- // The itemTemplateQuery may be changed after list items are added that contain inside,
- // so cache and use only the original template to avoid errors.
- this.itemTemplate = this.itemTemplateQuery;
-
- if (this._templateMap) {
- if (isLogEnabled()) {
- listViewLog("Setting templates");
- }
-
- const templates: KeyedTemplate[] = [];
- this._templateMap.forEach(value => {
- templates.push(value);
- });
- this.templatedItemsView.itemTemplates = templates;
- }
- }
-
- public registerTemplate(key: string, template: TemplateRef) {
- if (isLogEnabled()) {
- listViewLog(`registerTemplate for key: ${key}`);
- }
-
- if (!this._templateMap) {
- this._templateMap = new Map();
- }
-
- const keyedTemplate = {
- key,
- createView: this.getItemTemplateViewFactory(template)
- };
-
- this._templateMap.set(key, keyedTemplate);
- }
-
- @profile
- public onItemLoading(args: ItemEventData) {
- if (!args.view && !this.itemTemplate) {
- return;
- }
-
- const index = args.index;
- const items = (args.object).items;
- const currentItem = typeof items.getItem === "function" ? items.getItem(index) : items[index];
- let viewRef: EmbeddedViewRef;
-
- if (args.view) {
- if (isLogEnabled()) {
- listViewLog(`onItemLoading: ${index} - Reusing existing view`);
- }
-
- viewRef = args.view[NG_VIEW];
-
- // Getting angular view from original element (in cases when ProxyViewContainer
- // is used NativeScript internally wraps it in a StackLayout)
- if (!viewRef && args.view instanceof LayoutBase && args.view.getChildrenCount() > 0) {
- viewRef = args.view.getChildAt(0)[NG_VIEW];
- }
-
- if (!viewRef && isLogEnabled()) {
- listViewError(`ViewReference not found for item ${index}. View recycling is not working`);
- }
-
- // No ng-template is setup, continue with 'defaultTemplate'
- if (!viewRef) {
- return;
- }
- }
-
- if (!viewRef) {
- if (isLogEnabled()) {
- listViewLog(`onItemLoading: ${index} - Creating view from template`);
- }
-
- viewRef = this.loader.createEmbeddedView(this.itemTemplate, new ItemContext(), 0);
- args.view = getItemViewRoot(viewRef);
- args.view[NG_VIEW] = viewRef;
- }
-
- this.setupViewRef(viewRef, currentItem, index);
-
- this.detectChangesOnChild(viewRef, index);
- }
-
- public setupViewRef(viewRef: EmbeddedViewRef, data: any, index: number): void {
- const context = viewRef.context;
- context.$implicit = data;
- context.item = data;
- context.index = index;
- context.even = (index % 2 === 0);
- context.odd = !context.even;
-
- this.setupItemView.next({ view: viewRef, data: data, index: index, context: context });
- }
-
- protected getItemTemplateViewFactory(template: TemplateRef): () => View {
- return () => {
- const viewRef = this.loader.createEmbeddedView(template, new ItemContext(), 0);
- const resultView = getItemViewRoot(viewRef);
- resultView[NG_VIEW] = viewRef;
-
- return resultView;
- };
- }
-
- @profile
- private detectChangesOnChild(viewRef: EmbeddedViewRef, index: number) {
- if (isLogEnabled()) {
- listViewLog(`Manually detect changes in child: ${index}`);
- }
-
- viewRef.markForCheck();
- viewRef.detectChanges();
- }
-
- ngDoCheck() {
- if (this._differ) {
- if (isLogEnabled()) {
- listViewLog("ngDoCheck() - execute differ");
- }
-
- const changes = this._differ.diff(this._items);
- if (changes) {
- if (isLogEnabled()) {
- listViewLog("ngDoCheck() - refresh");
- }
-
- this.templatedItemsView.refresh();
- }
- }
- }
+ public abstract get nativeElement(): TemplatedItemsView;
+
+ protected templatedItemsView: TemplatedItemsView;
+ protected _items: any;
+ protected _differ: IterableDiffer;
+ protected _templateMap: Map;
+
+ @ViewChild('loader', { read: ViewContainerRef, static: false }) loader: ViewContainerRef;
+
+ @Output() public setupItemView = new EventEmitter();
+
+ @ContentChild(TemplateRef, { read: TemplateRef, static: false }) itemTemplateQuery: TemplateRef;
+
+ itemTemplate: TemplateRef;
+
+ @Input()
+ get items() {
+ return this._items;
+ }
+
+ set items(value: any) {
+ this._items = value;
+ let needDiffer = true;
+ if (value instanceof ObservableArray) {
+ needDiffer = false;
+ }
+ if (needDiffer && !this._differ && isListLikeIterable(value)) {
+ this._differ = this._iterableDiffers.find(this._items).create((_index, item) => {
+ return item;
+ });
+ }
+
+ this.templatedItemsView.items = this._items;
+ }
+
+ constructor(_elementRef: ElementRef, private _iterableDiffers: IterableDiffers) {
+ this.templatedItemsView = _elementRef.nativeElement;
+
+ this.templatedItemsView.on('itemLoading', this.onItemLoading, this);
+ }
+
+ ngAfterContentInit() {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewLog('TemplatedItemsView.ngAfterContentInit()');
+ }
+
+ this.setItemTemplates();
+ }
+
+ ngOnDestroy() {
+ this.templatedItemsView.off('itemLoading', this.onItemLoading, this);
+ this.templatedItemsView = null;
+
+ if (this._templateMap) {
+ this._templateMap.clear();
+ }
+ }
+
+ private setItemTemplates() {
+ // The itemTemplateQuery may be changed after list items are added that contain inside,
+ // so cache and use only the original template to avoid errors.
+ this.itemTemplate = this.itemTemplateQuery;
+
+ if (this._templateMap) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewLog('Setting templates');
+ }
+
+ const templates: KeyedTemplate[] = [];
+ this._templateMap.forEach((value) => {
+ templates.push(value);
+ });
+ this.templatedItemsView.itemTemplates = templates;
+ }
+ }
+
+ public registerTemplate(key: string, template: TemplateRef) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewLog(`registerTemplate for key: ${key}`);
+ }
+
+ if (!this._templateMap) {
+ this._templateMap = new Map();
+ }
+
+ const keyedTemplate = {
+ key,
+ createView: this.getItemTemplateViewFactory(template),
+ };
+
+ this._templateMap.set(key, keyedTemplate);
+ }
+
+ @profile
+ public onItemLoading(args: ItemEventData) {
+ if (!args.view && !this.itemTemplate) {
+ return;
+ }
+
+ const index = args.index;
+ const items = (args.object).items;
+ const currentItem = typeof items.getItem === 'function' ? items.getItem(index) : items[index];
+ let viewRef: EmbeddedViewRef;
+
+ if (args.view) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewLog(`onItemLoading: ${index} - Reusing existing view`);
+ }
+
+ viewRef = args.view[NG_VIEW];
+
+ // Getting angular view from original element (in cases when ProxyViewContainer
+ // is used NativeScript internally wraps it in a StackLayout)
+ if (!viewRef && args.view instanceof LayoutBase && args.view.getChildrenCount() > 0) {
+ viewRef = args.view.getChildAt(0)[NG_VIEW];
+ }
+
+ if (!viewRef && NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewError(`ViewReference not found for item ${index}. View recycling is not working`);
+ }
+
+ // No ng-template is setup, continue with 'defaultTemplate'
+ if (!viewRef) {
+ return;
+ }
+ }
+
+ if (!viewRef) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewLog(`onItemLoading: ${index} - Creating view from template`);
+ }
+
+ viewRef = this.loader.createEmbeddedView(this.itemTemplate, new ItemContext(), 0);
+ args.view = getItemViewRoot(viewRef);
+ args.view[NG_VIEW] = viewRef;
+ }
+
+ this.setupViewRef(viewRef, currentItem, index);
+
+ this.detectChangesOnChild(viewRef, index);
+ }
+
+ public setupViewRef(viewRef: EmbeddedViewRef, data: any, index: number): void {
+ const context = viewRef.context;
+ context.$implicit = data;
+ context.item = data;
+ context.index = index;
+ context.even = index % 2 === 0;
+ context.odd = !context.even;
+
+ this.setupItemView.next({ view: viewRef, data: data, index: index, context: context });
+ }
+
+ protected getItemTemplateViewFactory(template: TemplateRef): () => View {
+ return () => {
+ const viewRef = this.loader.createEmbeddedView(template, new ItemContext(), 0);
+ const resultView = getItemViewRoot(viewRef);
+ resultView[NG_VIEW] = viewRef;
+
+ return resultView;
+ };
+ }
+
+ @profile
+ private detectChangesOnChild(viewRef: EmbeddedViewRef, index: number) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewLog(`Manually detect changes in child: ${index}`);
+ }
+
+ viewRef.markForCheck();
+ viewRef.detectChanges();
+ }
+
+ ngDoCheck() {
+ if (this._differ) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewLog('ngDoCheck() - execute differ');
+ }
+
+ const changes = this._differ.diff(this._items);
+ if (changes) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.listViewLog('ngDoCheck() - refresh');
+ }
+
+ this.templatedItemsView.refresh();
+ }
+ }
+ }
}
export interface ComponentView {
- rootNodes: Array;
- destroy(): void;
+ rootNodes: Array;
+ destroy(): void;
}
export type RootLocator = (nodes: Array, nestLevel: number) => View;
export function getItemViewRoot(viewRef: ComponentView, rootLocator: RootLocator = getSingleViewRecursive): View {
- const rootView = rootLocator(viewRef.rootNodes, 0);
- return rootView;
+ const rootView = rootLocator(viewRef.rootNodes, 0);
+ return rootView;
}
-export const TEMPLATED_ITEMS_COMPONENT = new InjectionToken("TemplatedItemsComponent");
+export const TEMPLATED_ITEMS_COMPONENT = new InjectionToken('TemplatedItemsComponent');
-@Directive({ selector: "[nsTemplateKey]" })
+@Directive({ selector: '[nsTemplateKey]' })
export class TemplateKeyDirective {
- constructor(
- private templateRef: TemplateRef,
- @Inject(TEMPLATED_ITEMS_COMPONENT) @Host() private comp: TemplatedItemsComponent) {
- }
-
- @Input()
- set nsTemplateKey(value: any) {
- if (this.comp && this.templateRef) {
- this.comp.registerTemplate(value, this.templateRef);
- }
- }
+ constructor(private templateRef: TemplateRef, @Inject(TEMPLATED_ITEMS_COMPONENT) @Host() private comp: TemplatedItemsComponent) {}
+
+ @Input()
+ set nsTemplateKey(value: any) {
+ if (this.comp && this.templateRef) {
+ this.comp.registerTemplate(value, this.templateRef);
+ }
+ }
}
diff --git a/nativescript-angular/dom-adapter.ts b/nativescript-angular/dom-adapter.ts
index 582da2ffc..09f4bfecd 100644
--- a/nativescript-angular/dom-adapter.ts
+++ b/nativescript-angular/dom-adapter.ts
@@ -1,179 +1,386 @@
/* tslint:disable */
-import { Type } from "@angular/core";
-import { ɵDomAdapter, ɵsetRootDomAdapter } from "@angular/common";
-import { rendererLog, isLogEnabled } from "./trace";
+import { Type } from '@angular/core';
+import { ɵDomAdapter, ɵsetRootDomAdapter } from '@angular/common';
+import { NativeScriptDebug } from './trace';
export class NativeScriptDomAdapter implements ɵDomAdapter {
- static makeCurrent() {
- if (isLogEnabled()) {
- rendererLog("Setting root DOM adapter...");
- }
+ static makeCurrent() {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog('Setting root DOM adapter...');
+ }
- ɵsetRootDomAdapter(new NativeScriptDomAdapter());
- }
+ ɵsetRootDomAdapter(new NativeScriptDomAdapter());
+ }
- hasProperty(_element: any, _name: string) {
- // TODO: actually check if the property exists.
- return true;
- }
+ hasProperty(_element: any, _name: string) {
+ // TODO: actually check if the property exists.
+ return true;
+ }
- log(arg: any): void {
- console.log(arg);
- }
+ log(arg: any): void {
+ console.log(arg);
+ }
- logError(arg: any): void {
- console.log(arg);
- }
+ logError(arg: any): void {
+ console.log(arg);
+ }
- logGroup(arg: any): void {
- console.log(arg);
- }
+ logGroup(arg: any): void {
+ console.log(arg);
+ }
- logGroupEnd(): void {
- }
+ logGroupEnd(): void {}
- get attrToPropMap(): { [key: string]: string } { throw new Error("Not implemented!"); };
- set attrToPropMap(_value: { [key: string]: string }) { throw new Error("Not implemented!"); };
+ get attrToPropMap(): { [key: string]: string } {
+ throw new Error('Not implemented!');
+ }
+ set attrToPropMap(_value: { [key: string]: string }) {
+ throw new Error('Not implemented!');
+ }
- public resourceLoaderType: Type = null;
- setProperty(_el: Element, _name: string, _value: any): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- getProperty(_el: Element, _name: string): any { throw new Error("Not implemented!") }
- invoke(_el: Element, _methodName: string, _args: any[]): any { throw new Error("Not implemented!") }
+ public resourceLoaderType: Type = null;
+ setProperty(_el: Element, _name: string, _value: any): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ getProperty(_el: Element, _name: string): any {
+ throw new Error('Not implemented!');
+ }
+ invoke(_el: Element, _methodName: string, _args: any[]): any {
+ throw new Error('Not implemented!');
+ }
- contains(_nodeA: any, _nodeB: any): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- parse(_templateHtml: string): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- query(_selector: string): any { throw new Error("Not implemented!") }
- querySelector(_el: any /** TODO #9100 */, _selector: string): HTMLElement { throw new Error("Not implemented!") }
- querySelectorAll(_el: any /** TODO #9100 */, _selector: string): any[] { throw new Error("Not implemented!") }
- on(
- _el: any /** TODO #9100 */, _evt: any /** TODO #9100 */, _listener: any /** TODO #9100 */): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- onAndCancel(
- _el: any /** TODO #9100 */, _evt: any /** TODO #9100 */,
- _listener: any /** TODO #9100 */): Function { throw new Error("Not implemented!") }
- dispatchEvent(_el: any /** TODO #9100 */, _evt: any /** TODO #9100 */): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- createMouseEvent(_eventType: any /** TODO #9100 */): any { throw new Error("Not implemented!") }
- createEvent(_eventType: string): any { throw new Error("Not implemented!") }
- preventDefault(_evt: any /** TODO #9100 */): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- isPrevented(_evt: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") }
- getInnerHTML(_el: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
+ contains(_nodeA: any, _nodeB: any): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ parse(_templateHtml: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ query(_selector: string): any {
+ throw new Error('Not implemented!');
+ }
+ querySelector(_el: any /** TODO #9100 */, _selector: string): HTMLElement {
+ throw new Error('Not implemented!');
+ }
+ querySelectorAll(_el: any /** TODO #9100 */, _selector: string): any[] {
+ throw new Error('Not implemented!');
+ }
+ on(_el: any /** TODO #9100 */, _evt: any /** TODO #9100 */, _listener: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ onAndCancel(_el: any /** TODO #9100 */, _evt: any /** TODO #9100 */, _listener: any /** TODO #9100 */): Function {
+ throw new Error('Not implemented!');
+ }
+ dispatchEvent(_el: any /** TODO #9100 */, _evt: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ createMouseEvent(_eventType: any /** TODO #9100 */): any {
+ throw new Error('Not implemented!');
+ }
+ createEvent(_eventType: string): any {
+ throw new Error('Not implemented!');
+ }
+ preventDefault(_evt: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ isPrevented(_evt: any /** TODO #9100 */): boolean {
+ throw new Error('Not implemented!');
+ }
+ getInnerHTML(_el: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
- getTemplateContent(_el: any /** TODO #9100 */): any { throw new Error("Not implemented!") }
- getOuterHTML(_el: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- nodeName(_node: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- nodeValue(_node: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- type(_node: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- content(_node: any /** TODO #9100 */): any { throw new Error("Not implemented!") }
- firstChild(_el: any /** TODO #9100 */): Node { throw new Error("Not implemented!") }
- nextSibling(_el: any /** TODO #9100 */): Node { throw new Error("Not implemented!") }
- parentElement(_el: any /** TODO #9100 */): Node { throw new Error("Not implemented!") }
- childNodes(_el: any /** TODO #9100 */): Node[] { throw new Error("Not implemented!") }
- childNodesAsList(_el: any /** TODO #9100 */): Node[] { throw new Error("Not implemented!") }
- clearNodes(_el: any /** TODO #9100 */): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- appendChild(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- removeChild(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- replaceChild(
- _el: any /** TODO #9100 */, _newNode: any /** TODO #9100 */,
- _oldNode: any /** TODO #9100 */): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- remove(_el: any /** TODO #9100 */): Node { throw new Error("Not implemented!") }
- insertBefore(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- insertAllBefore(_el: any /** TODO #9100 */, _nodes: any /** TODO #9100 */): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- insertAfter(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- setInnerHTML(_el: any /** TODO #9100 */, _value: any /** TODO #9100 */): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- getText(_el: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- setText(_el: any /** TODO #9100 */, _value: string): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- getValue(_el: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- setValue(_el: any /** TODO #9100 */, _value: string): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- getChecked(_el: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") }
- setChecked(_el: any /** TODO #9100 */, _value: boolean): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- createComment(_text: string): any { throw new Error("Not implemented!") }
- createTemplate(_html: any /** TODO #9100 */): HTMLElement { throw new Error("Not implemented!") }
- createElement(_tagName: any /** TODO #9100 */, _doc?: any /** TODO #9100 */): HTMLElement { throw new Error("Not implemented!") }
- createElementNS(_ns: string, _tagName: string, _doc?: any /** TODO #9100 */): Element { throw new Error("Not implemented!") }
- createTextNode(_text: string, _doc?: any /** TODO #9100 */): Text { throw new Error("Not implemented!") }
- createScriptTag(_attrName: string, _attrValue: string, _doc?: any /** TODO #9100 */):
- HTMLElement { throw new Error("Not implemented!") }
- createStyleElement(_css: string, _doc?: any /** TODO #9100 */): HTMLStyleElement { throw new Error("Not implemented!") }
- createShadowRoot(_el: any /** TODO #9100 */): any { throw new Error("Not implemented!") }
- getShadowRoot(_el: any /** TODO #9100 */): any { throw new Error("Not implemented!") }
- getHost(_el: any /** TODO #9100 */): any { throw new Error("Not implemented!") }
- getDistributedNodes(_el: any /** TODO #9100 */): Node[] { throw new Error("Not implemented!") }
- clone /**/(_node: Node /*T*/): Node /*T*/ { throw new Error("Not implemented!") }
- getElementsByClassName(_element: any /** TODO #9100 */, _name: string): HTMLElement[] { throw new Error("Not implemented!") }
- getElementsByTagName(_element: any /** TODO #9100 */, _name: string): HTMLElement[] { throw new Error("Not implemented!") }
- classList(_element: any /** TODO #9100 */): any[] { throw new Error("Not implemented!") }
- addClass(_element: any /** TODO #9100 */, _className: string): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- removeClass(_element: any /** TODO #9100 */, _className: string): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- hasClass(_element: any /** TODO #9100 */, _className: string): boolean { throw new Error("Not implemented!") }
- setStyle(_element: any /** TODO #9100 */, _styleName: string, _styleValue: string): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- removeStyle(_element: any /** TODO #9100 */, _styleName: string): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- getStyle(_element: any /** TODO #9100 */, _styleName: string): string { throw new Error("Not implemented!") }
- hasStyle(_element: any /** TODO #9100 */, _styleName: string, _styleValue?: string):
- boolean { throw new Error("Not implemented!") }
- tagName(_element: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- attributeMap(_element: any /** TODO #9100 */): Map { throw new Error("Not implemented!") }
- hasAttribute(_element: any /** TODO #9100 */, _attribute: string): boolean { throw new Error("Not implemented!") }
- hasAttributeNS(_element: any /** TODO #9100 */, _ns: string, _attribute: string): boolean { throw new Error("Not implemented!") }
- getAttribute(_element: any /** TODO #9100 */, _attribute: string): string { throw new Error("Not implemented!") }
- getAttributeNS(_element: any /** TODO #9100 */, _ns: string, _attribute: string): string { throw new Error("Not implemented!") }
- setAttribute(_element: any /** TODO #9100 */, _name: string, _value: string): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- setAttributeNS(_element: any /** TODO #9100 */, _ns: string, _name: string, _value: string):
- any /** TODO #9100 */ { throw new Error("Not implemented!") }
- removeAttribute(_element: any /** TODO #9100 */, _attribute: string): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- removeAttributeNS(_element: any /** TODO #9100 */, _ns: string, _attribute: string): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- templateAwareRoot(_el: any /** TODO #9100 */): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- createHtmlDocument(): HTMLDocument { throw new Error("Not implemented!") }
- defaultDoc(): HTMLDocument { throw new Error("Not implemented!") }
- getDefaultDocument(): Document { throw new Error("Not implemented!") }
- getBoundingClientRect(_el: any /** TODO #9100 */): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- getTitle(): string { throw new Error("Not implemented!") }
- setTitle(_doc: Document, _newTitle: string): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- elementMatches(_n: any /** TODO #9100 */, _selector: string): boolean { throw new Error("Not implemented!") }
- isTemplateElement(_el: any): boolean { throw new Error("Not implemented!") }
- isTextNode(_node: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") }
- isCommentNode(_node: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") }
- isElementNode(_node: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") }
- hasShadowRoot(_node: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") }
- isShadowRoot(_node: any /** TODO #9100 */): boolean { throw new Error("Not implemented!") }
- importIntoDoc /**/(_node: Node /*T*/): Node /*T*/ { throw new Error("Not implemented!") }
- adoptNode /**/(_node: Node /*T*/): Node /*T*/ { throw new Error("Not implemented!") }
- getHref(_element: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- getEventKey(_event: any /** TODO #9100 */): string { throw new Error("Not implemented!") }
- resolveAndSetHref(_element: any /** TODO #9100 */, _baseUrl: string, _href: string): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- supportsDOMEvents(): boolean { throw new Error("Not implemented!") }
- supportsNativeShadowDOM(): boolean { throw new Error("Not implemented!") }
- getGlobalEventTarget(_doc: Document, _target: string): any { throw new Error("Not implemented!") }
- getHistory(): History { throw new Error("Not implemented!") }
- getLocation(): Location { throw new Error("Not implemented!") }
- getBaseHref(): string { throw new Error("Not implemented!") }
- resetBaseElement(): void { throw new Error("Not implemented!") }
- getUserAgent(): string { return "Fake user agent"; }
- setData(_element: any /** TODO #9100 */, _name: string, _value: string): any
- /** TODO #9100 */ { throw new Error("Not implemented!") }
- getComputedStyle(_element: any /** TODO #9100 */): any { throw new Error("Not implemented!") }
- getData(_element: any /** TODO #9100 */, _name: string): string { throw new Error("Not implemented!") }
- setGlobalVar(_name: string, _value: any): any /** TODO #9100 */ { throw new Error("Not implemented!") }
- supportsWebAnimation(): boolean { throw new Error("Not implemented!") }
- performanceNow(): number { throw new Error("Not implemented!") }
- getAnimationPrefix(): string { throw new Error("Not implemented!") }
- getTransitionEnd(): string { throw new Error("Not implemented!") }
- supportsAnimation(): boolean { throw new Error("Not implemented!") }
+ getTemplateContent(_el: any /** TODO #9100 */): any {
+ throw new Error('Not implemented!');
+ }
+ getOuterHTML(_el: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ nodeName(_node: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ nodeValue(_node: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ type(_node: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ content(_node: any /** TODO #9100 */): any {
+ throw new Error('Not implemented!');
+ }
+ firstChild(_el: any /** TODO #9100 */): Node {
+ throw new Error('Not implemented!');
+ }
+ nextSibling(_el: any /** TODO #9100 */): Node {
+ throw new Error('Not implemented!');
+ }
+ parentElement(_el: any /** TODO #9100 */): Node {
+ throw new Error('Not implemented!');
+ }
+ childNodes(_el: any /** TODO #9100 */): Node[] {
+ throw new Error('Not implemented!');
+ }
+ childNodesAsList(_el: any /** TODO #9100 */): Node[] {
+ throw new Error('Not implemented!');
+ }
+ clearNodes(_el: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ appendChild(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ removeChild(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ replaceChild(_el: any /** TODO #9100 */, _newNode: any /** TODO #9100 */, _oldNode: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ remove(_el: any /** TODO #9100 */): Node {
+ throw new Error('Not implemented!');
+ }
+ insertBefore(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ insertAllBefore(_el: any /** TODO #9100 */, _nodes: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ insertAfter(_el: any /** TODO #9100 */, _node: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ setInnerHTML(_el: any /** TODO #9100 */, _value: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ getText(_el: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ setText(_el: any /** TODO #9100 */, _value: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ getValue(_el: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ setValue(_el: any /** TODO #9100 */, _value: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ getChecked(_el: any /** TODO #9100 */): boolean {
+ throw new Error('Not implemented!');
+ }
+ setChecked(_el: any /** TODO #9100 */, _value: boolean): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ createComment(_text: string): any {
+ throw new Error('Not implemented!');
+ }
+ createTemplate(_html: any /** TODO #9100 */): HTMLElement {
+ throw new Error('Not implemented!');
+ }
+ createElement(_tagName: any /** TODO #9100 */, _doc?: any /** TODO #9100 */): HTMLElement {
+ throw new Error('Not implemented!');
+ }
+ createElementNS(_ns: string, _tagName: string, _doc?: any /** TODO #9100 */): Element {
+ throw new Error('Not implemented!');
+ }
+ createTextNode(_text: string, _doc?: any /** TODO #9100 */): Text {
+ throw new Error('Not implemented!');
+ }
+ createScriptTag(_attrName: string, _attrValue: string, _doc?: any /** TODO #9100 */): HTMLElement {
+ throw new Error('Not implemented!');
+ }
+ createStyleElement(_css: string, _doc?: any /** TODO #9100 */): HTMLStyleElement {
+ throw new Error('Not implemented!');
+ }
+ createShadowRoot(_el: any /** TODO #9100 */): any {
+ throw new Error('Not implemented!');
+ }
+ getShadowRoot(_el: any /** TODO #9100 */): any {
+ throw new Error('Not implemented!');
+ }
+ getHost(_el: any /** TODO #9100 */): any {
+ throw new Error('Not implemented!');
+ }
+ getDistributedNodes(_el: any /** TODO #9100 */): Node[] {
+ throw new Error('Not implemented!');
+ }
+ clone /**/(_node: Node /*T*/): Node /*T*/ {
+ throw new Error('Not implemented!');
+ }
+ getElementsByClassName(_element: any /** TODO #9100 */, _name: string): HTMLElement[] {
+ throw new Error('Not implemented!');
+ }
+ getElementsByTagName(_element: any /** TODO #9100 */, _name: string): HTMLElement[] {
+ throw new Error('Not implemented!');
+ }
+ classList(_element: any /** TODO #9100 */): any[] {
+ throw new Error('Not implemented!');
+ }
+ addClass(_element: any /** TODO #9100 */, _className: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ removeClass(_element: any /** TODO #9100 */, _className: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ hasClass(_element: any /** TODO #9100 */, _className: string): boolean {
+ throw new Error('Not implemented!');
+ }
+ setStyle(_element: any /** TODO #9100 */, _styleName: string, _styleValue: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ removeStyle(_element: any /** TODO #9100 */, _styleName: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ getStyle(_element: any /** TODO #9100 */, _styleName: string): string {
+ throw new Error('Not implemented!');
+ }
+ hasStyle(_element: any /** TODO #9100 */, _styleName: string, _styleValue?: string): boolean {
+ throw new Error('Not implemented!');
+ }
+ tagName(_element: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ attributeMap(_element: any /** TODO #9100 */): Map {
+ throw new Error('Not implemented!');
+ }
+ hasAttribute(_element: any /** TODO #9100 */, _attribute: string): boolean {
+ throw new Error('Not implemented!');
+ }
+ hasAttributeNS(_element: any /** TODO #9100 */, _ns: string, _attribute: string): boolean {
+ throw new Error('Not implemented!');
+ }
+ getAttribute(_element: any /** TODO #9100 */, _attribute: string): string {
+ throw new Error('Not implemented!');
+ }
+ getAttributeNS(_element: any /** TODO #9100 */, _ns: string, _attribute: string): string {
+ throw new Error('Not implemented!');
+ }
+ setAttribute(_element: any /** TODO #9100 */, _name: string, _value: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ setAttributeNS(_element: any /** TODO #9100 */, _ns: string, _name: string, _value: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ removeAttribute(_element: any /** TODO #9100 */, _attribute: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ removeAttributeNS(_element: any /** TODO #9100 */, _ns: string, _attribute: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ templateAwareRoot(_el: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ createHtmlDocument(): HTMLDocument {
+ throw new Error('Not implemented!');
+ }
+ defaultDoc(): HTMLDocument {
+ throw new Error('Not implemented!');
+ }
+ getDefaultDocument(): Document {
+ throw new Error('Not implemented!');
+ }
+ getBoundingClientRect(_el: any /** TODO #9100 */): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ getTitle(): string {
+ throw new Error('Not implemented!');
+ }
+ setTitle(_doc: Document, _newTitle: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ elementMatches(_n: any /** TODO #9100 */, _selector: string): boolean {
+ throw new Error('Not implemented!');
+ }
+ isTemplateElement(_el: any): boolean {
+ throw new Error('Not implemented!');
+ }
+ isTextNode(_node: any /** TODO #9100 */): boolean {
+ throw new Error('Not implemented!');
+ }
+ isCommentNode(_node: any /** TODO #9100 */): boolean {
+ throw new Error('Not implemented!');
+ }
+ isElementNode(_node: any /** TODO #9100 */): boolean {
+ throw new Error('Not implemented!');
+ }
+ hasShadowRoot(_node: any /** TODO #9100 */): boolean {
+ throw new Error('Not implemented!');
+ }
+ isShadowRoot(_node: any /** TODO #9100 */): boolean {
+ throw new Error('Not implemented!');
+ }
+ importIntoDoc /**/(_node: Node /*T*/): Node /*T*/ {
+ throw new Error('Not implemented!');
+ }
+ adoptNode /**/(_node: Node /*T*/): Node /*T*/ {
+ throw new Error('Not implemented!');
+ }
+ getHref(_element: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ getEventKey(_event: any /** TODO #9100 */): string {
+ throw new Error('Not implemented!');
+ }
+ resolveAndSetHref(_element: any /** TODO #9100 */, _baseUrl: string, _href: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ supportsDOMEvents(): boolean {
+ throw new Error('Not implemented!');
+ }
+ supportsNativeShadowDOM(): boolean {
+ throw new Error('Not implemented!');
+ }
+ getGlobalEventTarget(_doc: Document, _target: string): any {
+ throw new Error('Not implemented!');
+ }
+ getHistory(): History {
+ throw new Error('Not implemented!');
+ }
+ getLocation(): Location {
+ throw new Error('Not implemented!');
+ }
+ getBaseHref(): string {
+ throw new Error('Not implemented!');
+ }
+ resetBaseElement(): void {
+ throw new Error('Not implemented!');
+ }
+ getUserAgent(): string {
+ return 'Fake user agent';
+ }
+ setData(_element: any /** TODO #9100 */, _name: string, _value: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ getComputedStyle(_element: any /** TODO #9100 */): any {
+ throw new Error('Not implemented!');
+ }
+ getData(_element: any /** TODO #9100 */, _name: string): string {
+ throw new Error('Not implemented!');
+ }
+ setGlobalVar(_name: string, _value: any): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
+ supportsWebAnimation(): boolean {
+ throw new Error('Not implemented!');
+ }
+ performanceNow(): number {
+ throw new Error('Not implemented!');
+ }
+ getAnimationPrefix(): string {
+ throw new Error('Not implemented!');
+ }
+ getTransitionEnd(): string {
+ throw new Error('Not implemented!');
+ }
+ supportsAnimation(): boolean {
+ throw new Error('Not implemented!');
+ }
- supportsCookies(): boolean { return false; }
- getCookie(_name: string): string { throw new Error("Not implemented!") }
- setCookie(_name: string, _value: string): any /** TODO #9100 */ { throw new Error("Not implemented!") }
+ supportsCookies(): boolean {
+ return false;
+ }
+ getCookie(_name: string): string {
+ throw new Error('Not implemented!');
+ }
+ setCookie(_name: string, _value: string): any /** TODO #9100 */ {
+ throw new Error('Not implemented!');
+ }
}
NativeScriptDomAdapter.makeCurrent();
diff --git a/nativescript-angular/element-registry.ts b/nativescript-angular/element-registry.ts
index a371e6b6f..cb39569d8 100644
--- a/nativescript-angular/element-registry.ts
+++ b/nativescript-angular/element-registry.ts
@@ -1,222 +1,209 @@
-import { View } from "@nativescript/core/ui/core/view";
-import { LayoutBase } from "@nativescript/core/ui/layouts/layout-base";
-import { Page } from "@nativescript/core/ui/page";
-import { Frame } from "@nativescript/core/ui/frame";
-
-export type NgView = (View & ViewExtensions);
+import { View, LayoutBase, Page, Frame, AbsoluteLayout, ActivityIndicator, BottomNavigation, Button, ContentView, DatePicker, DockLayout, GridLayout, HtmlView, Image, Label, ListPicker, ListView, Placeholder, Progress, ProxyViewContainer, Repeater, ScrollView, SearchBar, SegmentedBar, SegmentedBarItem, Slider, StackLayout, FlexboxLayout, Switch, TabView, TabStrip, TabStripItem, TabContentItem, Tabs, TextField, TextView, TimePicker, WebView, WrapLayout, FormattedString, Span } from '@nativescript/core';
+export interface ViewClass {
+ new (): View;
+}
export interface ViewExtensions {
- meta: ViewClassMeta;
- nodeType: number;
- nodeName: string;
- parentNode: NgView;
- nextSibling: NgView;
- firstChild: NgView;
- lastChild: NgView;
- ngCssClasses: Map;
+ meta: ViewClassMeta;
+ nodeType: number;
+ nodeName: string;
+ parentNode: NgView;
+ nextSibling: NgView;
+ firstChild: NgView;
+ lastChild: NgView;
+ ngCssClasses: Map;
}
-export interface ViewClass {
- new(): View;
-}
+export type NgView = View & ViewExtensions;
export abstract class InvisibleNode extends View implements NgView {
- meta: { skipAddToDom: boolean };
- nodeType: number;
- nodeName: string;
- parentNode: NgView;
- nextSibling: NgView;
- firstChild: NgView;
- lastChild: NgView;
- ngCssClasses: Map;
-
- constructor() {
- super();
-
- this.nodeType = 1;
- this.nodeName = getClassName(this);
- }
-
- toString() {
- return `${this.nodeName}(${this.id})`;
- }
+ meta: { skipAddToDom: boolean };
+ nodeType: number;
+ nodeName: string;
+ parentNode: NgView;
+ nextSibling: NgView;
+ firstChild: NgView;
+ lastChild: NgView;
+ ngCssClasses: Map;
+
+ constructor() {
+ super();
+
+ this.nodeType = 1;
+ this.nodeName = getClassName(this);
+ }
+
+ toString() {
+ return `${this.nodeName}(${this.id})`;
+ }
}
export class CommentNode extends InvisibleNode {
- protected static id = 0;
+ protected static id = 0;
- constructor() {
- super();
+ constructor() {
+ super();
- this.meta = {
- skipAddToDom: true,
- };
- this.id = CommentNode.id.toString();
- CommentNode.id += 1;
- }
+ this.meta = {
+ skipAddToDom: true,
+ };
+ this.id = CommentNode.id.toString();
+ CommentNode.id += 1;
+ }
}
export class TextNode extends InvisibleNode {
- protected static id = 0;
+ protected static id = 0;
- constructor() {
- super();
+ constructor() {
+ super();
- this.meta = {
- skipAddToDom: true,
- };
- this.id = TextNode.id.toString();
- TextNode.id += 1;
- }
+ this.meta = {
+ skipAddToDom: true,
+ };
+ this.id = TextNode.id.toString();
+ TextNode.id += 1;
+ }
}
-const getClassName = instance => instance.constructor.name;
+const getClassName = (instance) => instance.constructor.name;
export interface ViewClassMeta {
- skipAddToDom?: boolean;
- insertChild?: (parent: any, child: any, next?: any) => void;
- removeChild?: (parent: any, child: any) => void;
+ skipAddToDom?: boolean;
+ insertChild?: (parent: any, child: any, next?: any) => void;
+ removeChild?: (parent: any, child: any) => void;
}
export function isDetachedElement(element): boolean {
- return (element && element.meta && element.meta.skipAddToDom);
+ return element && element.meta && element.meta.skipAddToDom;
}
export function isView(view: any): view is NgView {
- return view instanceof View;
+ return view instanceof View;
}
export function isInvisibleNode(view: any): view is InvisibleNode {
- return view instanceof InvisibleNode;
+ return view instanceof InvisibleNode;
}
-export type ViewResolver = () => ViewClass;
+export type ViewResolver = () => any;
-const elementMap = new Map();
+const elementMap = new Map();
const camelCaseSplit = /([a-z0-9])([A-Z])/g;
const defaultViewMeta: ViewClassMeta = { skipAddToDom: false };
-export function registerElement(
- elementName: string,
- resolver: ViewResolver,
- meta?: ViewClassMeta
-): void {
- const entry = { resolver: resolver, meta: meta };
- elementMap.set(elementName, entry);
- elementMap.set(elementName.toLowerCase(), entry);
- elementMap.set(elementName.replace(camelCaseSplit, "$1-$2").toLowerCase(), entry);
+export function registerElement(elementName: string, resolver: ViewResolver, meta?: ViewClassMeta): void {
+ const entry = { resolver, meta };
+ elementMap.set(elementName, entry);
+ elementMap.set(elementName.toLowerCase(), entry);
+ elementMap.set(elementName.replace(camelCaseSplit, '$1-$2').toLowerCase(), entry);
}
-export function getViewClass(elementName: string): ViewClass {
- const entry = elementMap.get(elementName) ||
- elementMap.get(elementName.toLowerCase());
- if (!entry) {
- throw new TypeError(`No known component for element ${elementName}.`);
- }
-
- try {
- return entry.resolver();
- } catch (e) {
- throw new TypeError(`Could not load view for: ${elementName}.${e}`);
- }
+export function getViewClass(elementName: string): any {
+ const entry = elementMap.get(elementName) || elementMap.get(elementName.toLowerCase());
+ if (!entry) {
+ throw new TypeError(`No known component for element ${elementName}.`);
+ }
+
+ try {
+ return entry.resolver();
+ } catch (e) {
+ throw new TypeError(`Could not load view for: ${elementName}.${e}`);
+ }
}
export function getViewMeta(nodeName: string): ViewClassMeta {
- const entry = elementMap.get(nodeName) || elementMap.get(nodeName.toLowerCase());
- return (entry && entry.meta) || defaultViewMeta;
+ const entry = elementMap.get(nodeName) || elementMap.get(nodeName.toLowerCase());
+ return (entry && entry.meta) || defaultViewMeta;
}
export function isKnownView(elementName: string): boolean {
- return elementMap.has(elementName) ||
- elementMap.has(elementName.toLowerCase());
+ return elementMap.has(elementName) || elementMap.has(elementName.toLowerCase());
}
export function getSingleViewRecursive(nodes: Array, nestLevel: number): View {
- const actualNodes = nodes.filter(node => !(node instanceof InvisibleNode));
-
- if (actualNodes.length === 0) {
- throw new Error(`No suitable views found in list template! ` +
- `Nesting level: ${nestLevel}`);
- } else if (actualNodes.length > 1) {
- throw new Error(`More than one view found in list template!` +
- `Nesting level: ${nestLevel}`);
- }
-
- const rootLayout = actualNodes[0];
- if (!rootLayout) {
- return getSingleViewRecursive(rootLayout.children, nestLevel + 1);
- }
-
- const parentLayout = rootLayout.parent;
- if (parentLayout instanceof LayoutBase) {
- let node = rootLayout.parentNode;
- parentLayout.removeChild(rootLayout);
- rootLayout.parentNode = node;
- }
-
- return rootLayout;
+ const actualNodes = nodes.filter((node) => !(node instanceof InvisibleNode));
+
+ if (actualNodes.length === 0) {
+ throw new Error(`No suitable views found in list template! ` + `Nesting level: ${nestLevel}`);
+ } else if (actualNodes.length > 1) {
+ throw new Error(`More than one view found in list template!` + `Nesting level: ${nestLevel}`);
+ }
+
+ const rootLayout = actualNodes[0];
+ if (!rootLayout) {
+ return getSingleViewRecursive(rootLayout.children, nestLevel + 1);
+ }
+
+ const parentLayout = rootLayout.parent;
+ if (parentLayout instanceof LayoutBase) {
+ let node = rootLayout.parentNode;
+ parentLayout.removeChild(rootLayout);
+ rootLayout.parentNode = node;
+ }
+
+ return rootLayout;
}
const frameMeta: ViewClassMeta = {
- insertChild: (parent: Frame, child: NgView, next: any) => {
- // Page cannot be added to Frame with _addChildFromBuilder (trows "use defaultPage" error)
- if (isInvisibleNode(child)) {
- return;
- } else if (child instanceof Page) {
- parent.navigate({ create: () => child });
- } else {
- throw new Error("Only a Page can be a child of Frame");
- }
- }
+ insertChild: (parent: Frame, child: NgView, next: any) => {
+ // Page cannot be added to Frame with _addChildFromBuilder (thwos "use defaultPage" error)
+ if (isInvisibleNode(child)) {
+ return;
+ } else if (child instanceof Page) {
+ parent.navigate({ create: () => child });
+ } else {
+ throw new Error('Only a Page can be a child of Frame');
+ }
+ },
};
// Register default NativeScript components
// Note: ActionBar related components are registerd together with action-bar directives.
-registerElement("AbsoluteLayout", () => require("@nativescript/core/ui/layouts/absolute-layout").AbsoluteLayout);
-registerElement("ActivityIndicator", () => require("@nativescript/core/ui/activity-indicator").ActivityIndicator);
-registerElement("Border", () => require("@nativescript/core/ui/border").Border);
-registerElement("BottomNavigation", () => require("@nativescript/core/ui/bottom-navigation").BottomNavigation);
-registerElement("Button", () => require("@nativescript/core/ui/button").Button);
-registerElement("ContentView", () => require("@nativescript/core/ui/content-view").ContentView);
-registerElement("DatePicker", () => require("@nativescript/core/ui/date-picker").DatePicker);
-registerElement("DockLayout", () => require("@nativescript/core/ui/layouts/dock-layout").DockLayout);
-registerElement("Frame", () => require("@nativescript/core/ui/frame").Frame, frameMeta);
-registerElement("GridLayout", () => require("@nativescript/core/ui/layouts/grid-layout").GridLayout);
-registerElement("HtmlView", () => require("@nativescript/core/ui/html-view").HtmlView);
-registerElement("Image", () => require("@nativescript/core/ui/image").Image);
+registerElement('AbsoluteLayout', () => AbsoluteLayout);
+registerElement('ActivityIndicator', () => ActivityIndicator);
+registerElement('BottomNavigation', () => BottomNavigation);
+registerElement('Button', () => Button);
+registerElement('ContentView', () => ContentView);
+registerElement('DatePicker', () => DatePicker);
+registerElement('DockLayout', () => DockLayout);
+registerElement('Frame', () => Frame, frameMeta);
+registerElement('GridLayout', () => GridLayout);
+registerElement('HtmlView', () => HtmlView);
+registerElement('Image', () => Image);
// Parse5 changes tags to
. WTF!
-registerElement("img", () => require("@nativescript/core/ui/image").Image);
-registerElement("Label", () => require("@nativescript/core/ui/label").Label);
-registerElement("ListPicker", () => require("@nativescript/core/ui/list-picker").ListPicker);
-registerElement("ListView", () => require("@nativescript/core/ui/list-view").ListView);
-registerElement("Page", () => require("@nativescript/core/ui/page").Page);
-registerElement("Placeholder", () => require("@nativescript/core/ui/placeholder").Placeholder);
-registerElement("Progress", () => require("@nativescript/core/ui/progress").Progress);
-registerElement("ProxyViewContainer", () => require("@nativescript/core/ui/proxy-view-container").ProxyViewContainer);
-registerElement("Repeater", () => require("@nativescript/core/ui/repeater").Repeater);
-registerElement("ScrollView", () => require("@nativescript/core/ui/scroll-view").ScrollView);
-registerElement("SearchBar", () => require("@nativescript/core/ui/search-bar").SearchBar);
-registerElement("SegmentedBar", () => require("@nativescript/core/ui/segmented-bar").SegmentedBar);
-registerElement("SegmentedBarItem", () => require("@nativescript/core/ui/segmented-bar").SegmentedBarItem);
-registerElement("Slider", () => require("@nativescript/core/ui/slider").Slider);
-registerElement("StackLayout", () => require("@nativescript/core/ui/layouts/stack-layout").StackLayout);
-registerElement("FlexboxLayout", () => require("@nativescript/core/ui/layouts/flexbox-layout").FlexboxLayout);
-registerElement("Switch", () => require("@nativescript/core/ui/switch").Switch);
-registerElement("TabView", () => require("@nativescript/core/ui/tab-view").TabView);
-registerElement("TabStrip", () => require("@nativescript/core/ui/tab-navigation-base/tab-strip").TabStrip);
-registerElement("TabStripItem", () => require("@nativescript/core/ui/tab-navigation-base/tab-strip-item").TabStripItem);
-registerElement("TabContentItem",
- () => require("@nativescript/core/ui/tab-navigation-base/tab-content-item").TabContentItem);
-registerElement("Tabs", () => require("@nativescript/core/ui/tabs").Tabs);
-registerElement("TextField", () => require("@nativescript/core/ui/text-field").TextField);
-registerElement("TextView", () => require("@nativescript/core/ui/text-view").TextView);
-registerElement("TimePicker", () => require("@nativescript/core/ui/time-picker").TimePicker);
-registerElement("WebView", () => require("@nativescript/core/ui/web-view").WebView);
-registerElement("WrapLayout", () => require("@nativescript/core/ui/layouts/wrap-layout").WrapLayout);
-registerElement("FormattedString", () => require("@nativescript/core/text/formatted-string").FormattedString);
-registerElement("Span", () => require("@nativescript/core/text/span").Span);
-
-registerElement("DetachedContainer", () => require("@nativescript/core/ui/proxy-view-container").ProxyViewContainer,
- { skipAddToDom: true });
-
-registerElement("page-router-outlet", () => require("@nativescript/core/ui/frame").Frame);
+registerElement('img', () => Image);
+registerElement('Label', () => Label);
+registerElement('ListPicker', () => ListPicker);
+registerElement('ListView', () => ListView);
+registerElement('Page', () => Page);
+registerElement('Placeholder', () => Placeholder);
+registerElement('Progress', () => Progress);
+registerElement('ProxyViewContainer', () => ProxyViewContainer);
+registerElement('Repeater', () => Repeater);
+registerElement('ScrollView', () => ScrollView);
+registerElement('SearchBar', () => SearchBar);
+registerElement('SegmentedBar', () => SegmentedBar);
+registerElement('SegmentedBarItem', () => SegmentedBarItem);
+registerElement('Slider', () => Slider);
+registerElement('StackLayout', () => StackLayout);
+registerElement('FlexboxLayout', () => FlexboxLayout);
+registerElement('Switch', () => Switch);
+registerElement('TabView', () => TabView);
+registerElement('TabStrip', () => TabStrip);
+registerElement('TabStripItem', () => TabStripItem);
+registerElement('TabContentItem', () => TabContentItem);
+registerElement('Tabs', () => Tabs);
+registerElement('TextField', () => TextField);
+registerElement('TextView', () => TextView);
+registerElement('TimePicker', () => TimePicker);
+registerElement('WebView', () => WebView);
+registerElement('WrapLayout', () => WrapLayout);
+registerElement('FormattedString', () => FormattedString);
+registerElement('Span', () => Span);
+
+registerElement('DetachedContainer', () => ProxyViewContainer, {
+ skipAddToDom: true,
+});
+
+registerElement('page-router-outlet', () => Frame);
diff --git a/nativescript-angular/file-system/index.ts b/nativescript-angular/file-system/index.ts
index e0e3dc687..755af9427 100644
--- a/nativescript-angular/file-system/index.ts
+++ b/nativescript-angular/file-system/index.ts
@@ -1 +1 @@
-export * from "./ns-file-system";
+export * from './ns-file-system';
diff --git a/nativescript-angular/file-system/ns-file-system.ts b/nativescript-angular/file-system/ns-file-system.ts
index 2e68c42f2..a55b63f15 100644
--- a/nativescript-angular/file-system/ns-file-system.ts
+++ b/nativescript-angular/file-system/ns-file-system.ts
@@ -1,20 +1,20 @@
-import { Injectable } from "@angular/core";
-import { knownFolders, Folder, File } from "@nativescript/core/file-system";
+import { Injectable } from '@angular/core';
+import { knownFolders, Folder, File } from '@nativescript/core';
// Allows greater flexibility with `file-system` and Angular
// Also provides a way for `file-system` to be mocked for testing
@Injectable()
export class NSFileSystem {
- public currentApp(): Folder {
- return knownFolders.currentApp();
- }
+ public currentApp(): Folder {
+ return knownFolders.currentApp();
+ }
- public fileFromPath(path: string): File {
- return File.fromPath(path);
- }
+ public fileFromPath(path: string): File {
+ return File.fromPath(path);
+ }
- public fileExists(path: string): boolean {
- return File.exists(path);
- }
+ public fileExists(path: string): boolean {
+ return File.exists(path);
+ }
}
diff --git a/nativescript-angular/forms/forms.module.ts b/nativescript-angular/forms/forms.module.ts
index 17b324c55..69ba95069 100644
--- a/nativescript-angular/forms/forms.module.ts
+++ b/nativescript-angular/forms/forms.module.ts
@@ -1,42 +1,13 @@
-import { NgModule } from "@angular/core";
-import { FormsModule } from "@angular/forms";
-import {
- TextValueAccessor,
- CheckedValueAccessor,
- DateValueAccessor,
- TimeValueAccessor,
- NumberValueAccessor,
- SelectedIndexValueAccessor,
-} from "./value-accessors";
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { TextValueAccessor, CheckedValueAccessor, DateValueAccessor, TimeValueAccessor, NumberValueAccessor, SelectedIndexValueAccessor } from './value-accessors';
-export {
- TextValueAccessor,
- CheckedValueAccessor,
- DateValueAccessor,
- TimeValueAccessor,
- SelectedIndexValueAccessor,
- NumberValueAccessor,
-};
+export * from './value-accessors';
@NgModule({
- declarations: [
- TextValueAccessor,
- CheckedValueAccessor,
- DateValueAccessor,
- TimeValueAccessor,
- SelectedIndexValueAccessor,
- NumberValueAccessor,
- ],
- providers: [],
- imports: [FormsModule],
- exports: [
- FormsModule,
- TextValueAccessor,
- CheckedValueAccessor,
- DateValueAccessor,
- TimeValueAccessor,
- SelectedIndexValueAccessor,
- NumberValueAccessor,
- ],
+ declarations: [TextValueAccessor, CheckedValueAccessor, DateValueAccessor, TimeValueAccessor, SelectedIndexValueAccessor, NumberValueAccessor],
+ providers: [],
+ imports: [FormsModule],
+ exports: [FormsModule, TextValueAccessor, CheckedValueAccessor, DateValueAccessor, TimeValueAccessor, SelectedIndexValueAccessor, NumberValueAccessor],
})
export class NativeScriptFormsModule {}
diff --git a/nativescript-angular/forms/index.ts b/nativescript-angular/forms/index.ts
index a95e3e129..c88691333 100644
--- a/nativescript-angular/forms/index.ts
+++ b/nativescript-angular/forms/index.ts
@@ -1 +1 @@
-export * from "./forms.module";
+export * from './forms.module';
diff --git a/nativescript-angular/forms/value-accessors/base-value-accessor.ts b/nativescript-angular/forms/value-accessors/base-value-accessor.ts
index 3b948988a..abc3bf4a3 100644
--- a/nativescript-angular/forms/value-accessors/base-value-accessor.ts
+++ b/nativescript-angular/forms/value-accessors/base-value-accessor.ts
@@ -1,38 +1,38 @@
-import { ControlValueAccessor } from "@angular/forms";
-import { View, unsetValue } from "@nativescript/core/ui/core/view";
+import { ControlValueAccessor } from '@angular/forms';
+import { View, unsetValue } from '@nativescript/core';
-import { isBlank } from "../../lang-facade";
+import { isBlank } from '../../lang-facade';
export class BaseValueAccessor implements ControlValueAccessor {
- private pendingChangeNotification: any = 0;
- onChange = (_) => { };
- onTouched = () => {};
-
- constructor(public view: TView) { }
-
- registerOnChange(fn: (_: any) => void): void {
- this.onChange = (arg) => {
- if (this.pendingChangeNotification) {
- clearTimeout(this.pendingChangeNotification);
- }
- this.pendingChangeNotification = setTimeout(() => {
- this.pendingChangeNotification = 0;
- fn(arg);
- }, 20);
- };
- }
-
- registerOnTouched(fn: () => void): void {
- this.onTouched = fn;
- }
-
- setDisabledState(isDisabled: boolean): void {
- this.view.isEnabled = !isDisabled;
- }
-
- writeValue(_: any) {}
-
- protected normalizeValue(value: any): any {
- return isBlank(value) ? unsetValue : value;
- }
+ private pendingChangeNotification: any = 0;
+ onChange = (_) => {};
+ onTouched = () => {};
+
+ constructor(public view: TView) {}
+
+ registerOnChange(fn: (_: any) => void): void {
+ this.onChange = (arg) => {
+ if (this.pendingChangeNotification) {
+ clearTimeout(this.pendingChangeNotification);
+ }
+ this.pendingChangeNotification = setTimeout(() => {
+ this.pendingChangeNotification = 0;
+ fn(arg);
+ }, 20);
+ };
+ }
+
+ registerOnTouched(fn: () => void): void {
+ this.onTouched = fn;
+ }
+
+ setDisabledState(isDisabled: boolean): void {
+ this.view.isEnabled = !isDisabled;
+ }
+
+ writeValue(_: any) {}
+
+ protected normalizeValue(value: any): any {
+ return isBlank(value) ? unsetValue : value;
+ }
}
diff --git a/nativescript-angular/forms/value-accessors/checked-value-accessor.ts b/nativescript-angular/forms/value-accessors/checked-value-accessor.ts
index bf5ebf45b..b394554d4 100644
--- a/nativescript-angular/forms/value-accessors/checked-value-accessor.ts
+++ b/nativescript-angular/forms/value-accessors/checked-value-accessor.ts
@@ -1,12 +1,12 @@
-import { Directive, ElementRef, forwardRef } from "@angular/core";
-import { NG_VALUE_ACCESSOR } from "@angular/forms";
-import { BaseValueAccessor } from "./base-value-accessor";
-import { Switch } from "@nativescript/core/ui/switch";
+import { Directive, ElementRef, forwardRef } from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+import { BaseValueAccessor } from './base-value-accessor';
+import { Switch } from '@nativescript/core';
const CHECKED_VALUE_ACCESSOR = {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => CheckedValueAccessor),
- multi: true,
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => CheckedValueAccessor),
+ multi: true,
};
/**
@@ -19,21 +19,20 @@ const CHECKED_VALUE_ACCESSOR = {
* ```
*/
@Directive({
- selector:
- "Switch[ngModel],Switch[formControlName],Switch[formControl]," +
- "switch[ngModel],switch[formControlName],switch[formControl]",
- providers: [CHECKED_VALUE_ACCESSOR],
- host: {
- "(checkedChange)": "onChange($event.value)",
- },
+ selector: 'Switch[ngModel],Switch[formControlName],Switch[formControl],' + 'switch[ngModel],switch[formControlName],switch[formControl]',
+ providers: [CHECKED_VALUE_ACCESSOR],
+ host: {
+ '(checkedChange)': 'onChange($event.value)',
+ },
})
-export class CheckedValueAccessor extends BaseValueAccessor { // tslint:disable-line:directive-class-suffix
- constructor(elementRef: ElementRef) {
- super(elementRef.nativeElement);
- }
+export class CheckedValueAccessor extends BaseValueAccessor {
+ // tslint:disable-line:directive-class-suffix
+ constructor(elementRef: ElementRef) {
+ super(elementRef.nativeElement);
+ }
- writeValue(value: any): void {
- const normalized = super.normalizeValue(value);
- this.view.checked = normalized;
- }
+ writeValue(value: any): void {
+ const normalized = super.normalizeValue(value);
+ this.view.checked = normalized;
+ }
}
diff --git a/nativescript-angular/forms/value-accessors/date-value-accessor.ts b/nativescript-angular/forms/value-accessors/date-value-accessor.ts
index e17488627..2417ef1b2 100644
--- a/nativescript-angular/forms/value-accessors/date-value-accessor.ts
+++ b/nativescript-angular/forms/value-accessors/date-value-accessor.ts
@@ -1,12 +1,12 @@
-import { Directive, ElementRef, forwardRef } from "@angular/core";
-import { NG_VALUE_ACCESSOR } from "@angular/forms";
-import { BaseValueAccessor } from "./base-value-accessor";
-import { DatePicker } from "@nativescript/core/ui/date-picker";
+import { Directive, ElementRef, forwardRef } from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+import { BaseValueAccessor } from './base-value-accessor';
+import { DatePicker } from '@nativescript/core';
const DATE_VALUE_ACCESSOR = {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => DateValueAccessor),
- multi: true,
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => DateValueAccessor),
+ multi: true,
};
/**
@@ -19,22 +19,20 @@ const DATE_VALUE_ACCESSOR = {
* ```
*/
@Directive({
- selector: "DatePicker[ngModel],DatePicker[formControlName],DatePicker[formControl]," +
- "datepicker[ngModel],datepicker[formControlName],datepicker[formControl]," +
- "datePicker[ngModel],datePicker[formControlName],datePicker[formControl]," +
- "date-picker[ngModel],date-picker[formControlName],date-picker[formControl]",
- providers: [DATE_VALUE_ACCESSOR],
- host: {
- "(dateChange)": "onChange($event.value)",
- },
+ selector: 'DatePicker[ngModel],DatePicker[formControlName],DatePicker[formControl],' + 'datepicker[ngModel],datepicker[formControlName],datepicker[formControl],' + 'datePicker[ngModel],datePicker[formControlName],datePicker[formControl],' + 'date-picker[ngModel],date-picker[formControlName],date-picker[formControl]',
+ providers: [DATE_VALUE_ACCESSOR],
+ host: {
+ '(dateChange)': 'onChange($event.value)',
+ },
})
-export class DateValueAccessor extends BaseValueAccessor { // tslint:disable-line:directive-class-suffix
- constructor(elementRef: ElementRef) {
- super(elementRef.nativeElement);
- }
+export class DateValueAccessor extends BaseValueAccessor {
+ // tslint:disable-line:directive-class-suffix
+ constructor(elementRef: ElementRef) {
+ super(elementRef.nativeElement);
+ }
- writeValue(value: any): void {
- const normalized = super.normalizeValue(value);
- this.view.date = normalized;
- }
+ writeValue(value: any): void {
+ const normalized = super.normalizeValue(value);
+ this.view.date = normalized;
+ }
}
diff --git a/nativescript-angular/forms/value-accessors/index.ts b/nativescript-angular/forms/value-accessors/index.ts
index 36d656b1b..c3b2a05a2 100644
--- a/nativescript-angular/forms/value-accessors/index.ts
+++ b/nativescript-angular/forms/value-accessors/index.ts
@@ -1,10 +1,7 @@
-export { BaseValueAccessor } from "./base-value-accessor";
-export { TextValueAccessor, TextView } from "./text-value-accessor";
-export { CheckedValueAccessor } from "./checked-value-accessor";
-export { DateValueAccessor } from "./date-value-accessor";
-export { TimeValueAccessor } from "./time-value-accessor";
-export { NumberValueAccessor } from "./number-value-accessor";
-export {
- SelectedIndexValueAccessor,
- SelectableView
-} from "./selectedIndex-value-accessor";
+export { BaseValueAccessor } from './base-value-accessor';
+export { TextValueAccessor, TextView } from './text-value-accessor';
+export { CheckedValueAccessor } from './checked-value-accessor';
+export { DateValueAccessor } from './date-value-accessor';
+export { TimeValueAccessor } from './time-value-accessor';
+export { NumberValueAccessor } from './number-value-accessor';
+export { SelectedIndexValueAccessor, SelectableView } from './selectedIndex-value-accessor';
diff --git a/nativescript-angular/forms/value-accessors/number-value-accessor.ts b/nativescript-angular/forms/value-accessors/number-value-accessor.ts
index 46d28a234..8ec1481bf 100644
--- a/nativescript-angular/forms/value-accessors/number-value-accessor.ts
+++ b/nativescript-angular/forms/value-accessors/number-value-accessor.ts
@@ -1,12 +1,12 @@
-import { Directive, ElementRef, forwardRef } from "@angular/core";
-import { NG_VALUE_ACCESSOR } from "@angular/forms";
-import { BaseValueAccessor } from "./base-value-accessor";
-import { Slider } from "@nativescript/core/ui/slider";
+import { Directive, ElementRef, forwardRef } from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+import { BaseValueAccessor } from './base-value-accessor';
+import { Slider } from '@nativescript/core';
const NUMBER_VALUE_ACCESSOR = {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => NumberValueAccessor),
- multi: true,
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => NumberValueAccessor),
+ multi: true,
};
/**
@@ -19,21 +19,20 @@ const NUMBER_VALUE_ACCESSOR = {
* ```
*/
@Directive({
- selector:
- "Slider[ngModel],Slider[formControlName],Slider[formControl]," +
- "slider[ngModel],slider[formControlName],slider[formControl]",
- providers: [NUMBER_VALUE_ACCESSOR],
- host: {
- "(valueChange)": "onChange($event.value)",
- },
+ selector: 'Slider[ngModel],Slider[formControlName],Slider[formControl],' + 'slider[ngModel],slider[formControlName],slider[formControl]',
+ providers: [NUMBER_VALUE_ACCESSOR],
+ host: {
+ '(valueChange)': 'onChange($event.value)',
+ },
})
-export class NumberValueAccessor extends BaseValueAccessor { // tslint:disable-line:directive-class-suffix
- constructor(elementRef: ElementRef) {
- super(elementRef.nativeElement);
- }
+export class NumberValueAccessor extends BaseValueAccessor {
+ // tslint:disable-line:directive-class-suffix
+ constructor(elementRef: ElementRef) {
+ super(elementRef.nativeElement);
+ }
- writeValue(value: any): void {
- const normalized = super.normalizeValue(value);
- this.view.value = normalized;
- }
+ writeValue(value: any): void {
+ const normalized = super.normalizeValue(value);
+ this.view.value = normalized;
+ }
}
diff --git a/nativescript-angular/forms/value-accessors/selectedIndex-value-accessor.ts b/nativescript-angular/forms/value-accessors/selectedIndex-value-accessor.ts
index 5deb24f6b..9e98be412 100644
--- a/nativescript-angular/forms/value-accessors/selectedIndex-value-accessor.ts
+++ b/nativescript-angular/forms/value-accessors/selectedIndex-value-accessor.ts
@@ -1,15 +1,15 @@
-import { Directive, ElementRef, forwardRef, AfterViewInit } from "@angular/core";
-import { NG_VALUE_ACCESSOR } from "@angular/forms";
-import { BaseValueAccessor } from "./base-value-accessor";
-import { View } from "@nativescript/core/ui/core/view";
+import { Directive, ElementRef, forwardRef, AfterViewInit } from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+import { BaseValueAccessor } from './base-value-accessor';
+import { View } from '@nativescript/core';
const SELECTED_INDEX_VALUE_ACCESSOR = {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => SelectedIndexValueAccessor),
- multi: true,
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => SelectedIndexValueAccessor),
+ multi: true,
};
-export type SelectableView = {selectedIndex: number} & View;
+export type SelectableView = { selectedIndex: number } & View;
/**
* The accessor for setting a selectedIndex and listening to changes that is used by the
@@ -21,45 +21,44 @@ export type SelectableView = {selectedIndex: number} & View;
* ```
*/
@Directive({
- selector:
- "SegmentedBar[ngModel],SegmentedBar[formControlName],SegmentedBar[formControl]," +
- "segmentedBar[ngModel],segmentedBar[formControlName],segmentedBar[formControl]," +
- "segmentedbar[ngModel],segmentedbar[formControlName],segmentedbar[formControl]," +
- "segmented-bar[ngModel],segmented-bar[formControlName],segmented-bar[formControl]," +
-
- "ListPicker[ngModel],ListPicker[formControlName],ListPicker[formControl]," +
- "listPicker[ngModel],listPicker[formControlName],listPicker[formControl]," +
- "listpicker[ngModel],listpicker[formControlName],listpicker[formControl]," +
- "list-picker[ngModel],list-picker[formControlName],list-picker[formControl]," +
-
- "TabView[ngModel],TabView[formControlName],TabView[formControl]," +
- "tabView[ngModel],tabView[formControlName],tabView[formControl]," +
- "tabview[ngModel],tabview[formControlName],tabview[formControl]," +
- "tab-view[ngModel],tab-view[formControlName],tab-view[formControl]",
- providers: [SELECTED_INDEX_VALUE_ACCESSOR],
- host: {
- "(selectedIndexChange)": "onChange($event.value)",
- },
+ selector:
+ 'SegmentedBar[ngModel],SegmentedBar[formControlName],SegmentedBar[formControl],' +
+ 'segmentedBar[ngModel],segmentedBar[formControlName],segmentedBar[formControl],' +
+ 'segmentedbar[ngModel],segmentedbar[formControlName],segmentedbar[formControl],' +
+ 'segmented-bar[ngModel],segmented-bar[formControlName],segmented-bar[formControl],' +
+ 'ListPicker[ngModel],ListPicker[formControlName],ListPicker[formControl],' +
+ 'listPicker[ngModel],listPicker[formControlName],listPicker[formControl],' +
+ 'listpicker[ngModel],listpicker[formControlName],listpicker[formControl],' +
+ 'list-picker[ngModel],list-picker[formControlName],list-picker[formControl],' +
+ 'TabView[ngModel],TabView[formControlName],TabView[formControl],' +
+ 'tabView[ngModel],tabView[formControlName],tabView[formControl],' +
+ 'tabview[ngModel],tabview[formControlName],tabview[formControl],' +
+ 'tab-view[ngModel],tab-view[formControlName],tab-view[formControl]',
+ providers: [SELECTED_INDEX_VALUE_ACCESSOR],
+ host: {
+ '(selectedIndexChange)': 'onChange($event.value)',
+ },
})
-export class SelectedIndexValueAccessor extends BaseValueAccessor implements AfterViewInit { // tslint:disable-line:max-line-length directive-class-suffix
- constructor(elementRef: ElementRef) {
- super(elementRef.nativeElement);
- }
+export class SelectedIndexValueAccessor extends BaseValueAccessor implements AfterViewInit {
+ // tslint:disable-line:max-line-length directive-class-suffix
+ constructor(elementRef: ElementRef) {
+ super(elementRef.nativeElement);
+ }
- private value: number;
- private viewInitialized: boolean;
+ private value: number;
+ private viewInitialized: boolean;
- writeValue(value: any): void {
- const normalized = super.normalizeValue(value);
- this.value = normalized;
+ writeValue(value: any): void {
+ const normalized = super.normalizeValue(value);
+ this.value = normalized;
- if (this.viewInitialized) {
- this.view.selectedIndex = this.value;
- }
- }
+ if (this.viewInitialized) {
+ this.view.selectedIndex = this.value;
+ }
+ }
- ngAfterViewInit() {
- this.viewInitialized = true;
- this.view.selectedIndex = this.value;
- }
+ ngAfterViewInit() {
+ this.viewInitialized = true;
+ this.view.selectedIndex = this.value;
+ }
}
diff --git a/nativescript-angular/forms/value-accessors/text-value-accessor.ts b/nativescript-angular/forms/value-accessors/text-value-accessor.ts
index bd9751daf..c993e3490 100644
--- a/nativescript-angular/forms/value-accessors/text-value-accessor.ts
+++ b/nativescript-angular/forms/value-accessors/text-value-accessor.ts
@@ -1,15 +1,15 @@
-import { Directive, ElementRef, forwardRef } from "@angular/core";
-import { NG_VALUE_ACCESSOR } from "@angular/forms";
-import { BaseValueAccessor } from "./base-value-accessor";
-import { View } from "@nativescript/core/ui/core/view";
+import { Directive, ElementRef, forwardRef } from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+import { View } from '@nativescript/core';
+import { BaseValueAccessor } from './base-value-accessor';
const TEXT_VALUE_ACCESSOR = {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => TextValueAccessor),
- multi: true,
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => TextValueAccessor),
+ multi: true,
};
-export type TextView = {text: string} & View;
+export type TextView = { text: string } & View;
/**
* The accessor for writing a text and listening to changes that is used by the
@@ -21,34 +21,33 @@ export type TextView = {text: string} & View;
* ```
*/
@Directive({
- selector:
- "TextField[ngModel],TextField[formControlName],TextField[formControl]," +
- "textField[ngModel],textField[formControlName],textField[formControl]," +
- "textfield[ngModel],textfield[formControlName],textfield[formControl]," +
- "text-field[ngModel],text-field[formControlName],text-field[formControl]," +
-
- "TextView[ngModel],TextView[formControlName],TextView[formControl]," +
- "textView[ngModel],textView[formControlName],textView[formControl]," +
- "textview[ngModel],textview[formControlName],textview[formControl]," +
- "text-view[ngModel],text-view[formControlName],text-view[formControl]," +
-
- "SearchBar[ngModel],SearchBar[formControlName],SearchBar[formControl]," +
- "searchBar[ngModel],searchBar[formControlName],searchBar[formControl]," +
- "searchbar[ngModel],searchbar[formControlName],searchbar[formControl]," +
- "search-bar[ngModel], search-bar[formControlName],search-bar[formControl]",
- providers: [TEXT_VALUE_ACCESSOR],
- host: {
- "(blur)": "onTouched()",
- "(textChange)": "onChange($event.value)",
- },
+ selector:
+ 'TextField[ngModel],TextField[formControlName],TextField[formControl],' +
+ 'textField[ngModel],textField[formControlName],textField[formControl],' +
+ 'textfield[ngModel],textfield[formControlName],textfield[formControl],' +
+ 'text-field[ngModel],text-field[formControlName],text-field[formControl],' +
+ 'TextView[ngModel],TextView[formControlName],TextView[formControl],' +
+ 'textView[ngModel],textView[formControlName],textView[formControl],' +
+ 'textview[ngModel],textview[formControlName],textview[formControl],' +
+ 'text-view[ngModel],text-view[formControlName],text-view[formControl],' +
+ 'SearchBar[ngModel],SearchBar[formControlName],SearchBar[formControl],' +
+ 'searchBar[ngModel],searchBar[formControlName],searchBar[formControl],' +
+ 'searchbar[ngModel],searchbar[formControlName],searchbar[formControl],' +
+ 'search-bar[ngModel], search-bar[formControlName],search-bar[formControl]',
+ providers: [TEXT_VALUE_ACCESSOR],
+ host: {
+ '(blur)': 'onTouched()',
+ '(textChange)': 'onChange($event.value)',
+ },
})
-export class TextValueAccessor extends BaseValueAccessor { // tslint:disable-line:directive-class-suffix
- constructor(elementRef: ElementRef) {
- super(elementRef.nativeElement);
- }
+export class TextValueAccessor extends BaseValueAccessor {
+ // tslint:disable-line:directive-class-suffix
+ constructor(elementRef: ElementRef) {
+ super(elementRef.nativeElement);
+ }
- writeValue(value: any): void {
- const normalized = super.normalizeValue(value);
- this.view.text = normalized;
- }
+ writeValue(value: any): void {
+ const normalized = super.normalizeValue(value);
+ this.view.text = normalized;
+ }
}
diff --git a/nativescript-angular/forms/value-accessors/time-value-accessor.ts b/nativescript-angular/forms/value-accessors/time-value-accessor.ts
index da3593f2b..b15aa6c34 100644
--- a/nativescript-angular/forms/value-accessors/time-value-accessor.ts
+++ b/nativescript-angular/forms/value-accessors/time-value-accessor.ts
@@ -1,12 +1,12 @@
-import { Directive, ElementRef, forwardRef } from "@angular/core";
-import { NG_VALUE_ACCESSOR } from "@angular/forms";
-import { BaseValueAccessor } from "./base-value-accessor";
-import { TimePicker } from "@nativescript/core/ui/time-picker";
+import { Directive, ElementRef, forwardRef } from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+import { BaseValueAccessor } from './base-value-accessor';
+import { TimePicker } from '@nativescript/core';
const TIME_VALUE_ACCESSOR = {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => TimeValueAccessor),
- multi: true,
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => TimeValueAccessor),
+ multi: true,
};
/**
@@ -19,23 +19,20 @@ const TIME_VALUE_ACCESSOR = {
* ```
*/
@Directive({
- selector:
- "TimePicker[ngModel],TimePicker[formControlName],TimePicker[formControl]," +
- "timepicker[ngModel],timepicker[formControlName],timepicker[formControl]," +
- "timePicker[ngModel],timePicker[formControlName],timePicker[formControl]," +
- "time-picker[ngModel],time-picker[formControlName],time-picker[formControl]",
- providers: [TIME_VALUE_ACCESSOR],
- host: {
- "(timeChange)": "onChange($event.value)",
- },
+ selector: 'TimePicker[ngModel],TimePicker[formControlName],TimePicker[formControl],' + 'timepicker[ngModel],timepicker[formControlName],timepicker[formControl],' + 'timePicker[ngModel],timePicker[formControlName],timePicker[formControl],' + 'time-picker[ngModel],time-picker[formControlName],time-picker[formControl]',
+ providers: [TIME_VALUE_ACCESSOR],
+ host: {
+ '(timeChange)': 'onChange($event.value)',
+ },
})
-export class TimeValueAccessor extends BaseValueAccessor { // tslint:disable-line:directive-class-suffix
- constructor(elementRef: ElementRef) {
- super(elementRef.nativeElement);
- }
+export class TimeValueAccessor extends BaseValueAccessor {
+ // tslint:disable-line:directive-class-suffix
+ constructor(elementRef: ElementRef) {
+ super(elementRef.nativeElement);
+ }
- writeValue(value: any): void {
- const normalized = super.normalizeValue(value);
- this.view.time = normalized;
- }
+ writeValue(value: any): void {
+ const normalized = super.normalizeValue(value);
+ this.view.time = normalized;
+ }
}
diff --git a/nativescript-angular/frame.service.ts b/nativescript-angular/frame.service.ts
new file mode 100644
index 000000000..952a25d24
--- /dev/null
+++ b/nativescript-angular/frame.service.ts
@@ -0,0 +1,13 @@
+import { Injectable } from '@angular/core';
+import { Frame } from '@nativescript/core';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class FrameService {
+ // TODO: Add any methods that are needed to handle frame/page navigation
+ getFrame(): Frame {
+ let topmostFrame = Frame.topmost();
+ return topmostFrame;
+ }
+}
diff --git a/nativescript-angular/http-client/http-client.module.ts b/nativescript-angular/http-client/http-client.module.ts
index 59d55a05a..2f1c9a084 100644
--- a/nativescript-angular/http-client/http-client.module.ts
+++ b/nativescript-angular/http-client/http-client.module.ts
@@ -1,22 +1,13 @@
-import { NgModule } from "@angular/core";
+import { NgModule } from '@angular/core';
-import { HttpClientModule, HttpBackend } from "@angular/common/http";
+import { HttpClientModule, HttpBackend } from '@angular/common/http';
-import { NSFileSystem } from "../file-system/ns-file-system";
-import { NsHttpBackEnd } from "./ns-http-backend";
+import { NSFileSystem } from '../file-system/ns-file-system';
+import { NsHttpBackEnd } from './ns-http-backend';
@NgModule({
- providers: [
- NSFileSystem,
- NsHttpBackEnd,
- { provide: HttpBackend, useExisting: NsHttpBackEnd },
- ],
- imports: [
- HttpClientModule,
- ],
- exports: [
- HttpClientModule,
- ]
+ providers: [NSFileSystem, NsHttpBackEnd, { provide: HttpBackend, useExisting: NsHttpBackEnd }],
+ imports: [HttpClientModule],
+ exports: [HttpClientModule],
})
-export class NativeScriptHttpClientModule {
-}
+export class NativeScriptHttpClientModule {}
diff --git a/nativescript-angular/http-client/http-utils.ts b/nativescript-angular/http-client/http-utils.ts
index c8508016e..c719e4c2f 100644
--- a/nativescript-angular/http-client/http-utils.ts
+++ b/nativescript-angular/http-client/http-utils.ts
@@ -1,51 +1,47 @@
-import { NSFileSystem } from "../file-system/ns-file-system";
+import { NSFileSystem } from '../file-system/ns-file-system';
-import { Observable, Observer } from "rxjs";
-import { path } from "@nativescript/core/file-system/file-system";
+import { Observable, Observer } from 'rxjs';
+import { path } from '@nativescript/core';
export type httpResponseFactory = (url: string, body: any, status: number) => T;
export type httpErrorFactory = (url: string, body: any, status: number) => any;
export function isLocalRequest(url: string): boolean {
- return url.indexOf("~") === 0 || url.indexOf("/") === 0;
+ return url.indexOf('~') === 0 || url.indexOf('/') === 0;
}
export function getAbsolutePath(url: string, nsFileSystem: NSFileSystem): string {
- url = url.replace("~", "").replace("/", "");
- url = path.join(nsFileSystem.currentApp().path, url);
- return url;
+ url = url.replace('~', '').replace('/', '');
+ url = path.join(nsFileSystem.currentApp().path, url);
+ return url;
}
-export function processLocalFileRequest(
- url: string,
- nsFileSystem: NSFileSystem,
- successResponse: httpResponseFactory,
- errorResponse: httpErrorFactory): Observable {
-
- url = getAbsolutePath(url, nsFileSystem);
-
- // request from local app resources
- return new Observable((observer: Observer) => {
- if (nsFileSystem.fileExists(url)) {
- const localFile = nsFileSystem.fileFromPath(url);
- localFile.readText()
- .then((data) => {
- try {
- const json = JSON.parse(data);
- observer.next(successResponse(url, json, 200));
- observer.complete();
- } catch (error) {
- // Even though the response status was 2xx, this is still an error.
- // The parse error contains the text of the body that failed to parse.
- const errorResult = { error, text: data };
- observer.error(errorResponse(url, errorResult, 200));
- }
- }, (err: Object) => {
- observer.error(errorResponse(url, err, 400));
-
- });
- } else {
- observer.error(errorResponse(url, "Not Found", 404));
- }
- });
+export function processLocalFileRequest(url: string, nsFileSystem: NSFileSystem, successResponse: httpResponseFactory, errorResponse: httpErrorFactory): Observable {
+ url = getAbsolutePath(url, nsFileSystem);
+
+ // request from local app resources
+ return new Observable((observer: Observer) => {
+ if (nsFileSystem.fileExists(url)) {
+ const localFile = nsFileSystem.fileFromPath(url);
+ localFile.readText().then(
+ (data) => {
+ try {
+ const json = JSON.parse(data);
+ observer.next(successResponse(url, json, 200));
+ observer.complete();
+ } catch (error) {
+ // Even though the response status was 2xx, this is still an error.
+ // The parse error contains the text of the body that failed to parse.
+ const errorResult = { error, text: data };
+ observer.error(errorResponse(url, errorResult, 200));
+ }
+ },
+ (err: Object) => {
+ observer.error(errorResponse(url, err, 400));
+ }
+ );
+ } else {
+ observer.error(errorResponse(url, 'Not Found', 404));
+ }
+ });
}
diff --git a/nativescript-angular/http-client/index.ts b/nativescript-angular/http-client/index.ts
index da2e04db0..f4174328b 100644
--- a/nativescript-angular/http-client/index.ts
+++ b/nativescript-angular/http-client/index.ts
@@ -1,2 +1,2 @@
-export * from "./http-client.module";
-export * from "./ns-http-backend";
+export * from './http-client.module';
+export * from './ns-http-backend';
diff --git a/nativescript-angular/http-client/ns-http-backend.ts b/nativescript-angular/http-client/ns-http-backend.ts
index ed62c0150..a4ef9bc2a 100644
--- a/nativescript-angular/http-client/ns-http-backend.ts
+++ b/nativescript-angular/http-client/ns-http-backend.ts
@@ -1,62 +1,47 @@
-import { Injectable } from "@angular/core";
-import {
- HttpRequest, HttpEvent,
- XhrFactory, HttpResponse,
- HttpErrorResponse, HttpXhrBackend
-} from "@angular/common/http";
-import { Observable } from "rxjs";
+import { Injectable } from '@angular/core';
+import { HttpRequest, HttpEvent, XhrFactory, HttpResponse, HttpErrorResponse, HttpXhrBackend } from '@angular/common/http';
+import { Observable } from 'rxjs';
-import { NSFileSystem } from "../file-system/ns-file-system";
-import { isLocalRequest, processLocalFileRequest } from "./http-utils";
+import { NSFileSystem } from '../file-system/ns-file-system';
+import { isLocalRequest, processLocalFileRequest } from './http-utils';
@Injectable()
export class NsHttpBackEnd extends HttpXhrBackend {
- constructor(xhrFactory: XhrFactory, private nsFileSystem: NSFileSystem) {
- super(xhrFactory);
- }
-
- handle(req: HttpRequest): Observable> {
- let result: Observable>;
-
- if (isLocalRequest(req.url)) {
- result = this.handleLocalFileRequest(req.url);
- } else {
- result = super.handle(req);
- }
-
- return result;
- }
-
- private handleLocalFileRequest(url: string): Observable> {
- return processLocalFileRequest(
- url,
- this.nsFileSystem,
- createSuccessResponse,
- createErrorResponse
- );
- }
+ constructor(xhrFactory: XhrFactory, private nsFileSystem: NSFileSystem) {
+ super(xhrFactory);
+ }
+
+ handle(req: HttpRequest): Observable> {
+ let result: Observable>;
+
+ if (isLocalRequest(req.url)) {
+ result = this.handleLocalFileRequest(req.url);
+ } else {
+ result = super.handle(req);
+ }
+
+ return result;
+ }
+
+ private handleLocalFileRequest(url: string): Observable> {
+ return processLocalFileRequest(url, this.nsFileSystem, createSuccessResponse, createErrorResponse);
+ }
}
-function createSuccessResponse(
- url: string,
- body: any,
- status: number): HttpEvent {
- return new HttpResponse({
- url,
- body,
- status,
- statusText: "OK"
- });
+function createSuccessResponse(url: string, body: any, status: number): HttpEvent {
+ return new HttpResponse({
+ url,
+ body,
+ status,
+ statusText: 'OK',
+ });
}
-function createErrorResponse(
- url: string,
- body: any,
- status: number): HttpErrorResponse {
- return new HttpErrorResponse({
- url,
- error: body,
- status,
- statusText: "ERROR"
- });
+function createErrorResponse(url: string, body: any, status: number): HttpErrorResponse {
+ return new HttpErrorResponse({
+ url,
+ error: body,
+ status,
+ statusText: 'ERROR',
+ });
}
diff --git a/nativescript-angular/index.ts b/nativescript-angular/index.ts
index 0aa692a4a..6244a00e5 100644
--- a/nativescript-angular/index.ts
+++ b/nativescript-angular/index.ts
@@ -1,27 +1,38 @@
-import "@nativescript/core/application";
+// Initial imports and polyfills
+import '@nativescript/core';
+import '@nativescript/zone-js';
+// TODO: migrate to standard zone.js if possible
+// investigate Ivy with templated-items-comp to allow standard zone below to be used instead of patched {N} zone above
+// import 'zone.js/dist/zone';
+import './dom-adapter';
+import 'nativescript-intl';
+// import "./polyfills/array";
+import './polyfills/console';
-export * from "./platform-common";
-export * from "./platform";
-export * from "./platform-static";
-export * from "./platform-providers";
-export * from "./resource-loader";
+export * from './platform-common';
+export * from './platform-providers';
+export * from './platform';
+export * from './resource-loader';
-export * from "./nativescript.module";
-export * from "./common";
+export * from './nativescript.module';
+export * from './common';
+export * from './common/detached-loader';
+export * from './common/utils';
-export * from "./animations";
-export * from "./file-system";
-export * from "./http-client";
-export * from "./forms";
-export * from "./directives/dialogs";
-export * from "./router";
+export { NativeScriptAnimationsModule } from './animations';
+export * from './file-system';
+export * from './http-client';
+export * from './forms';
+export * from './directives';
+export * from './router';
+export * from './frame.service';
-export {
- ViewClass,
- ViewClassMeta,
- ViewResolver,
- getViewClass,
- getViewMeta,
- isKnownView,
- registerElement,
-} from "./element-registry";
+export { NativeScriptRenderer } from './renderer';
+export { EmulatedRenderer } from './renderer-emulated';
+export { NativeScriptRendererFactory } from './renderer-factory';
+
+// utils
+export { NativeScriptDebug } from './trace';
+export * from './app-host-view';
+export { getViewClass, getViewMeta, isKnownView, registerElement, CommentNode, getSingleViewRecursive, isInvisibleNode, isView } from './element-registry';
+export type { NgView, ViewClass, ViewClassMeta, ViewResolver, ViewExtensions } from './element-registry';
diff --git a/nativescript-angular/lang-facade.ts b/nativescript-angular/lang-facade.ts
index 31b567065..62b677fd3 100644
--- a/nativescript-angular/lang-facade.ts
+++ b/nativescript-angular/lang-facade.ts
@@ -1,7 +1,7 @@
export function isPresent(obj: any): boolean {
- return obj !== undefined && obj !== null;
+ return obj !== undefined && obj !== null;
}
export function isBlank(obj: any): boolean {
- return obj === undefined || obj === null;
+ return obj === undefined || obj === null;
}
diff --git a/nativescript-angular/modal-dialog.ts b/nativescript-angular/modal-dialog.ts
deleted file mode 100644
index 0ee522dcb..000000000
--- a/nativescript-angular/modal-dialog.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export {
- ModalDialogOptions,
- ModalDialogParams,
- ModalDialogService
-} from "./directives/dialogs";
diff --git a/nativescript-angular/nativescript.module.ts b/nativescript-angular/nativescript.module.ts
index 71563721a..27f682d33 100644
--- a/nativescript-angular/nativescript.module.ts
+++ b/nativescript-angular/nativescript.module.ts
@@ -1,70 +1,30 @@
-import "@nativescript/core/globals";
-// Require application early to work around a circular import
-import "@nativescript/core/application";
+import { ApplicationModule, ErrorHandler, NO_ERRORS_SCHEMA, NgModule, RendererFactory2, SystemJsNgModuleLoader, Optional, SkipSelf, ɵINJECTOR_SCOPE } from '@angular/core';
-import "./polyfills/array";
-import "./polyfills/console";
+import { ViewportScroller, ɵNullViewportScroller as NullViewportScroller } from '@angular/common';
-import {
- ApplicationModule,
- ErrorHandler,
- NO_ERRORS_SCHEMA,
- NgModule,
- RendererFactory2,
- SystemJsNgModuleLoader,
- Optional,
- SkipSelf,
- ɵINJECTOR_SCOPE,
-} from "@angular/core";
-
-import {
- ViewportScroller,
- ɵNullViewportScroller as NullViewportScroller,
-} from "@angular/common";
-
-import { NativeScriptCommonModule } from "./common";
-import { NativeScriptRendererFactory } from "./renderer";
-import { DetachedLoader } from "./common/detached-loader";
-import { throwIfAlreadyLoaded } from "./common/utils";
-import { FrameService, PageService } from "./platform-providers";
+import { NativeScriptCommonModule } from './common';
+import { NativeScriptRendererFactory } from './renderer-factory';
+import { DetachedLoader } from './common/detached-loader';
+import { throwIfAlreadyLoaded } from './common/utils';
+import { FrameService } from './frame.service';
export function errorHandlerFactory() {
- return new ErrorHandler();
+ return new ErrorHandler();
}
-export { DetachedLoader };
+export { DetachedLoader } from './common/detached-loader';
@NgModule({
- declarations: [
- DetachedLoader,
- ],
- providers: [
- FrameService,
- PageService,
- NativeScriptRendererFactory,
- SystemJsNgModuleLoader,
- { provide: ɵINJECTOR_SCOPE, useValue: "root" },
- { provide: ErrorHandler, useFactory: errorHandlerFactory },
- { provide: RendererFactory2, useExisting: NativeScriptRendererFactory },
- { provide: ViewportScroller, useClass: NullViewportScroller },
- ],
- entryComponents: [
- DetachedLoader,
- ],
- imports: [
- ApplicationModule,
- NativeScriptCommonModule,
- ],
- exports: [
- ApplicationModule,
- NativeScriptCommonModule,
- DetachedLoader,
- ],
- schemas: [NO_ERRORS_SCHEMA]
+ declarations: [DetachedLoader],
+ providers: [FrameService, NativeScriptRendererFactory, SystemJsNgModuleLoader, { provide: ɵINJECTOR_SCOPE, useValue: 'root' }, { provide: ErrorHandler, useFactory: errorHandlerFactory }, { provide: RendererFactory2, useExisting: NativeScriptRendererFactory }, { provide: ViewportScroller, useClass: NullViewportScroller }],
+ entryComponents: [DetachedLoader],
+ imports: [ApplicationModule, NativeScriptCommonModule],
+ exports: [ApplicationModule, NativeScriptCommonModule, DetachedLoader],
+ schemas: [NO_ERRORS_SCHEMA],
})
export class NativeScriptModule {
- constructor(@Optional() @SkipSelf() parentModule: NativeScriptModule) {
- // Prevents NativeScriptModule from getting imported multiple times
- throwIfAlreadyLoaded(parentModule, "NativeScriptModule");
- }
+ constructor(@Optional() @SkipSelf() parentModule: NativeScriptModule) {
+ // Prevents NativeScriptModule from getting imported multiple times
+ throwIfAlreadyLoaded(parentModule, 'NativeScriptModule');
+ }
}
diff --git a/nativescript-angular/package.json b/nativescript-angular/package.json
index a927665eb..938a48d95 100644
--- a/nativescript-angular/package.json
+++ b/nativescript-angular/package.json
@@ -1,10 +1,11 @@
{
"name": "@nativescript/angular",
- "version": "9.0.0",
+ "version": "10.0.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",
"main": "index.js",
+ "module": "index.js",
"types": "index.d.ts",
"author": {
"name": "NativeScript Team"
@@ -25,54 +26,83 @@
"url": "https://github.com/NativeScript/nativescript-angular.git"
},
"scripts": {
- "setup": "npx rimraf hooks node_modules package-lock.json && npm run pack",
- "tslint": "tslint --project tsconfig.json --config tslint.json",
+ "setup": "npx rimraf hooks node_modules package-lock.json && npm i && npm run prep.apps",
+ "prep.apps": "npm run build.pack && cd ../build/pack-scripts && npm i && npx ts-node pack-scoped.ts",
+ "build": "ng-packagr -p package.json",
+ "build.pack": "npm run tsc && npm run build && cd dist && npm pack",
+ "format": "npx prettier --write .",
+ "format-check": "npx prettier --check .",
"tsc": "tsc -p tsconfig.json",
"tsc-w": "tsc -p tsconfig.json -w",
"ngc": "ngc",
"ngcc": "ngcc",
- "prepare": "npm run ngcc && ngc -p tsconfig.json",
- "pack": "npm i && tsc && npm run prepare && cd ../build/pack-scripts && npm i && npx ts-node pack-scoped.ts",
+ "ngcc-run": "npm run ngcc && ngc -p tsconfig.json",
"changelog": "conventional-changelog -p angular -i ../CHANGELOG.md -s",
"version": "rm -rf package-lock.json && npm run changelog && 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"
+ "sideEffects": false,
+ "ngPackage": {
+ "assets": [
+ "../README.md"
+ ],
+ "lib": {
+ "entryFile": "index.ts",
+ "umdModuleIds": {
+ "@nativescript/core": "ns-core"
+ }
+ },
+ "whitelistedNonPeerDependencies": [
+ "."
+ ]
},
"dependencies": {
- "nativescript-intl": "^3.0.0"
+ "@nativescript/zone-js": "~1.0.0",
+ "nativescript-angular": "~10.0.0",
+ "nativescript-intl": "^4.0.0"
},
"peerDependencies": {
- "@angular/platform-browser-dynamic": "~9.1.0",
- "@angular/common": "~9.1.0",
- "@angular/compiler": "~9.1.0",
- "@angular/core": "~9.1.0",
- "@angular/forms": "~9.1.0",
- "@angular/platform-browser": "~9.1.0",
- "@angular/router": "~9.1.0",
- "rxjs": "~6.5.5",
- "typescript": "~3.8.3",
- "zone.js": "^0.10.3"
+ "@angular/common": "^10.0.0",
+ "@angular/compiler": "^10.0.0",
+ "@angular/core": "^10.0.0",
+ "@angular/forms": "^10.0.0",
+ "@angular/platform-browser": "^10.0.0",
+ "@angular/platform-browser-dynamic": "^10.0.0",
+ "@angular/router": "^10.0.0"
},
"devDependencies": {
- "@angular/animations": "~9.1.0",
- "@angular/common": "~9.1.0",
- "@angular/compiler": "~9.1.0",
- "@angular/compiler-cli": "~9.1.0",
- "@angular/core": "~9.1.0",
- "@angular/forms": "~9.1.0",
- "@angular/platform-browser": "~9.1.0",
- "@angular/platform-browser-dynamic": "~9.1.0",
- "@angular/router": "~9.1.0",
- "codelyzer": "^5.1.0",
- "conventional-changelog-cli": "^1.3.22",
- "rxjs": "~6.5.5",
- "@nativescript/core": "next",
- "tslint": "^5.5.0",
- "typescript": "~3.8.3",
- "zone.js": "^0.10.3",
+ "@angular/animations": "~10.0.0",
+ "@angular/common": "~10.0.0",
+ "@angular/compiler": "~10.0.0",
+ "@angular/compiler-cli": "~10.0.0",
+ "@angular/core": "~10.0.0",
+ "@angular/forms": "~10.0.0",
+ "@angular/platform-browser": "~10.0.0",
+ "@angular/platform-browser-dynamic": "~10.0.0",
+ "@angular/router": "~10.0.0",
+ "@nativescript/core": "rc",
+ "codelyzer": "^5.2.0",
+ "conventional-changelog-cli": "^2.0.34",
+ "husky": "^4.2.5",
+ "lint-staged": "^10.2.11",
"nativescript-typedoc-theme": "git://github.com/NativeScript/nativescript-typedoc-theme.git#master",
- "typedoc": "0.15.0"
+ "ng-packagr": "~10.0.0",
+ "prettier": "^2.0.5",
+ "rxjs": "~6.5.5",
+ "tslint": "^6.1.2",
+ "tslint-config-prettier": "^1.18.0",
+ "typedoc": "~0.17.0",
+ "typescript": "~3.9.0",
+ "zone.js": "^0.10.3"
+ },
+ "husky": {
+ "hooks": {
+ "pre-commit": "lint-staged"
+ }
+ },
+ "lint-staged": {
+ ".": [
+ "prettier --write ."
+ ]
}
}
diff --git a/nativescript-angular/platform-common.ts b/nativescript-angular/platform-common.ts
index 58e995ee9..913bd3f06 100644
--- a/nativescript-angular/platform-common.ts
+++ b/nativescript-angular/platform-common.ts
@@ -1,351 +1,344 @@
-// Initial imports and polyfills
-import "@nativescript/core/globals";
-// Require application early to work around a circular import
-import "@nativescript/core/application";
-import "./zone-js/dist/zone-nativescript";
-// TODO: migrate to standard zone.js if possible
-// investigate Ivy with templated-items-comp to allow standard zone below to be used instead of patched {N} zone above
-// import 'zone.js/dist/zone';
-import "./polyfills/array";
-import "./polyfills/console";
-import { profile, uptime } from "@nativescript/core/profiling";
-import { getRootView } from "@nativescript/core/application";
-import "./dom-adapter";
-import "nativescript-intl";
-// TODO: refactor core module imports to not require these deep imports
-import { TextView } from "@nativescript/core/ui/text-view";
-import { Color, View } from "@nativescript/core/ui/core/view";
-import { Frame } from "@nativescript/core/ui/frame";
-import { GridLayout } from "@nativescript/core/ui/layouts/grid-layout";
-
-import {
- Type,
- Injector,
- CompilerOptions,
- PlatformRef,
- NgModuleFactory,
- NgModuleRef,
- EventEmitter,
- Sanitizer,
- InjectionToken,
- StaticProvider,
-} from "@angular/core";
-import { DOCUMENT } from "@angular/common";
-
-import { bootstrapLog, bootstrapLogError, isLogEnabled } from "./trace";
-import { defaultPageFactoryProvider, setRootPage, PageFactory, PAGE_FACTORY, getRootPage } from "./platform-providers";
-
-import {
- setCssFileName,
- run as applicationRun,
- _resetRootView as applicationRerun,
- on,
- launchEvent,
- LaunchEventData,
- exitEvent,
- ApplicationEventData,
-} from "@nativescript/core/application";
+import { Application, TextView, Color, View, Frame, GridLayout, LaunchEventData, ApplicationEventData, profile, profilingUptime, Page, LayoutBase } from '@nativescript/core';
+// import './dom-adapter';
+// import 'nativescript-intl';
+
+import { Type, Injector, CompilerOptions, PlatformRef, NgModuleFactory, NgModuleRef, EventEmitter, Sanitizer, InjectionToken, StaticProvider } from '@angular/core';
+import { DOCUMENT } from '@angular/common';
+
+import { NativeScriptDebug } from './trace';
+import { defaultPageFactoryProvider, setRootPage, PageFactory, PAGE_FACTORY, getRootPage } from './platform-providers';
+import { AppHostView, AppHostAsyncView } from './app-host-view';
export const onBeforeLivesync = new EventEmitter>();
-export const onAfterLivesync = new EventEmitter<{ moduleRef?: NgModuleRef; error?: Error }>();
+export const onAfterLivesync = new EventEmitter<{
+ moduleRef?: NgModuleRef;
+ error?: Error;
+}>();
let lastBootstrappedModule: WeakRef>;
type BootstrapperAction = () => Promise>;
// Work around a TS bug requiring an import of OpaqueToken without using it
-if ((global).___TS_UNUSED) {
- (() => {
- return InjectionToken;
- })();
-}
+// if ((global).___TS_UNUSED) {
+// (() => {
+// return InjectionToken;
+// })();
+// }
// tslint:disable:max-line-length
/**
* Options to be passed when HMR is enabled
*/
export interface HmrOptions {
- /**
- * A factory function that returns either Module type or NgModuleFactory type.
- * This needs to be a factory function as the types will change when modules are replaced.
- */
- moduleTypeFactory?: () => Type | NgModuleFactory;
-
- /**
- * A livesync callback that will be called instead of the original livesync.
- * It gives the HMR a hook to apply the module replacement.
- * @param bootstrapPlatform - A bootstrap callback to be called after HMR is done. It will bootstrap a new angular app within the exisiting platform, using the moduleTypeFactory to get the Module or NgModuleFactory to be used.
- */
- livesyncCallback: (bootstrapPlatform: () => void) => void;
+ /**
+ * A factory function that returns either Module type or NgModuleFactory type.
+ * This needs to be a factory function as the types will change when modules are replaced.
+ */
+ moduleTypeFactory?: () => Type | NgModuleFactory;
+
+ /**
+ * A livesync callback that will be called instead of the original livesync.
+ * It gives the HMR a hook to apply the module replacement.
+ * @param bootstrapPlatform - A bootstrap callback to be called after HMR is done. It will bootstrap a new angular app within the exisiting platform, using the moduleTypeFactory to get the Module or NgModuleFactory to be used.
+ */
+ livesyncCallback: (bootstrapPlatform: () => void) => void;
}
// tslint:enable:max-line-length
-export interface AppLaunchView extends View {
- startAnimation?: () => void;
- cleanup?: () => void;
+export interface AppLaunchView extends LayoutBase {
+ // called when the animation is to begin
+ startAnimation?: () => void;
+ // called when bootstrap is complete and cleanup can begin
+ // should resolve when animation is completely finished
+ cleanup?: () => Promise;
}
export interface AppOptions {
- bootInExistingPage?: boolean;
- cssFile?: string;
- startPageActionBarHidden?: boolean;
- hmrOptions?: HmrOptions;
- backgroundColor?: string;
- launchView?: AppLaunchView;
+ bootInExistingPage?: boolean;
+ cssFile?: string;
+ startPageActionBarHidden?: boolean;
+ hmrOptions?: HmrOptions;
+ /**
+ * Background color of the root view
+ */
+ backgroundColor?: string;
+ /**
+ * Use animated launch view (async by default)
+ */
+ launchView?: AppLaunchView;
+ /**
+ * When using Async APP_INITIALIZER, set this to `true`.
+ * (Not needed when using launchView)
+ */
+ async?: boolean;
}
export type PlatformFactory = (extraProviders?: StaticProvider[]) => PlatformRef;
export class NativeScriptSanitizer extends Sanitizer {
- sanitize(_context: any, value: string): string {
- return value;
- }
+ sanitize(_context: any, value: string): string {
+ return value;
+ }
}
export class NativeScriptDocument {
- // Required by the AnimationDriver
- public body: any = {
- isOverride: true,
- };
-
- createElement(tag: string) {
- throw new Error("NativeScriptDocument is not DOM Document. There is no createElement() method.");
- }
+ // Required by the AnimationDriver
+ public body: any = {
+ isOverride: true,
+ };
+
+ createElement(tag: string) {
+ throw new Error('NativeScriptDocument is not DOM Document. There is no createElement() method.');
+ }
}
-export const COMMON_PROVIDERS = [
- defaultPageFactoryProvider,
- { provide: Sanitizer, useClass: NativeScriptSanitizer, deps: [] },
- { provide: DOCUMENT, useClass: NativeScriptDocument, deps: [] },
-];
+export const COMMON_PROVIDERS = [defaultPageFactoryProvider, { provide: Sanitizer, useClass: NativeScriptSanitizer, deps: [] }, { provide: DOCUMENT, useClass: NativeScriptDocument, deps: [] }];
export class NativeScriptPlatformRef extends PlatformRef {
- private _bootstrapper: BootstrapperAction;
-
- constructor(private platform: PlatformRef, private appOptions: AppOptions = {}) {
- super();
- }
-
- @profile
- bootstrapModuleFactory(moduleFactory: NgModuleFactory): Promise> {
- this._bootstrapper = () => {
- let bootstrapFactory = moduleFactory;
- if (this.appOptions.hmrOptions) {
- bootstrapFactory = >this.appOptions.hmrOptions.moduleTypeFactory();
- }
-
- return this.platform.bootstrapModuleFactory(bootstrapFactory);
- };
-
- this.bootstrapApp();
-
- return null; // Make the compiler happy
- }
-
- @profile
- bootstrapModule(
- moduleType: Type,
- compilerOptions: CompilerOptions | CompilerOptions[] = []
- ): Promise> {
- this._bootstrapper = () => {
- let bootstrapType = moduleType;
- if (this.appOptions.hmrOptions) {
- bootstrapType = >this.appOptions.hmrOptions.moduleTypeFactory();
- }
-
- return this.platform.bootstrapModule(bootstrapType, compilerOptions);
- };
- this.bootstrapApp();
-
- return null; // Make the compiler happy
- }
-
- @profile
- private bootstrapApp() {
- (global).__onLiveSyncCore = () => {
- if (this.appOptions.hmrOptions) {
- const rootView = getRootView();
- if (rootView) {
- rootView._closeAllModalViewsInternal();
- }
-
- this.appOptions.hmrOptions.livesyncCallback(() => this._livesync());
- } else {
- this._livesync();
- }
- };
-
- if (this.appOptions && typeof this.appOptions.cssFile === "string") {
- setCssFileName(this.appOptions.cssFile);
- }
-
- this.bootstrapNativeScriptApp();
- }
-
- onDestroy(callback: () => void): void {
- this.platform.onDestroy(callback);
- }
-
- get injector(): Injector {
- return this.platform.injector;
- }
-
- destroy(): void {
- this.platform.destroy();
- }
-
- get destroyed(): boolean {
- return this.platform.destroyed;
- }
-
- @profile
- private bootstrapNativeScriptApp() {
- let rootContent: View;
- let launchView: AppLaunchView;
-
- if (isLogEnabled()) {
- bootstrapLog("NativeScriptPlatform bootstrap started.");
- }
- const launchCallback = profile(
- "@nativescript/angular/platform-common.launchCallback",
- (args: LaunchEventData) => {
- if (isLogEnabled()) {
- bootstrapLog("Application launch event fired");
- }
-
- if (this.appOptions && this.appOptions.launchView) {
- launchView = this.appOptions.launchView;
- } else {
- launchView = new GridLayout();
- // Custom launch view color
- // Useful when using async app intializers to avoid flash of undesirable color
- launchView.backgroundColor = new Color(this.appOptions
- && this.appOptions.backgroundColor ? this.appOptions.backgroundColor : "#fff");
- }
-
- setRootPage(launchView);
- args.root = launchView;
-
- // Launch Angular app on next tick
- setTimeout(() => {
- if (this.appOptions && this.appOptions.launchView && this.appOptions.launchView.startAnimation) {
- // ensure launch animation is executed after launchView added to view stack
- this.appOptions.launchView.startAnimation();
- }
- this._bootstrapper().then(moduleRef => {
-
- if (isLogEnabled()) {
- bootstrapLog(`Angular bootstrap bootstrap done. uptime: ${uptime()}`);
- }
-
- rootContent = launchView;
- if (launchView && launchView.cleanup) {
- // cleanup any custom launch views
- launchView.cleanup();
- }
-
- lastBootstrappedModule = new WeakRef(moduleRef);
- },
- err => {
-
- const errorMessage = err.message + "\n\n" + err.stack;
- if (isLogEnabled()) {
- bootstrapLogError("ERROR BOOTSTRAPPING ANGULAR");
- }
- if (isLogEnabled()) {
- bootstrapLogError(errorMessage);
- }
-
- rootContent = this.createErrorUI(errorMessage);
- }
- );
- if (isLogEnabled()) {
- bootstrapLog("bootstrapAction called, draining micro tasks queue. Root: " + rootContent);
- }
- (global).Zone.drainMicroTaskQueue();
- if (isLogEnabled()) {
- bootstrapLog("bootstrapAction called, draining micro tasks queue finished! 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();
- }
-
- rootContent = null;
- }
- );
- on(launchEvent, launchCallback);
- on(exitEvent, exitCallback);
-
- applicationRun();
- }
-
- @profile
- public _livesync() {
- if (isLogEnabled()) {
- bootstrapLog("Angular livesync started.");
- }
-
- const lastModuleRef = lastBootstrappedModule ? lastBootstrappedModule.get() : null;
- onBeforeLivesync.next(lastModuleRef);
- if (lastModuleRef) {
- lastModuleRef.destroy();
- }
-
- this._bootstrapper().then(
- moduleRef => {
- if (isLogEnabled()) {
- bootstrapLog("Angular livesync done.");
- }
- onAfterLivesync.next({ moduleRef });
-
- lastBootstrappedModule = new WeakRef(moduleRef);
- applicationRerun({
- create: () => getRootPage(),
- });
- },
- error => {
- if (isLogEnabled()) {
- bootstrapLogError("ERROR LIVESYNC BOOTSTRAPPING ANGULAR");
- }
- const errorMessage = error.message + "\n\n" + error.stack;
- if (isLogEnabled()) {
- bootstrapLogError(errorMessage);
- }
-
- applicationRerun({
- create: () => this.createErrorUI(errorMessage),
- });
- onAfterLivesync.next({ error });
- }
- );
- }
-
- private createErrorUI(message: string): View {
- const errorTextBox = new TextView();
- errorTextBox.text = message;
- errorTextBox.color = new Color("red");
- return errorTextBox;
- }
-
- private createFrameAndPage(isLivesync: boolean) {
- const frame = new Frame();
- const pageFactory: PageFactory = this.platform.injector.get(PAGE_FACTORY);
- const page = pageFactory({ isBootstrap: true, isLivesync });
-
- frame.navigate({ create: () => { return page; } });
- return { page, frame };
- }
+ private _bootstrapper: BootstrapperAction;
+
+ constructor(private platform: PlatformRef, private appOptions: AppOptions = {}) {
+ super();
+ }
+
+ @profile
+ bootstrapModuleFactory(moduleFactory: NgModuleFactory): Promise> {
+ this._bootstrapper = () => {
+ let bootstrapFactory = moduleFactory;
+ if (this.appOptions.hmrOptions) {
+ bootstrapFactory = >this.appOptions.hmrOptions.moduleTypeFactory();
+ }
+
+ return this.platform.bootstrapModuleFactory(bootstrapFactory);
+ };
+
+ this.bootstrapApp();
+
+ return null; // Make the compiler happy
+ }
+
+ @profile
+ bootstrapModule(moduleType: Type, compilerOptions: CompilerOptions | CompilerOptions[] = []): Promise> {
+ this._bootstrapper = () => {
+ let bootstrapType = moduleType;
+ if (this.appOptions.hmrOptions) {
+ bootstrapType = >this.appOptions.hmrOptions.moduleTypeFactory();
+ }
+
+ return this.platform.bootstrapModule(bootstrapType, compilerOptions);
+ };
+ this.bootstrapApp();
+
+ return null; // Make the compiler happy
+ }
+
+ @profile
+ private bootstrapApp() {
+ (global).__onLiveSyncCore = () => {
+ if (this.appOptions.hmrOptions) {
+ const rootView = Application.getRootView();
+ if (rootView) {
+ rootView._closeAllModalViewsInternal();
+ }
+
+ this.appOptions.hmrOptions.livesyncCallback(() => this._livesync());
+ } else {
+ this._livesync();
+ }
+ };
+
+ if (this.appOptions && typeof this.appOptions.cssFile === 'string') {
+ Application.setCssFileName(this.appOptions.cssFile);
+ }
+
+ this.bootstrapNativeScriptApp();
+ }
+
+ onDestroy(callback: () => void): void {
+ this.platform.onDestroy(callback);
+ }
+
+ get injector(): Injector {
+ return this.platform.injector;
+ }
+
+ destroy(): void {
+ this.platform.destroy();
+ }
+
+ get destroyed(): boolean {
+ return this.platform.destroyed;
+ }
+
+ @profile
+ private bootstrapNativeScriptApp() {
+ let rootContent: View;
+
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLog('NativeScriptPlatform bootstrap started.');
+ }
+ const launchCallback = profile('@nativescript/angular/platform-common.launchCallback', (args: LaunchEventData) => {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLog('Application launch event fired');
+ }
+
+ // Create a temp page for root of the renderer
+ let tempAppHostView: AppHostView;
+ let tempAppHostAsyncView: AppHostAsyncView;
+ if (this.appOptions && (this.appOptions.async || this.appOptions.launchView)) {
+ tempAppHostAsyncView = new AppHostAsyncView(new Color(this.appOptions && this.appOptions.backgroundColor ? this.appOptions.backgroundColor : '#fff'));
+ if (this.appOptions.launchView) {
+ this.appOptions.launchView.style.zIndex = 1000;
+ tempAppHostAsyncView.addChild(this.appOptions.launchView);
+ }
+ rootContent = tempAppHostAsyncView.ngAppRoot;
+ setRootPage(tempAppHostAsyncView);
+ } else {
+ tempAppHostView = new AppHostView(new Color(this.appOptions && this.appOptions.backgroundColor ? this.appOptions.backgroundColor : '#fff'));
+ setRootPage(tempAppHostView);
+ }
+
+ let bootstrapPromiseCompleted = false;
+ const bootstrap = () => {
+ this._bootstrapper().then(
+ (moduleRef) => {
+ bootstrapPromiseCompleted = true;
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLog(`Angular bootstrap bootstrap done. uptime: ${profilingUptime()}`);
+ }
+
+ if (this.appOptions.launchView && this.appOptions.launchView.cleanup) {
+ this.appOptions.launchView.cleanup().then(() => {
+ // cleanup any custom launch views
+ tempAppHostAsyncView.removeChild(this.appOptions.launchView);
+ this.appOptions.launchView = null;
+ });
+ } else if (tempAppHostView) {
+ rootContent = tempAppHostView.content;
+ }
+
+ lastBootstrappedModule = new WeakRef(moduleRef);
+ },
+ (err) => {
+ bootstrapPromiseCompleted = true;
+ const errorMessage = err.message + '\n\n' + err.stack;
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLogError('ERROR BOOTSTRAPPING ANGULAR');
+ }
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLogError(errorMessage);
+ }
+
+ rootContent = this.createErrorUI(errorMessage);
+ }
+ );
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLog('bootstrapAction called, draining micro tasks queue. Root: ' + rootContent);
+ }
+ (global).Zone.drainMicroTaskQueue();
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLog('bootstrapAction called, draining micro tasks queue finished! Root: ' + rootContent);
+ }
+ };
+
+ if (this.appOptions && this.appOptions.launchView && this.appOptions.launchView.startAnimation) {
+ // start animations on next tick (after initial boot)
+ setTimeout(() => {
+ // ensure launch animation is executed after launchView added to view stack
+ this.appOptions.launchView.startAnimation();
+ });
+ }
+ bootstrap();
+ // if (!bootstrapPromiseCompleted) {
+ // const errorMessage = "Bootstrap promise didn't resolve";
+ // if (NativeScriptDebug.isLogEnabled()) {
+ // NativeScriptDebug.bootstrapLogError(errorMessage);
+ // }
+ // rootContent = this.createErrorUI(errorMessage);
+ // }
+ 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();
+ }
+
+ rootContent = null;
+ });
+
+ Application.on(Application.launchEvent, launchCallback);
+ Application.on(Application.exitEvent, exitCallback);
+
+ Application.run();
+ }
+
+ @profile
+ public _livesync() {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLog('Angular livesync started.');
+ }
+
+ const lastModuleRef = lastBootstrappedModule ? lastBootstrappedModule.get() : null;
+ onBeforeLivesync.next(lastModuleRef);
+ if (lastModuleRef) {
+ lastModuleRef.destroy();
+ }
+
+ this._bootstrapper().then(
+ (moduleRef) => {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLog('Angular livesync done.');
+ }
+ onAfterLivesync.next({ moduleRef });
+
+ lastBootstrappedModule = new WeakRef(moduleRef);
+ Application.resetRootView({
+ create: () => getRootPage(),
+ });
+ },
+ (error) => {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLogError('ERROR LIVESYNC BOOTSTRAPPING ANGULAR');
+ }
+ const errorMessage = error.message + '\n\n' + error.stack;
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.bootstrapLogError(errorMessage);
+ }
+
+ Application.resetRootView({
+ create: () => this.createErrorUI(errorMessage),
+ });
+ onAfterLivesync.next({ error });
+ }
+ );
+ }
+
+ private createErrorUI(message: string): View {
+ const errorTextBox = new TextView();
+ errorTextBox.text = message;
+ errorTextBox.color = new Color('red');
+ return errorTextBox;
+ }
+
+ private createFrameAndPage(isLivesync: boolean) {
+ const frame = new Frame();
+ const pageFactory: PageFactory = this.platform.injector.get(PAGE_FACTORY);
+ const page = pageFactory({ isBootstrap: true, isLivesync });
+
+ frame.navigate({
+ create: () => {
+ return page;
+ },
+ });
+ return { page, frame };
+ }
}
diff --git a/nativescript-angular/platform-providers.ts b/nativescript-angular/platform-providers.ts
index 5941ff7e5..a7ad87f2b 100644
--- a/nativescript-angular/platform-providers.ts
+++ b/nativescript-angular/platform-providers.ts
@@ -1,117 +1,64 @@
-import { InjectionToken, Injectable, OnDestroy } from "@angular/core";
+import { InjectionToken, Injectable, OnDestroy } from '@angular/core';
-import { Frame, NavigatedData } from "@nativescript/core/ui/frame";
-import { View } from "@nativescript/core/ui/core/view";
-import { Page } from "@nativescript/core/ui/page";
-import { device, Device } from "@nativescript/core/platform";
-import { BehaviorSubject, Subject, Observable } from "rxjs";
-import { distinctUntilChanged } from "rxjs/operators";
+import { Frame, View, Page, IDevice, Device } from '@nativescript/core';
-export const APP_ROOT_VIEW = new InjectionToken("App Root View");
-export const DEVICE = new InjectionToken("platform device");
-export const PAGE_FACTORY = new InjectionToken("page factory");
+export const APP_ROOT_VIEW = new InjectionToken('NativeScriptAppRootView');
+export const DeviceToken = new InjectionToken('NativeScriptPlatformDeviceToken');
+
+export type PageFactory = (options: PageFactoryOptions) => Page;
+export interface PageFactoryOptions {
+ isBootstrap?: boolean;
+ isLivesync?: boolean;
+ isModal?: boolean;
+ isNavigation?: boolean;
+ componentType?: any;
+}
+export const PAGE_FACTORY = new InjectionToken('NativeScriptPageFactory');
+export const defaultPageFactory: PageFactory = function (_opts: PageFactoryOptions) {
+ return new Page();
+};
+export const defaultPageFactoryProvider = { provide: PAGE_FACTORY, useValue: defaultPageFactory };
let _rootPageRef: WeakRef;
export function setRootPage(page: Page): void {
- _rootPageRef = new WeakRef(page);
+ _rootPageRef = new WeakRef(page);
}
export function getRootPage(): Page {
- return _rootPageRef && _rootPageRef.get();
+ return _rootPageRef && _rootPageRef.get();
}
// Use an exported function to make the AoT compiler happy.
export function getDefaultPage(): Page {
- const rootPage = getRootPage();
- if (rootPage instanceof Page) {
- return rootPage;
- }
-
- const frame = Frame.topmost();
- if (frame && frame.currentPage) {
- return frame.currentPage;
- }
-
- return null;
+ const rootPage = getRootPage();
+ if (rootPage instanceof Page) {
+ return rootPage;
+ }
+ // if (rootPage) {
+ // return rootPage;
+ // }
+
+ const frame = Frame.topmost();
+ if (frame && frame.currentPage) {
+ return frame.currentPage;
+ }
+
+ return null;
}
export const defaultPageProvider = { provide: Page, useFactory: getDefaultPage };
// Use an exported function to make the AoT compiler happy.
export function getDefaultFrame(): Frame {
- return Frame.topmost();
+ return Frame.topmost();
}
export const defaultFrameProvider = { provide: Frame, useFactory: getDefaultFrame };
// Use an exported function to make the AoT compiler happy.
-export function getDefaultDevice(): Device {
- return device;
+export function getDefaultDevice(): IDevice {
+ return Device;
}
-export const defaultDeviceProvider = { provide: DEVICE, useFactory: getDefaultDevice };
-
-export type PageFactory = (options: PageFactoryOptions) => Page;
-export interface PageFactoryOptions {
- isBootstrap?: boolean;
- isLivesync?: boolean;
- isModal?: boolean;
- isNavigation?: boolean;
- componentType?: any;
-}
-export const defaultPageFactory: PageFactory = function (_opts: PageFactoryOptions) {
- return new Page();
-};
-export const defaultPageFactoryProvider = { provide: PAGE_FACTORY, useValue: defaultPageFactory };
-
-@Injectable()
-export class FrameService {
- // TODO: Add any methods that are needed to handle frame/page navigation
- getFrame(): Frame {
- let topmostFrame = Frame.topmost();
- 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:
- }
- }
-}
+export const defaultDeviceProvider = { provide: DeviceToken, useFactory: getDefaultDevice };
diff --git a/nativescript-angular/platform-static.ts b/nativescript-angular/platform-static.ts
deleted file mode 100644
index ed85f38e6..000000000
--- a/nativescript-angular/platform-static.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-// Always import platform-common first - because polyfills
-import {
- NativeScriptPlatformRef,
- AppOptions,
- COMMON_PROVIDERS,
- PlatformFactory
-} from "./platform-common";
-
-import { platformCore, PlatformRef, createPlatformFactory } from "@angular/core";
-
-// "Static" platform
-const _platformNativeScript: PlatformFactory = createPlatformFactory(
- platformCore, "nativeScript", [...COMMON_PROVIDERS]);
-
-export function platformNativeScript(options?: AppOptions, extraProviders?: any[]): PlatformRef {
- // Return raw platform to advanced users only if explicitly requested
- if (options && options.bootInExistingPage === true) {
- return _platformNativeScript(extraProviders);
- } else {
- return new NativeScriptPlatformRef(_platformNativeScript(extraProviders), options);
- }
-}
diff --git a/nativescript-angular/platform.ts b/nativescript-angular/platform.ts
index 8dadb4836..12e83ffbb 100644
--- a/nativescript-angular/platform.ts
+++ b/nativescript-angular/platform.ts
@@ -1,69 +1,15 @@
-import {
- NativeScriptPlatformRef,
- AppOptions,
- PlatformFactory,
- COMMON_PROVIDERS
-} from "./platform-common";
+import { PlatformRef, platformCore, createPlatformFactory } from '@angular/core';
-import { NSFileSystem } from "./file-system/ns-file-system";
+import { NativeScriptPlatformRef, AppOptions, PlatformFactory, COMMON_PROVIDERS } from './platform-common';
-import {
- ElementSchemaRegistry,
- ResourceLoader,
-} from "@angular/compiler";
+// "Static" platform
+const _platformNativeScript: PlatformFactory = createPlatformFactory(platformCore, 'nativeScript', [...COMMON_PROVIDERS]);
-import {
- ɵplatformCoreDynamic as platformCoreDynamic
-} from "@angular/platform-browser-dynamic";
-
-import {
- COMPILER_OPTIONS,
- PlatformRef,
- InjectionToken,
- ViewEncapsulation,
- createPlatformFactory,
- MissingTranslationStrategy,
- StaticProvider,
-} from "@angular/core";
-
-// Work around a TS bug requiring an imports of
-// InjectionToken, ViewEncapsulation and MissingTranslationStrategy
-// without using them
-if ((global).___TS_UNUSED) {
- (() => InjectionToken)();
- (() => ViewEncapsulation)();
- (() => MissingTranslationStrategy)();
-}
-
-import { NativeScriptElementSchemaRegistry } from "./schema-registry";
-import { FileSystemResourceLoader } from "./resource-loader";
-
-export const NS_COMPILER_PROVIDERS: StaticProvider[] = [
- {
- provide: COMPILER_OPTIONS,
- useValue: {
- providers: [
- { provide: NSFileSystem, deps: [] },
- { provide: ResourceLoader, useClass: FileSystemResourceLoader, deps: [NSFileSystem] },
- { provide: ElementSchemaRegistry, useClass: NativeScriptElementSchemaRegistry, deps: [] },
- ]
- },
- multi: true
- },
-];
-
-// Dynamic platform
-const _platformNativeScriptDynamic: PlatformFactory = createPlatformFactory(
- platformCoreDynamic, "nativeScriptDynamic", [...COMMON_PROVIDERS, ...NS_COMPILER_PROVIDERS]);
-
-export function platformNativeScriptDynamic(
- options?: AppOptions,
- extraProviders?: any[]
-): PlatformRef {
- // Return raw platform to advanced users only if explicitly requested
- if (options && options.bootInExistingPage === true) {
- return _platformNativeScriptDynamic(extraProviders);
- } else {
- return new NativeScriptPlatformRef(_platformNativeScriptDynamic(extraProviders), options);
- }
+export function platformNativeScriptDynamic(options?: AppOptions, extraProviders?: any[]): PlatformRef {
+ // Return raw platform to advanced users only if explicitly requested
+ if (options && options.bootInExistingPage === true) {
+ return _platformNativeScript(extraProviders);
+ } else {
+ return new NativeScriptPlatformRef(_platformNativeScript(extraProviders), options);
+ }
}
diff --git a/nativescript-angular/polyfills/array.ts b/nativescript-angular/polyfills/array.ts
index a08882398..43e3e0b68 100644
--- a/nativescript-angular/polyfills/array.ts
+++ b/nativescript-angular/polyfills/array.ts
@@ -1,50 +1,43 @@
-if (!(Array.prototype).fill) {
- (Array.prototype).fill = function(value) {
+if (!(Array.prototype).fill) {
+ (Array.prototype).fill = function (value) {
+ let O = Object(this);
+ let len = parseInt(O.length, 10);
+ let start = arguments[1];
+ let relativeStart = parseInt(start, 10) || 0;
+ let k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
+ let end = arguments[2];
+ let relativeEnd = end === undefined ? len : parseInt(end, 10) || 0;
+ let final = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len);
- let O = Object(this);
- let len = parseInt(O.length, 10);
- let start = arguments[1];
- let relativeStart = parseInt(start, 10) || 0;
- let k = relativeStart < 0
- ? Math.max( len + relativeStart, 0)
- : Math.min( relativeStart, len );
- let end = arguments[2];
- let relativeEnd = end === undefined
- ? len
- : (parseInt(end, 10) || 0) ;
- let final = relativeEnd < 0
- ? Math.max(len + relativeEnd, 0)
- : Math.min(relativeEnd, len);
+ for (; k < final; k++) {
+ O[k] = value;
+ }
- for (; k < final; k++) {
- O[k] = value;
- }
-
- return O;
- };
+ return O;
+ };
}
if (!(Array).from) {
- (Array).from = function(iterable, mapFn, thisArg) {
- let results: Array = [];
+ (Array).from = function (iterable, mapFn, thisArg) {
+ let results: Array = [];
- if (iterable.next) {
- // Iterator objects
- for (let step = null; ; step = iterable.next()) {
- if (step.done) {
- break;
- } else {
- results.push(step.value);
- }
- }
- } else {
- // Array-like objects
- results = [].slice.call(iterable);
- }
+ if (iterable.next) {
+ // Iterator objects
+ for (let step = null; ; step = iterable.next()) {
+ if (step.done) {
+ break;
+ } else {
+ results.push(step.value);
+ }
+ }
+ } else {
+ // Array-like objects
+ results = [].slice.call(iterable);
+ }
- if (mapFn) {
- results = >results.forEach(mapFn, thisArg);
- }
- return results;
- };
+ if (mapFn) {
+ results = >(results.forEach(mapFn, thisArg));
+ }
+ return results;
+ };
}
diff --git a/nativescript-angular/polyfills/console.ts b/nativescript-angular/polyfills/console.ts
index 383ae203e..80b8bbbad 100644
--- a/nativescript-angular/polyfills/console.ts
+++ b/nativescript-angular/polyfills/console.ts
@@ -1,7 +1,7 @@
if (!console.group) {
- console.group = () => {};
+ console.group = () => {};
}
if (!console.groupEnd) {
- console.groupEnd = () => {};
+ console.groupEnd = () => {};
}
diff --git a/nativescript-angular/renderer-emulated.ts b/nativescript-angular/renderer-emulated.ts
new file mode 100644
index 000000000..d009fef27
--- /dev/null
+++ b/nativescript-angular/renderer-emulated.ts
@@ -0,0 +1,63 @@
+import { Inject, Injectable, RendererFactory2, Optional, NgZone, RendererType2 } from '@angular/core';
+import { View, getViewById, Application, profile } from '@nativescript/core';
+import { APP_ROOT_VIEW, getRootPage } from './platform-providers';
+import { ViewUtil } from './view-util';
+import { NgView, InvisibleNode } from './element-registry';
+import { NativeScriptDebug } from './trace';
+import { NativeScriptRenderer } from './renderer';
+
+// CONTENT_ATTR not exported from NativeScript_renderer - we need it for styles application.
+const COMPONENT_REGEX = /%COMP%/g;
+const ATTR_SANITIZER = /-/g;
+export const COMPONENT_VARIABLE = '%COMP%';
+export const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
+export const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
+
+const replaceNgAttribute = function (input: string, componentId: string): string {
+ return input.replace(COMPONENT_REGEX, componentId);
+};
+
+const addScopedStyleToCss = profile(`"renderer".addScopedStyleToCss`, function addScopedStyleToCss(style: string): void {
+ Application.addCss(style, true);
+});
+
+@Injectable()
+export class EmulatedRenderer extends NativeScriptRenderer {
+ private contentAttr: string;
+ private hostAttr: string;
+
+ constructor(component: RendererType2, rootView: NgView, zone: NgZone, viewUtil: ViewUtil) {
+ super(rootView, zone, viewUtil);
+
+ const componentId = component.id.replace(ATTR_SANITIZER, '_');
+ this.contentAttr = replaceNgAttribute(CONTENT_ATTR, componentId);
+ this.hostAttr = replaceNgAttribute(HOST_ATTR, componentId);
+ this.addStyles(component.styles, componentId);
+ }
+
+ applyToHost(view: NgView) {
+ super.setAttribute(view, this.hostAttr, '');
+ }
+
+ appendChild(parent: any, newChild: NgView): void {
+ super.appendChild(parent, newChild);
+ }
+
+ createElement(parent: any, name: string): NgView {
+ const view = super.createElement(parent, name);
+
+ // Set an attribute to the view to scope component-specific css.
+ // The property name is pre-generated by Angular.
+ super.setAttribute(view, this.contentAttr, '');
+
+ return view;
+ }
+
+ @profile
+ private addStyles(styles: (string | any[])[], componentId: string) {
+ styles
+ .map((s) => s.toString())
+ .map((s) => replaceNgAttribute(s, componentId))
+ .forEach(addScopedStyleToCss);
+ }
+}
diff --git a/nativescript-angular/renderer-factory.ts b/nativescript-angular/renderer-factory.ts
new file mode 100644
index 000000000..d77694b56
--- /dev/null
+++ b/nativescript-angular/renderer-factory.ts
@@ -0,0 +1,71 @@
+import { Inject, Injectable, RendererFactory2, Optional, NgZone, RendererType2, ViewEncapsulation } from '@angular/core';
+import { View, getViewById, Application, profile, Device } from '@nativescript/core';
+import { APP_ROOT_VIEW, getRootPage } from './platform-providers';
+import { ViewUtil } from './view-util';
+import { NgView, InvisibleNode } from './element-registry';
+import { NativeScriptDebug } from './trace';
+import { NativeScriptRenderer } from './renderer';
+import { EmulatedRenderer } from './renderer-emulated';
+
+const addStyleToCss = profile('"renderer".addStyleToCss', function addStyleToCss(style: string): void {
+ Application.addCss(style);
+});
+
+@Injectable()
+export class NativeScriptRendererFactory implements RendererFactory2 {
+ componentRenderers = new Map();
+ viewUtil: ViewUtil;
+ defaultRenderer: NativeScriptRenderer;
+ rootNgView: NgView;
+
+ constructor(@Optional() @Inject(APP_ROOT_VIEW) rootView: View, private zone: NgZone) {
+ this.viewUtil = new ViewUtil(Device);
+ this.setRootNgView(rootView);
+ this.defaultRenderer = new NativeScriptRenderer(this.rootNgView, zone, this.viewUtil);
+ }
+
+ private setRootNgView(rootView: any) {
+ if (!rootView) {
+ rootView = getRootPage();
+ }
+
+ rootView.nodeName = 'NONE';
+ this.rootNgView = rootView;
+ }
+
+ createRenderer(element: any, type: RendererType2): NativeScriptRenderer {
+ if (!element || !type) {
+ return this.defaultRenderer;
+ }
+
+ let renderer = this.componentRenderers.get(type.id);
+ if (renderer) {
+ if (renderer instanceof EmulatedRenderer) {
+ renderer.applyToHost(element);
+ }
+
+ return renderer;
+ }
+
+ if (type.encapsulation === ViewEncapsulation.None) {
+ type.styles.map((s) => s.toString()).forEach(addStyleToCss);
+ renderer = this.defaultRenderer;
+ } else {
+ renderer = new EmulatedRenderer(type, this.rootNgView, this.zone, this.viewUtil);
+ (renderer).applyToHost(element);
+ }
+
+ this.componentRenderers.set(type.id, renderer);
+ return renderer;
+ }
+
+ ngOnDestroy(): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRendererFactory.ngOnDestroy()`);
+ }
+
+ while (this.rootNgView && this.rootNgView.firstChild) {
+ this.viewUtil.removeChild(this.rootNgView, this.rootNgView.firstChild);
+ }
+ }
+}
diff --git a/nativescript-angular/renderer.ts b/nativescript-angular/renderer.ts
index 508284f36..bc6f8a5f8 100644
--- a/nativescript-angular/renderer.ts
+++ b/nativescript-angular/renderer.ts
@@ -1,375 +1,234 @@
-import {
- Inject, Injectable, Optional, NgZone,
- Renderer2, RendererFactory2, RendererType2,
- RendererStyleFlags2, ViewEncapsulation,
-} from "@angular/core";
+import { Inject, Injectable, Optional, NgZone, Renderer2, RendererFactory2, RendererType2, RendererStyleFlags2, ViewEncapsulation } from '@angular/core';
-import { Device } from "@nativescript/core/platform";
-import { View, getViewById } from "@nativescript/core/ui/core/view";
-import { addCss } from "@nativescript/core/application";
-import { profile } from "@nativescript/core/profiling";
+import { View, getViewById, profile } from '@nativescript/core';
-import { APP_ROOT_VIEW, DEVICE, getRootPage } from "./platform-providers";
-import { ViewUtil } from "./view-util";
-import { NgView, InvisibleNode } from "./element-registry";
-import { rendererLog as traceLog, isLogEnabled } from "./trace";
-
-// CONTENT_ATTR not exported from NativeScript_renderer - we need it for styles application.
-const COMPONENT_REGEX = /%COMP%/g;
-export const COMPONENT_VARIABLE = "%COMP%";
-export const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
-export const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
-const ATTR_SANITIZER = /-/g;
+import { ViewUtil } from './view-util';
+import { NgView, InvisibleNode } from './element-registry';
+import { NativeScriptDebug } from './trace';
export interface ElementReference {
- previous: NgView;
- next: NgView;
+ previous: NgView;
+ next: NgView;
}
@Injectable()
-export class NativeScriptRendererFactory implements RendererFactory2 {
- private componentRenderers = new Map();
- private viewUtil: ViewUtil;
- private defaultRenderer: NativeScriptRenderer;
- private rootNgView: NgView;
-
- constructor(
- @Optional() @Inject(APP_ROOT_VIEW) rootView: View,
- @Inject(DEVICE) device: Device,
- private zone: NgZone
- ) {
- this.viewUtil = new ViewUtil(device);
- this.setRootNgView(rootView);
- this.defaultRenderer = new NativeScriptRenderer(this.rootNgView, zone, this.viewUtil);
- }
-
- private setRootNgView(rootView: any) {
- if (!rootView) {
- rootView = getRootPage();
- }
-
- rootView.nodeName = "NONE";
- this.rootNgView = rootView;
- }
-
- createRenderer(element: any, type: RendererType2): NativeScriptRenderer {
- if (!element || !type) {
- return this.defaultRenderer;
- }
-
- let renderer = this.componentRenderers.get(type.id);
- if (renderer) {
- if (renderer instanceof EmulatedRenderer) {
- renderer.applyToHost(element);
- }
-
- return renderer;
- }
-
- if (type.encapsulation === ViewEncapsulation.None) {
- type.styles.map(s => s.toString()).forEach(addStyleToCss);
- renderer = this.defaultRenderer;
- } else {
- renderer = new EmulatedRenderer(type, this.rootNgView, this.zone, this.viewUtil);
- (renderer).applyToHost(element);
- }
-
- this.componentRenderers.set(type.id, renderer);
- return renderer;
- }
-
- ngOnDestroy(): void {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRendererFactory.ngOnDestroy()`);
- }
-
- while (this.rootNgView && this.rootNgView.firstChild) {
- this.viewUtil.removeChild(this.rootNgView, this.rootNgView.firstChild);
- }
- }
-}
-
export class NativeScriptRenderer extends Renderer2 {
- data: { [key: string]: any } = Object.create(null);
-
- constructor(
- private rootView: NgView,
- private zone: NgZone,
- private viewUtil: ViewUtil
- ) {
- super();
- if (isLogEnabled()) {
- traceLog("NativeScriptRenderer created");
- }
- }
-
- @profile
- appendChild(parent: NgView, newChild: NgView): void {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.appendChild child: ${newChild} parent: ${parent}`);
- }
- this.viewUtil.insertChild(parent, newChild);
- }
-
- @profile
- insertBefore(parent: NgView, newChild: NgView, { previous, next }: ElementReference): void {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.insertBefore child: ${newChild} ` +
- `parent: ${parent} previous: ${previous} next: ${next}`);
- }
- this.viewUtil.insertChild(parent, newChild, previous, next);
- }
-
- @profile
- removeChild(parent: any, oldChild: NgView): void {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.removeChild child: ${oldChild} parent: ${parent}`);
- }
- this.viewUtil.removeChild(parent, oldChild);
- }
-
- @profile
- selectRootElement(selector: string): NgView {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.selectRootElement: ${selector}`);
- }
- if (selector && selector[0] === "#") {
- const result = getViewById(this.rootView, selector.slice(1));
- return (result || this.rootView) as NgView;
- }
- return this.rootView;
- }
-
- @profile
- parentNode(node: NgView): any {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.parentNode for node: ${node} is ${node.parentNode}`);
- }
- return node.parentNode;
- }
-
- @profile
- nextSibling(node: NgView): ElementReference {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.nextSibling of ${node} is ${node.nextSibling}`);
- }
-
- return {
- previous: node,
- next: node.nextSibling,
- };
- }
-
- @profile
- createComment(_value: any): InvisibleNode {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.createComment ${_value}`);
- }
- return this.viewUtil.createComment();
- }
-
- @profile
- createElement(name: any, _namespace: string): NgView {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.createElement: ${name}`);
- }
- return this.viewUtil.createView(name);
- }
-
- @profile
- createText(_value: string): InvisibleNode {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.createText ${_value}`);
- }
- return this.viewUtil.createText();
- }
-
- @profile
- createViewRoot(hostElement: NgView): NgView {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.createViewRoot ${hostElement.nodeName}`);
- }
- return hostElement;
- }
-
- @profile
- projectNodes(parentElement: NgView, nodes: NgView[]): void {
- if (isLogEnabled()) {
- traceLog("NativeScriptRenderer.projectNodes");
- }
- nodes.forEach((node) => this.viewUtil.insertChild(parentElement, node));
- }
-
- @profile
- destroy() {
- if (isLogEnabled()) {
- traceLog("NativeScriptRenderer.destroy");
- }
- // Seems to be called on component dispose only (router outlet)
- // TODO: handle this when we resolve routing and navigation.
- }
-
- @profile
- setAttribute(view: NgView, name: string, value: string, namespace?: string) {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.setAttribute ${view} : ${name} = ${value}, namespace: ${namespace}`);
- }
- return this.viewUtil.setProperty(view, name, value, namespace);
- }
-
- @profile
- removeAttribute(_el: NgView, _name: string): void {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.removeAttribute ${_el}: ${_name}`);
- }
- }
-
- @profile
- setProperty(view: any, name: string, value: any) {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.setProperty ${view} : ${name} = ${value}`);
- }
- return this.viewUtil.setProperty(view, name, value);
- }
-
- @profile
- addClass(view: NgView, name: string): void {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.addClass ${name}`);
- }
- this.viewUtil.addClass(view, name);
- }
-
- @profile
- removeClass(view: NgView, name: string): void {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.removeClass ${name}`);
- }
- this.viewUtil.removeClass(view, name);
- }
-
- @profile
- setStyle(view: NgView, styleName: string, value: any, _flags?: RendererStyleFlags2): void {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.setStyle: ${styleName} = ${value}`);
- }
- this.viewUtil.setStyle(view, styleName, value);
- }
-
- @profile
- removeStyle(view: NgView, styleName: string, _flags?: RendererStyleFlags2): void {
- if (isLogEnabled()) {
- traceLog("NativeScriptRenderer.removeStyle: ${styleName}");
- }
- this.viewUtil.removeStyle(view, styleName);
- }
-
- // Used only in debug mode to serialize property changes to comment nodes,
- // such as placeholders.
- @profile
- setBindingDebugInfo(renderElement: NgView, propertyName: string, propertyValue: string): void {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.setBindingDebugInfo: ${renderElement}, ${propertyName} = ${propertyValue}`);
- }
- }
-
- @profile
- setElementDebugInfo(renderElement: any, _info: any /*RenderDebugInfo*/): void {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.setElementDebugInfo: ${renderElement}`);
- }
- }
-
- @profile
- invokeElementMethod(_renderElement: NgView, methodName: string, args: Array) {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.invokeElementMethod ${methodName} ${args}`);
- }
- }
-
- @profile
- setValue(_renderNode: any, _value: string) {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.setValue renderNode: ${_renderNode}, value: ${_value}`);
- }
- }
-
- @profile
- listen(renderElement: any, eventName: string, callback: (event: any) => boolean):
- () => void {
- if (isLogEnabled()) {
- traceLog(`NativeScriptRenderer.listen: ${eventName}`);
- }
- // Explicitly wrap in zone
- let zonedCallback = (...args) => {
- this.zone.run(() => {
- callback.apply(undefined, args);
- });
- };
-
- renderElement.on(eventName, zonedCallback);
- if (eventName === View.loadedEvent && renderElement.isLoaded) {
- const notifyData = { eventName: View.loadedEvent, object: renderElement };
- zonedCallback(notifyData);
- }
- return () => renderElement.off(eventName, zonedCallback);
- }
-}
-
-class EmulatedRenderer extends NativeScriptRenderer {
- private contentAttr: string;
- private hostAttr: string;
-
- constructor(
- component: RendererType2,
- rootView: NgView,
- zone: NgZone,
- viewUtil: ViewUtil,
- ) {
- super(rootView, zone, viewUtil);
-
- const componentId = component.id.replace(ATTR_SANITIZER, "_");
- this.contentAttr = replaceNgAttribute(CONTENT_ATTR, componentId);
- this.hostAttr = replaceNgAttribute(HOST_ATTR, componentId);
- this.addStyles(component.styles, componentId);
- }
-
- applyToHost(view: NgView) {
- super.setAttribute(view, this.hostAttr, "");
- }
-
- appendChild(parent: any, newChild: NgView): void {
- super.appendChild(parent, newChild);
- }
-
- createElement(parent: any, name: string): NgView {
- const view = super.createElement(parent, name);
-
- // Set an attribute to the view to scope component-specific css.
- // The property name is pre-generated by Angular.
- super.setAttribute(view, this.contentAttr, "");
-
- return view;
- }
-
- @profile
- private addStyles(styles: (string | any[])[], componentId: string) {
- styles.map(s => s.toString())
- .map(s => replaceNgAttribute(s, componentId))
- .forEach(addScopedStyleToCss);
- }
-}
-
-// tslint:disable-next-line
-const addStyleToCss = profile('"renderer".addStyleToCss', function addStyleToCss(style: string): void {
- addCss(style);
-});
-
-// tslint:disable-next-line
-const addScopedStyleToCss = profile('"renderer".addScopedStyleToCss', function addScopedStyleToCss(style: string): void {
- addCss(style, true);
-});
-
-function replaceNgAttribute(input: string, componentId: string): string {
- return input.replace(COMPONENT_REGEX, componentId);
+ data: { [key: string]: any } = Object.create(null);
+
+ constructor(private rootView: NgView, private zone: NgZone, private viewUtil: ViewUtil) {
+ super();
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog('NativeScriptRenderer created');
+ }
+ }
+
+ @profile
+ appendChild(parent: NgView, newChild: NgView): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.appendChild child: ${newChild} parent: ${parent}`);
+ }
+ this.viewUtil.insertChild(parent, newChild);
+ }
+
+ @profile
+ insertBefore(parent: NgView, newChild: NgView, { previous, next }: ElementReference): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.insertBefore child: ${newChild} ` + `parent: ${parent} previous: ${previous} next: ${next}`);
+ }
+ this.viewUtil.insertChild(parent, newChild, previous, next);
+ }
+
+ @profile
+ removeChild(parent: any, oldChild: NgView): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.removeChild child: ${oldChild} parent: ${parent}`);
+ }
+ this.viewUtil.removeChild(parent, oldChild);
+ }
+
+ @profile
+ selectRootElement(selector: string): NgView {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.selectRootElement: ${selector}`);
+ }
+ if (selector && selector[0] === '#') {
+ const result = getViewById(this.rootView, selector.slice(1));
+ return (result || this.rootView) as NgView;
+ }
+ return this.rootView;
+ }
+
+ @profile
+ parentNode(node: NgView): any {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.parentNode for node: ${node} is ${node.parentNode}`);
+ }
+ return node.parentNode;
+ }
+
+ @profile
+ nextSibling(node: NgView): ElementReference {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.nextSibling of ${node} is ${node.nextSibling}`);
+ }
+
+ return {
+ previous: node,
+ next: node.nextSibling,
+ };
+ }
+
+ @profile
+ createComment(_value: any): InvisibleNode {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.createComment ${_value}`);
+ }
+ return this.viewUtil.createComment();
+ }
+
+ @profile
+ createElement(name: any, _namespace: string): NgView {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.createElement: ${name}`);
+ }
+ return this.viewUtil.createView(name);
+ }
+
+ @profile
+ createText(_value: string): InvisibleNode {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.createText ${_value}`);
+ }
+ return this.viewUtil.createText();
+ }
+
+ @profile
+ createViewRoot(hostElement: NgView): NgView {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.createViewRoot ${hostElement.nodeName}`);
+ }
+ return hostElement;
+ }
+
+ @profile
+ projectNodes(parentElement: NgView, nodes: NgView[]): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog('NativeScriptRenderer.projectNodes');
+ }
+ nodes.forEach((node) => this.viewUtil.insertChild(parentElement, node));
+ }
+
+ @profile
+ destroy() {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog('NativeScriptRenderer.destroy');
+ }
+ // Seems to be called on component dispose only (router outlet)
+ // TODO: handle this when we resolve routing and navigation.
+ }
+
+ @profile
+ setAttribute(view: NgView, name: string, value: string, namespace?: string) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.setAttribute ${view} : ${name} = ${value}, namespace: ${namespace}`);
+ }
+ return this.viewUtil.setProperty(view, name, value, namespace);
+ }
+
+ @profile
+ removeAttribute(_el: NgView, _name: string): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.removeAttribute ${_el}: ${_name}`);
+ }
+ }
+
+ @profile
+ setProperty(view: any, name: string, value: any) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.setProperty ${view} : ${name} = ${value}`);
+ }
+ return this.viewUtil.setProperty(view, name, value);
+ }
+
+ @profile
+ addClass(view: NgView, name: string): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.addClass ${name}`);
+ }
+ this.viewUtil.addClass(view, name);
+ }
+
+ @profile
+ removeClass(view: NgView, name: string): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.removeClass ${name}`);
+ }
+ this.viewUtil.removeClass(view, name);
+ }
+
+ @profile
+ setStyle(view: NgView, styleName: string, value: any, _flags?: RendererStyleFlags2): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.setStyle: ${styleName} = ${value}`);
+ }
+ this.viewUtil.setStyle(view, styleName, value);
+ }
+
+ @profile
+ removeStyle(view: NgView, styleName: string, _flags?: RendererStyleFlags2): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog('NativeScriptRenderer.removeStyle: ${styleName}');
+ }
+ this.viewUtil.removeStyle(view, styleName);
+ }
+
+ // Used only in debug mode to serialize property changes to comment nodes,
+ // such as placeholders.
+ @profile
+ setBindingDebugInfo(renderElement: NgView, propertyName: string, propertyValue: string): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.setBindingDebugInfo: ${renderElement}, ${propertyName} = ${propertyValue}`);
+ }
+ }
+
+ @profile
+ setElementDebugInfo(renderElement: any, _info: any /*RenderDebugInfo*/): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.setElementDebugInfo: ${renderElement}`);
+ }
+ }
+
+ @profile
+ invokeElementMethod(_renderElement: NgView, methodName: string, args: Array) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.invokeElementMethod ${methodName} ${args}`);
+ }
+ }
+
+ @profile
+ setValue(_renderNode: any, _value: string) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.setValue renderNode: ${_renderNode}, value: ${_value}`);
+ }
+ }
+
+ @profile
+ listen(renderElement: any, eventName: string, callback: (event: any) => boolean): () => void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.rendererLog(`NativeScriptRenderer.listen: ${eventName}`);
+ }
+ // Explicitly wrap in zone
+ let zonedCallback = (...args) => {
+ this.zone.run(() => {
+ callback.apply(undefined, args);
+ });
+ };
+
+ renderElement.on(eventName, zonedCallback);
+ if (eventName === View.loadedEvent && renderElement.isLoaded) {
+ const notifyData = { eventName: View.loadedEvent, object: renderElement };
+ zonedCallback(notifyData);
+ }
+ return () => renderElement.off(eventName, zonedCallback);
+ }
}
diff --git a/nativescript-angular/resource-loader.ts b/nativescript-angular/resource-loader.ts
index be2177d5c..dec0228ce 100644
--- a/nativescript-angular/resource-loader.ts
+++ b/nativescript-angular/resource-loader.ts
@@ -1,54 +1,53 @@
-import { Injectable } from "@angular/core";
-import { ResourceLoader } from "@angular/compiler";
-import { path } from "@nativescript/core/file-system";
+import { Injectable } from '@angular/core';
+import { ResourceLoader } from '@angular/compiler';
+import { path } from '@nativescript/core';
-import { NSFileSystem } from "./file-system/ns-file-system";
+import { NSFileSystem } from './file-system/ns-file-system';
const sourceExtensionsMap = {
- ".scss": ".css",
- ".sass": ".css",
- ".less": ".css"
+ '.scss': '.css',
+ '.sass': '.css',
+ '.less': '.css',
};
@Injectable()
export class FileSystemResourceLoader extends ResourceLoader {
- constructor(private fs: NSFileSystem) {
- super();
- }
-
- get(url: string): string {
- const resolvedPath = this.resolve(url);
-
- const templateFile = this.fs.fileFromPath(resolvedPath);
-
- return templateFile.readTextSync();
- }
-
- resolve(url: string): string {
- const normalizedSourceUrl = this.resolveRelativeUrls(url);
- const normalizedCompiledFileUrl = normalizedSourceUrl.replace(/\.\w+$/, ext => sourceExtensionsMap[ext] || ext);
- if (normalizedCompiledFileUrl !== normalizedSourceUrl && this.fs.fileExists(normalizedCompiledFileUrl)) {
- return normalizedCompiledFileUrl;
- }
- if (this.fs.fileExists(normalizedSourceUrl)) {
- return normalizedSourceUrl;
- }
-
- if (normalizedCompiledFileUrl === normalizedSourceUrl) {
- throw new Error(`Could not resolve ${url}. Looked for: ${normalizedSourceUrl}.`);
- } else {
- throw new Error(`Could not resolve ${url}.` +
- `Looked for: ${normalizedCompiledFileUrl}, ${normalizedSourceUrl}.`);
- }
- }
-
- private resolveRelativeUrls(url: string): string {
- // Angular assembles absolute URLs and prefixes them with //
- if (url.indexOf("/") !== 0) {
- // Resolve relative URLs based on the app root.
- return path.join(this.fs.currentApp().path, url);
- } else {
- return url;
- }
- }
+ constructor(private fs: NSFileSystem) {
+ super();
+ }
+
+ get(url: string): string {
+ const resolvedPath = this.resolve(url);
+
+ const templateFile = this.fs.fileFromPath(resolvedPath);
+
+ return templateFile.readTextSync();
+ }
+
+ resolve(url: string): string {
+ const normalizedSourceUrl = this.resolveRelativeUrls(url);
+ const normalizedCompiledFileUrl = normalizedSourceUrl.replace(/\.\w+$/, (ext) => sourceExtensionsMap[ext] || ext);
+ if (normalizedCompiledFileUrl !== normalizedSourceUrl && this.fs.fileExists(normalizedCompiledFileUrl)) {
+ return normalizedCompiledFileUrl;
+ }
+ if (this.fs.fileExists(normalizedSourceUrl)) {
+ return normalizedSourceUrl;
+ }
+
+ if (normalizedCompiledFileUrl === normalizedSourceUrl) {
+ throw new Error(`Could not resolve ${url}. Looked for: ${normalizedSourceUrl}.`);
+ } else {
+ throw new Error(`Could not resolve ${url}.` + `Looked for: ${normalizedCompiledFileUrl}, ${normalizedSourceUrl}.`);
+ }
+ }
+
+ private resolveRelativeUrls(url: string): string {
+ // Angular assembles absolute URLs and prefixes them with //
+ if (url.indexOf('/') !== 0) {
+ // Resolve relative URLs based on the app root.
+ return path.join(this.fs.currentApp().path, url);
+ } else {
+ return url;
+ }
+ }
}
diff --git a/nativescript-angular/router/index.ts b/nativescript-angular/router/index.ts
index d13492a9e..26f68bf55 100644
--- a/nativescript-angular/router/index.ts
+++ b/nativescript-angular/router/index.ts
@@ -1 +1 @@
-export * from "./router.module";
+export * from './router.module';
diff --git a/nativescript-angular/router/ns-empty-outlet.component.ts b/nativescript-angular/router/ns-empty-outlet.component.ts
index 06ddc302f..b94a643e2 100644
--- a/nativescript-angular/router/ns-empty-outlet.component.ts
+++ b/nativescript-angular/router/ns-empty-outlet.component.ts
@@ -1,22 +1,22 @@
-import { Component, ViewChild } from "@angular/core";
-import { Page } from "@nativescript/core/ui/page";
-import { PageRouterOutlet } from "./page-router-outlet";
+import { Component, ViewChild } from '@angular/core';
+import { Page } from '@nativescript/core';
+import { PageRouterOutlet } from './page-router-outlet';
@Component({
- // tslint:disable-next-line:component-selector
- selector: "ns-empty-outlet",
- template: ""
+ // tslint:disable-next-line:component-selector
+ selector: 'ns-empty-outlet',
+ template: "",
})
export class NSEmptyOutletComponent {
- @ViewChild(PageRouterOutlet, { read: PageRouterOutlet, static: false }) pageRouterOutlet: PageRouterOutlet;
- constructor(private page: Page) {
- if (this.page) {
- this.page.actionBarHidden = true;
+ @ViewChild(PageRouterOutlet, { read: PageRouterOutlet, static: false }) pageRouterOutlet: PageRouterOutlet;
+ constructor(private page: Page) {
+ if (this.page) {
+ this.page.actionBarHidden = true;
- this.page.on("loaded", () => {
- if (this.pageRouterOutlet && this.page.frame) {
- this.pageRouterOutlet.setActionBarVisibility(this.page.frame.actionBarVisibility);
- }
- });
- }
- }
+ this.page.on('loaded', () => {
+ if (this.pageRouterOutlet && this.page.frame) {
+ this.pageRouterOutlet.setActionBarVisibility(this.page.frame.actionBarVisibility);
+ }
+ });
+ }
+ }
}
diff --git a/nativescript-angular/router/ns-location-strategy.ts b/nativescript-angular/router/ns-location-strategy.ts
index cde7002d9..99c3b8a3c 100644
--- a/nativescript-angular/router/ns-location-strategy.ts
+++ b/nativescript-angular/router/ns-location-strategy.ts
@@ -1,761 +1,671 @@
-import { Injectable } from "@angular/core";
-import { LocationStrategy } from "@angular/common";
-import { DefaultUrlSerializer, UrlSegmentGroup, UrlTree, ActivatedRouteSnapshot, Params } from "@angular/router";
-import { routerLog, routerError, isLogEnabled } from "../trace";
-import { NavigationTransition, Frame } from "@nativescript/core/ui/frame";
-import { isPresent } from "../lang-facade";
-import { FrameService } from "../platform-providers";
-
-export class Outlet {
- showingModal: boolean;
- modalNavigationDepth: number;
- parent: Outlet;
- isPageNavigationBack: boolean;
-
- // More than one key available when using NSEmptyOutletComponent component
- // in module that lazy loads children (loadChildren) and has outlet name.
- outletKeys: Array;
-
- // More than one frame available when using NSEmptyOutletComponent component
- // in module that lazy loads children (loadChildren) and has outlet name.
- frames: Array = [];
- // The url path to the Outlet.
- // E.G: the path to Outlet3 that is nested Outlet1(home)->Outlet2(nested1)->Outlet3(nested2)
- // will be 'home/nested1'
- path: string;
- pathByOutlets: string;
- states: Array = [];
- isNSEmptyOutlet: boolean;
-
- // Used in reuse-strategy by its children to determine if they should be detached too.
- shouldDetach: boolean = true;
- constructor(outletKey: string, path: string, pathByOutlets: string, modalNavigationDepth?: number) {
- this.outletKeys = [outletKey];
- this.isPageNavigationBack = false;
- this.showingModal = false;
- this.modalNavigationDepth = modalNavigationDepth || 0;
- this.pathByOutlets = pathByOutlets;
- this.path = path;
- }
-
- containsFrame(frame: Frame): boolean {
- return this.frames.indexOf(frame) > -1;
- }
-
- peekState(): LocationState {
- if (this.states.length > 0) {
- return this.states[this.states.length - 1];
- }
- return null;
- }
-
- containsTopState(stateUrl: string): boolean {
- const lastState = this.peekState();
- return lastState && lastState.segmentGroup.toString() === stateUrl;
- }
-
- // Search for frame that can go back.
- // Nested 'primary' outlets could result in Outlet with multiple navigatable frames.
- getFrameToBack(): Frame {
- let frame = this.frames[this.frames.length - 1];
-
- if (!this.isNSEmptyOutlet) {
- for (let index = this.frames.length - 1; index >= 0; index--) {
- const currentFrame = this.frames[index];
- if (currentFrame.canGoBack()) {
- frame = currentFrame;
- break;
- }
- }
- }
-
- return frame;
- }
-}
-
-export interface NavigationOptions {
- clearHistory?: boolean;
- animated?: boolean;
- transition?: NavigationTransition;
-}
-
-const defaultNavOptions: NavigationOptions = {
- clearHistory: false,
- animated: true
-};
-
-export interface LocationState {
- queryParams: Params;
- segmentGroup: UrlSegmentGroup;
- isRootSegmentGroup: boolean;
- isPageNavigation: boolean;
- frame?: Frame;
-}
-
-@Injectable()
+import { Injectable } from '@angular/core';
+import { LocationStrategy } from '@angular/common';
+import { DefaultUrlSerializer, UrlSegmentGroup, UrlTree, ActivatedRouteSnapshot, Params } from '@angular/router';
+import { Frame } from '@nativescript/core';
+import { NativeScriptDebug } from '../trace';
+import { isPresent } from '../lang-facade';
+import { FrameService } from '../frame.service';
+import { Outlet, NavigationOptions, LocationState, defaultNavOptions } from './ns-location-utils';
+
+@Injectable({
+ providedIn: 'root',
+})
export class NSLocationStrategy extends LocationStrategy {
- private outlets: Array = [];
- private currentOutlet: Outlet;
-
- private popStateCallbacks = new Array<(_: any) => any>();
- private _currentNavigationOptions: NavigationOptions;
- private currentUrlTree: UrlTree;
-
- public _modalNavigationDepth = 0;
-
- constructor(private frameService: FrameService) {
- super();
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy.constructor()");
- }
- }
-
- path(): string {
- if (!this.currentUrlTree) {
- return "/";
- }
-
- const state = this.currentOutlet && this.currentOutlet.peekState();
- if (!state) {
- return "/";
- }
-
- let tree = this.currentUrlTree;
- let changedOutlet = this.getSegmentGroupByOutlet(this.currentOutlet);
-
- // Handle case where the user declares a component at path "/".
- // The url serializer doesn't parse this url as having a primary outlet.
- if (state.isRootSegmentGroup) {
- tree.root = state.segmentGroup;
- } else if (changedOutlet) {
- this.updateSegmentGroup(tree.root, changedOutlet, state.segmentGroup);
- }
-
- const urlSerializer = new DefaultUrlSerializer();
- tree.queryParams = state.queryParams;
- const url = urlSerializer.serialize(tree);
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy.path(): " + url);
- }
- return url;
- }
-
- prepareExternalUrl(internal: string): string {
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy.prepareExternalUrl() internal: " + internal);
- }
- return internal;
- }
-
- pushState(state: any, title: string, url: string, queryParams: string): void {
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy.pushState state: " +
- `${state}, title: ${title}, url: ${url}, queryParams: ${queryParams}`);
- }
- this.pushStateInternal(state, title, url, queryParams);
- }
-
- pushStateInternal(state: any, title: string, url: string, queryParams: string): void {
- const urlSerializer = new DefaultUrlSerializer();
- this.currentUrlTree = urlSerializer.parse(url);
- const urlTreeRoot = this.currentUrlTree.root;
-
- // Handle case where the user declares a component at path "/".
- // The url serializer doesn't parse this url as having a primary outlet.
- if (!Object.keys(urlTreeRoot.children).length) {
- const segmentGroup = this.currentUrlTree && this.currentUrlTree.root;
- const outletKey = this.getOutletKey(this.getSegmentGroupFullPath(segmentGroup), "primary");
- const outlet = this.findOutlet(outletKey);
-
- if (outlet && this.updateStates(outlet, segmentGroup, this.currentUrlTree.queryParams)) {
- this.currentOutlet = outlet; // If states updated
- } else if (!outlet) {
- // tslint:disable-next-line:max-line-length
- const rootOutlet = this.createOutlet("primary", null, segmentGroup, null, null, this.currentUrlTree.queryParams);
- this.currentOutlet = rootOutlet;
- }
-
- this.currentOutlet.peekState().isRootSegmentGroup = true;
- return;
- }
-
- const queue = [];
- let currentTree = urlTreeRoot;
-
- while (currentTree) {
- Object.keys(currentTree.children).forEach(outletName => {
- const currentSegmentGroup = currentTree.children[outletName];
- currentSegmentGroup.outlet = outletName;
- currentSegmentGroup.root = urlTreeRoot;
-
- const outletPath = this.getSegmentGroupFullPath(currentTree);
- let outletKey = this.getOutletKey(outletPath, outletName);
- let outlet = this.findOutlet(outletKey);
-
- const parentOutletName = currentTree.outlet || "";
- const parentOutletPath = this.getSegmentGroupFullPath(currentTree.parent);
- const parentOutletKey = this.getOutletKey(parentOutletPath, parentOutletName);
- const parentOutlet = this.findOutlet(parentOutletKey);
-
- const containsLastState = outlet && outlet.containsTopState(currentSegmentGroup.toString());
- if (!outlet) {
- // tslint:disable-next-line:max-line-length
- outlet = this.createOutlet(outletKey, outletPath, currentSegmentGroup, parentOutlet, this._modalNavigationDepth, this.currentUrlTree.queryParams);
- this.currentOutlet = outlet;
- } else if (this._modalNavigationDepth > 0 && outlet.showingModal && !containsLastState) {
- // Navigation inside modal view.
- this.upsertModalOutlet(outlet, currentSegmentGroup, this.currentUrlTree.queryParams);
- } else {
- outlet.parent = parentOutlet;
-
- if (this.updateStates(outlet, currentSegmentGroup, this.currentUrlTree.queryParams)) {
- this.currentOutlet = outlet; // If states updated
- }
- }
-
- queue.push(currentSegmentGroup);
- });
-
- currentTree = queue.shift();
- }
- }
-
- replaceState(state: any, title: string, url: string, queryParams: string): void {
- const states = this.currentOutlet && this.currentOutlet.states;
-
- if (states && states.length > 0) {
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy.replaceState changing existing state: " +
- `${state}, title: ${title}, url: ${url}, queryParams: ${queryParams}`);
- }
- } else {
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy.replaceState pushing new state: " +
- `${state}, title: ${title}, url: ${url}, queryParams: ${queryParams}`);
- }
- this.pushStateInternal(state, title, url, queryParams);
- }
- }
-
- forward(): void {
- throw new Error("NSLocationStrategy.forward() - not implemented");
- }
-
- back(outlet?: Outlet, frame?: Frame): void {
- this.currentOutlet = outlet || this.currentOutlet;
-
- if (this.currentOutlet.isPageNavigationBack) {
- const states = this.currentOutlet.states;
- // We are navigating to the previous page
- // clear the stack until we get to a page navigation state
- let state = states.pop();
- let count = 1;
-
- if (frame) {
- while (state.frame && state.frame !== frame) {
- state = states.pop();
- count++;
- }
- }
-
- while (!state.isPageNavigation) {
- state = states.pop();
- count++;
- }
-
- if (isLogEnabled()) {
- routerLog(`NSLocationStrategy.back() while navigating back. States popped: ${count}`);
- }
- this.callPopState(state, true);
- } else {
- let state = this.currentOutlet.peekState();
- if (state && state.isPageNavigation) {
- // This was a page navigation - so navigate through frame.
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy.back() while not navigating back but top" +
- " state is page - will call frame.goBack()");
- }
-
- if (!outlet) {
- const topmostFrame = this.frameService.getFrame();
- this.currentOutlet = this.getOutletByFrame(topmostFrame) || this.currentOutlet;
- }
-
- const frameToBack: Frame = this.currentOutlet.getFrameToBack();
- if (frameToBack) {
- frameToBack.goBack();
- }
- } else {
- // Nested navigation - just pop the state
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy.back() while not navigating back but top" +
- " state is not page - just pop");
- }
-
- this.callPopState(this.currentOutlet.states.pop(), true);
- }
- }
- }
-
- canGoBack(outlet?: Outlet) {
- outlet = outlet || this.currentOutlet;
- return outlet.states.length > 1;
- }
-
- onPopState(fn: (_: any) => any): void {
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy.onPopState");
- }
- this.popStateCallbacks.push(fn);
- }
-
- getBaseHref(): string {
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy.getBaseHref()");
- }
- return "";
- }
-
- private callPopState(state: LocationState, pop: boolean = true, outlet?: Outlet) {
- outlet = outlet || this.currentOutlet;
- const urlSerializer = new DefaultUrlSerializer();
- let changedOutlet = this.getSegmentGroupByOutlet(outlet);
-
- if (state && changedOutlet) {
- this.updateSegmentGroup(this.currentUrlTree.root, changedOutlet, state.segmentGroup);
- } else if (changedOutlet) {
- // when closing modal view there are scenarios (e.g. root viewContainerRef) when we need
- // to clean up the named page router outlet to make sure we will open the modal properly again if needed.
- this.updateSegmentGroup(this.currentUrlTree.root, changedOutlet, null);
- }
-
- const url = urlSerializer.serialize(this.currentUrlTree);
- const change = { url: url, pop: pop };
- for (let fn of this.popStateCallbacks) {
- fn(change);
- }
- }
-
- public toString() {
- let result = [];
-
- this.outlets.forEach(outlet => {
- const outletStates = outlet.states;
- const outletLog = outletStates
- // tslint:disable-next-line:max-line-length
- .map((v, i) => `${outlet.outletKeys}.${i}.[${v.isPageNavigation ? "PAGE" : "INTERNAL"}].[${outlet.modalNavigationDepth ? "MODAL" : "BASE"}] "${v.segmentGroup.toString()}"`)
- .reverse();
-
- result = result.concat(outletLog);
- });
-
- return result.join("\n");
- }
-
- // Methods for syncing with page navigation in PageRouterOutlet
- public _beginBackPageNavigation(frame: Frame) {
- const outlet: Outlet = this.getOutletByFrame(frame);
-
- if (!outlet || outlet.isPageNavigationBack) {
- if (isLogEnabled()) {
- routerError("Attempted to call startGoBack while going back.");
- }
- return;
- }
-
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy.startGoBack()");
- }
- outlet.isPageNavigationBack = true;
-
- this.currentOutlet = outlet;
- }
-
- public _finishBackPageNavigation(frame: Frame) {
- const outlet: Outlet = this.getOutletByFrame(frame);
-
- if (!outlet || !outlet.isPageNavigationBack) {
- if (isLogEnabled()) {
- routerError("Attempted to call endGoBack while not going back.");
- }
- return;
- }
-
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy.finishBackPageNavigation()");
- }
- outlet.isPageNavigationBack = false;
- }
-
- public _beginModalNavigation(frame: Frame): void {
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy._beginModalNavigation()");
- }
-
- this.currentOutlet = this.getOutletByFrame(frame) || this.currentOutlet;
-
- // It is possible to have frame, but not corresponding Outlet, if
- // showing modal dialog on app.component.ts ngOnInit() e.g. In that case
- // the modal is treated as none modal navigation.
- if (this.currentOutlet) {
- this.currentOutlet.showingModal = true;
- this._modalNavigationDepth++;
- }
- }
-
- public _closeModalNavigation() {
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy.closeModalNavigation()");
- }
-
- const isShowingModal = this._modalNavigationDepth > 0;
-
- if (isShowingModal) {
- this._modalNavigationDepth--;
- }
-
- // currentOutlet should be the one that corresponds to the topmost frame
- const topmostOutlet = this.getOutletByFrame(this.frameService.getFrame());
- const outlet = this.findOutletByModal(this._modalNavigationDepth, isShowingModal) || topmostOutlet;
-
- if (outlet) {
- this.currentOutlet = outlet;
- this.currentOutlet.showingModal = false;
- this.callPopState(this.currentOutlet.peekState(), false);
- }
- }
-
- public _beginPageNavigation(frame: Frame): NavigationOptions {
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy._beginPageNavigation()");
- }
-
- this.currentOutlet = this.getOutletByFrame(frame);
- const lastState = this.currentOutlet.peekState();
-
- if (lastState) {
- lastState.isPageNavigation = true;
- }
-
- const navOptions = this._currentNavigationOptions || defaultNavOptions;
- if (navOptions.clearHistory) {
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy._beginPageNavigation clearing states history");
- }
- this.currentOutlet.states = [lastState];
- }
-
- this._currentNavigationOptions = undefined;
- return navOptions;
- }
-
- public _setNavigationOptions(options: NavigationOptions) {
- this._currentNavigationOptions = {
- clearHistory: isPresent(options.clearHistory) ? options.clearHistory : false,
- animated: isPresent(options.animated) ? options.animated : true,
- transition: options.transition
- };
-
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy._setNavigationOptions(" +
- `${JSON.stringify(this._currentNavigationOptions)})`);
- }
- }
-
- public _getOutlets(): Array {
- return this.outlets;
- }
-
- updateOutletFrame(outlet: Outlet, frame: Frame, isEmptyOutletFrame: boolean) {
- const lastState = outlet.peekState();
-
- if (lastState && !lastState.frame && !isEmptyOutletFrame) {
- lastState.frame = frame;
- }
-
- if (!outlet.containsFrame(frame)) {
- outlet.frames.push(frame);
- }
- this.currentOutlet = outlet;
- }
-
- clearOutlet(frame: Frame) {
- this.outlets = this.outlets.filter(currentOutlet => {
- let isEqualToCurrent;
-
- if (this.currentOutlet) {
- isEqualToCurrent = currentOutlet.pathByOutlets === this.currentOutlet.pathByOutlets;
- }
-
- // Remove outlet from the url tree.
- if (currentOutlet.containsFrame(frame) && !isEqualToCurrent) {
- this.callPopState(null, true, currentOutlet);
- }
-
- // Skip frames filtering since currentOutlet is when no frames available.
- if (currentOutlet.frames.length && !currentOutlet.isNSEmptyOutlet) {
- currentOutlet.frames = currentOutlet.frames.filter(currentFrame => currentFrame !== frame);
- return currentOutlet.frames.length;
- }
-
- return !currentOutlet.containsFrame(frame);
- });
- }
-
- getSegmentGroupFullPath(segmentGroup: UrlSegmentGroup): string {
- let fullPath = "";
-
- while (segmentGroup) {
- const url = segmentGroup.toString();
-
- if (fullPath) {
- fullPath = (url ? url + "/" : "") + fullPath;
- } else {
- fullPath = url;
- }
-
- segmentGroup = segmentGroup.parent;
- }
-
- return fullPath;
- }
-
- getRouteFullPath(currentRoute: any): string {
- const outletName = currentRoute.outlet;
- let fullPath;
-
- currentRoute = currentRoute.parent;
- while (currentRoute) {
- const urls = (currentRoute.url.value || currentRoute.url);
- let url = urls;
-
- if (Array.isArray(urls)) {
- url = url.join("/");
- }
-
- fullPath = fullPath ? (url ? url + "/" : url) + fullPath : url;
- currentRoute = currentRoute.parent;
- }
-
- return fullPath ? fullPath + "-" + outletName : outletName;
- }
-
-
- getPathByOutlets(urlSegmentGroup: any): string {
- if (!urlSegmentGroup) {
- return "";
- }
-
- let pathToOutlet;
- let lastPath = urlSegmentGroup.outlet || "primary";
- let parent = urlSegmentGroup.parent;
-
- while (parent && urlSegmentGroup.root !== parent) {
- if (parent && parent.outlet !== lastPath) {
- if (lastPath === "primary") {
- lastPath = parent.outlet;
- } else {
- lastPath = parent.outlet;
- pathToOutlet = lastPath + "-" + (pathToOutlet || urlSegmentGroup.outlet);
- }
- }
-
- parent = parent.parent;
- }
-
- return pathToOutlet || lastPath;
- }
-
- findOutlet(outletKey: string, activatedRouteSnapshot?: ActivatedRouteSnapshot): Outlet {
- let outlet: Outlet = this.outlets.find((currentOutlet) => {
- let equalModalDepth = currentOutlet.modalNavigationDepth === this._modalNavigationDepth;
- return equalModalDepth && currentOutlet.outletKeys.indexOf(outletKey) > -1;
- });
-
- // No Outlet with the given outletKey could happen when using nested unnamed p-r-o
- // primary -> primary -> prymary
- if (!outlet && activatedRouteSnapshot) {
- const pathByOutlets = this.getPathByOutlets(activatedRouteSnapshot);
- outlet = this.outlets.find((currentOutlet) => {
- let equalModalDepth = currentOutlet.modalNavigationDepth === this._modalNavigationDepth;
- return equalModalDepth && currentOutlet.pathByOutlets === pathByOutlets;
- });
- }
-
- return outlet;
- }
-
- private findOutletByModal(modalNavigation: number, isShowingModal?: boolean): Outlet {
- return this.outlets.find((outlet) => {
- let equalModalDepth = outlet.modalNavigationDepth === modalNavigation;
- return isShowingModal ? equalModalDepth && outlet.showingModal : equalModalDepth;
- });
- }
-
- private getOutletByFrame(frame: Frame): Outlet {
- let outlet;
-
- for (let index = 0; index < this.outlets.length; index++) {
- const currentOutlet = this.outlets[index];
-
- if (currentOutlet.containsFrame(frame)) {
- outlet = currentOutlet;
- break;
- }
- }
-
- return outlet;
- }
-
- private updateStates(outlet: Outlet, currentSegmentGroup: UrlSegmentGroup, queryParams: Params): boolean {
- const isNewPage = outlet.states.length === 0;
- const lastState = outlet.states[outlet.states.length - 1];
- const equalStateUrls = outlet.containsTopState(currentSegmentGroup.toString());
-
- const locationState: LocationState = {
- segmentGroup: currentSegmentGroup,
- isRootSegmentGroup: false,
- isPageNavigation: isNewPage,
- queryParams: {...queryParams}
- };
-
- if (!lastState || !equalStateUrls) {
- outlet.states.push(locationState);
-
- // Update last state segmentGroup of parent Outlet.
- if (this._modalNavigationDepth === 0 && !outlet.showingModal) {
- this.updateParentsStates(outlet, currentSegmentGroup.parent);
- }
-
- return true;
- }
-
- return false;
- }
-
- private updateParentsStates(outlet: Outlet, newSegmentGroup: UrlSegmentGroup) {
- let parentOutlet = outlet.parent;
-
- // Update parents lastState segmentGroups
- while (parentOutlet && newSegmentGroup) {
- const state = parentOutlet.peekState();
-
- if (state) {
- state.segmentGroup = newSegmentGroup;
- newSegmentGroup = newSegmentGroup.parent;
- parentOutlet = parentOutlet.parent;
- }
- }
- }
-
- // tslint:disable-next-line:max-line-length
- private createOutlet(outletKey: string, path: string, segmentGroup: any, parent: Outlet, modalNavigation?: number, queryParams: Params = {}): Outlet {
- const pathByOutlets = this.getPathByOutlets(segmentGroup);
- const newOutlet = new Outlet(outletKey, path, pathByOutlets, modalNavigation);
-
- const locationState: LocationState = {
- segmentGroup: segmentGroup,
- isRootSegmentGroup: false,
- isPageNavigation: true, // It is a new OutletNode.
- queryParams: {...queryParams}
- };
-
- newOutlet.states = [locationState];
- newOutlet.parent = parent;
- this.outlets.push(newOutlet);
-
- // Update last state segmentGroup of parent Outlet.
- if (this._modalNavigationDepth === 0 && !newOutlet.showingModal) {
- this.updateParentsStates(newOutlet, segmentGroup.parent);
- }
-
- return newOutlet;
- }
-
- private getSegmentGroupByOutlet(outlet: Outlet): UrlSegmentGroup {
- const pathList = outlet.pathByOutlets.split("-");
- let segmentGroup = this.currentUrlTree.root;
- let pathToOutlet;
-
- for (let index = 0; index < pathList.length; index++) {
- const currentPath = pathList[index];
- const childrenCount = Object.keys(segmentGroup.children).length;
-
- if (childrenCount && segmentGroup.children[currentPath]) {
- const url = segmentGroup.toString();
- pathToOutlet = pathToOutlet ? pathToOutlet + "/" + url : url;
- segmentGroup = segmentGroup.children[currentPath];
- } else {
- // If no child outlet found with the given name - forget about all previously found outlets.
- // example: seaching for 'primary-second-primary' shouldn't return 'primary-second'
- // if no 'primary' child available on 'second'.
- segmentGroup = null;
- break;
- }
- }
-
- // Paths should also match since there could be another Outlet
- // with the same pathByOutlets but different url path.
- if (segmentGroup && outlet.path && pathToOutlet && outlet.path !== pathToOutlet) {
- segmentGroup = null;
- }
-
- return segmentGroup;
- }
-
- // Traversal and replacement of segmentGroup.
- private updateSegmentGroup(rootNode: any, oldSegmentGroup: UrlSegmentGroup, newSegmentGroup: UrlSegmentGroup) {
- const queue = [];
- let currentTree = rootNode;
-
- while (currentTree) {
- Object.keys(currentTree.children).forEach(outletName => {
- if (currentTree.children[outletName] === oldSegmentGroup) {
- if (newSegmentGroup) {
- currentTree.children[outletName] = newSegmentGroup;
- } else {
- delete currentTree.children[outletName];
- }
- }
- queue.push(currentTree.children[outletName]);
- });
-
- currentTree = queue.shift();
- }
- }
-
- private upsertModalOutlet(parentOutlet: Outlet, segmentedGroup: UrlSegmentGroup, queryParams: Params) {
- let currentModalOutlet = this.findOutletByModal(this._modalNavigationDepth);
-
- // We want to treat every p-r-o as a standalone Outlet.
- if (!currentModalOutlet) {
- if (this._modalNavigationDepth > 1) {
- // The parent of the current Outlet should be the previous opened modal (if any).
- parentOutlet = this.findOutletByModal(this._modalNavigationDepth - 1);
- }
-
- // No currentModalOutlet available when opening 'primary' p-r-o.
- const outletName = "primary";
- const outletPath = parentOutlet.peekState().segmentGroup.toString();
- const outletKey = this.getOutletKey(outletPath, outletName);
- // tslint:disable-next-line:max-line-length
- currentModalOutlet = this.createOutlet(outletKey, outletPath, segmentedGroup, parentOutlet, this._modalNavigationDepth, queryParams);
- this.currentOutlet = currentModalOutlet;
- } else if (this.updateStates(currentModalOutlet, segmentedGroup, queryParams)) {
- this.currentOutlet = currentModalOutlet; // If states updated
- }
- }
-
- private getOutletKey(path: string, outletName: string): string {
- return path ? path + "-" + outletName : outletName;
- }
-
- ngOnDestroy() {
- if (isLogEnabled()) {
- routerLog("NSLocationStrategy.ngOnDestroy()");
- }
-
- this.outlets = [];
- this.currentOutlet = null;
- }
+ private outlets: Array = [];
+ private currentOutlet: Outlet;
+
+ private popStateCallbacks = new Array<(_: any) => any>();
+ private _currentNavigationOptions: NavigationOptions;
+ private currentUrlTree: UrlTree;
+
+ public _modalNavigationDepth = 0;
+
+ constructor(private frameService: FrameService) {
+ super();
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy.constructor()');
+ }
+ }
+
+ path(): string {
+ if (!this.currentUrlTree) {
+ return '/';
+ }
+
+ const state = this.currentOutlet && this.currentOutlet.peekState();
+ if (!state) {
+ return '/';
+ }
+
+ let tree = this.currentUrlTree;
+ let changedOutlet = this.getSegmentGroupByOutlet(this.currentOutlet);
+
+ // Handle case where the user declares a component at path "/".
+ // The url serializer doesn't parse this url as having a primary outlet.
+ if (state.isRootSegmentGroup) {
+ tree.root = state.segmentGroup;
+ } else if (changedOutlet) {
+ this.updateSegmentGroup(tree.root, changedOutlet, state.segmentGroup);
+ }
+
+ const urlSerializer = new DefaultUrlSerializer();
+ tree.queryParams = state.queryParams;
+ const url = urlSerializer.serialize(tree);
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy.path(): ' + url);
+ }
+ return url;
+ }
+
+ prepareExternalUrl(internal: string): string {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy.prepareExternalUrl() internal: ' + internal);
+ }
+ return internal;
+ }
+
+ pushState(state: any, title: string, url: string, queryParams: string): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy.pushState state: ' + `${state}, title: ${title}, url: ${url}, queryParams: ${queryParams}`);
+ }
+ this.pushStateInternal(state, title, url, queryParams);
+ }
+
+ pushStateInternal(state: any, title: string, url: string, queryParams: string): void {
+ const urlSerializer = new DefaultUrlSerializer();
+ this.currentUrlTree = urlSerializer.parse(url);
+ const urlTreeRoot = this.currentUrlTree.root;
+
+ // Handle case where the user declares a component at path "/".
+ // The url serializer doesn't parse this url as having a primary outlet.
+ if (!Object.keys(urlTreeRoot.children).length) {
+ const segmentGroup = this.currentUrlTree && this.currentUrlTree.root;
+ const outletKey = this.getOutletKey(this.getSegmentGroupFullPath(segmentGroup), 'primary');
+ const outlet = this.findOutlet(outletKey);
+
+ if (outlet && this.updateStates(outlet, segmentGroup, this.currentUrlTree.queryParams)) {
+ this.currentOutlet = outlet; // If states updated
+ } else if (!outlet) {
+ // tslint:disable-next-line:max-line-length
+ const rootOutlet = this.createOutlet('primary', null, segmentGroup, null, null, this.currentUrlTree.queryParams);
+ this.currentOutlet = rootOutlet;
+ }
+
+ this.currentOutlet.peekState().isRootSegmentGroup = true;
+ return;
+ }
+
+ const queue = [];
+ let currentTree = urlTreeRoot;
+
+ while (currentTree) {
+ Object.keys(currentTree.children).forEach((outletName) => {
+ const currentSegmentGroup = currentTree.children[outletName];
+ currentSegmentGroup.outlet = outletName;
+ currentSegmentGroup.root = urlTreeRoot;
+
+ const outletPath = this.getSegmentGroupFullPath(currentTree);
+ let outletKey = this.getOutletKey(outletPath, outletName);
+ let outlet = this.findOutlet(outletKey);
+
+ const parentOutletName = currentTree.outlet || '';
+ const parentOutletPath = this.getSegmentGroupFullPath(currentTree.parent);
+ const parentOutletKey = this.getOutletKey(parentOutletPath, parentOutletName);
+ const parentOutlet = this.findOutlet(parentOutletKey);
+
+ const containsLastState = outlet && outlet.containsTopState(currentSegmentGroup.toString());
+ if (!outlet) {
+ // tslint:disable-next-line:max-line-length
+ outlet = this.createOutlet(outletKey, outletPath, currentSegmentGroup, parentOutlet, this._modalNavigationDepth, this.currentUrlTree.queryParams);
+ this.currentOutlet = outlet;
+ } else if (this._modalNavigationDepth > 0 && outlet.showingModal && !containsLastState) {
+ // Navigation inside modal view.
+ this.upsertModalOutlet(outlet, currentSegmentGroup, this.currentUrlTree.queryParams);
+ } else {
+ outlet.parent = parentOutlet;
+
+ if (this.updateStates(outlet, currentSegmentGroup, this.currentUrlTree.queryParams)) {
+ this.currentOutlet = outlet; // If states updated
+ }
+ }
+
+ queue.push(currentSegmentGroup);
+ });
+
+ currentTree = queue.shift();
+ }
+ }
+
+ replaceState(state: any, title: string, url: string, queryParams: string): void {
+ const states = this.currentOutlet && this.currentOutlet.states;
+
+ if (states && states.length > 0) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy.replaceState changing existing state: ' + `${state}, title: ${title}, url: ${url}, queryParams: ${queryParams}`);
+ }
+ } else {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy.replaceState pushing new state: ' + `${state}, title: ${title}, url: ${url}, queryParams: ${queryParams}`);
+ }
+ this.pushStateInternal(state, title, url, queryParams);
+ }
+ }
+
+ forward(): void {
+ throw new Error('NSLocationStrategy.forward() - not implemented');
+ }
+
+ back(outlet?: Outlet, frame?: Frame): void {
+ this.currentOutlet = outlet || this.currentOutlet;
+
+ if (this.currentOutlet.isPageNavigationBack) {
+ const states = this.currentOutlet.states;
+ // We are navigating to the previous page
+ // clear the stack until we get to a page navigation state
+ let state = states.pop();
+ let count = 1;
+
+ if (frame) {
+ while (state.frame && state.frame !== frame) {
+ state = states.pop();
+ count++;
+ }
+ }
+
+ while (!state.isPageNavigation) {
+ state = states.pop();
+ count++;
+ }
+
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog(`NSLocationStrategy.back() while navigating back. States popped: ${count}`);
+ }
+ this.callPopState(state, true);
+ } else {
+ let state = this.currentOutlet.peekState();
+ if (state && state.isPageNavigation) {
+ // This was a page navigation - so navigate through frame.
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy.back() while not navigating back but top' + ' state is page - will call frame.goBack()');
+ }
+
+ if (!outlet) {
+ const topmostFrame = this.frameService.getFrame();
+ this.currentOutlet = this.getOutletByFrame(topmostFrame) || this.currentOutlet;
+ }
+
+ const frameToBack: Frame = this.currentOutlet.getFrameToBack();
+ if (frameToBack) {
+ frameToBack.goBack();
+ }
+ } else {
+ // Nested navigation - just pop the state
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy.back() while not navigating back but top' + ' state is not page - just pop');
+ }
+
+ this.callPopState(this.currentOutlet.states.pop(), true);
+ }
+ }
+ }
+
+ canGoBack(outlet?: Outlet) {
+ outlet = outlet || this.currentOutlet;
+ return outlet.states.length > 1;
+ }
+
+ onPopState(fn: (_: any) => any): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy.onPopState');
+ }
+ this.popStateCallbacks.push(fn);
+ }
+
+ getBaseHref(): string {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy.getBaseHref()');
+ }
+ return '';
+ }
+
+ private callPopState(state: LocationState, pop: boolean = true, outlet?: Outlet) {
+ outlet = outlet || this.currentOutlet;
+ const urlSerializer = new DefaultUrlSerializer();
+ let changedOutlet = this.getSegmentGroupByOutlet(outlet);
+
+ if (state && changedOutlet) {
+ this.updateSegmentGroup(this.currentUrlTree.root, changedOutlet, state.segmentGroup);
+ } else if (changedOutlet) {
+ // when closing modal view there are scenarios (e.g. root viewContainerRef) when we need
+ // to clean up the named page router outlet to make sure we will open the modal properly again if needed.
+ this.updateSegmentGroup(this.currentUrlTree.root, changedOutlet, null);
+ }
+
+ const url = urlSerializer.serialize(this.currentUrlTree);
+ const change = { url: url, pop: pop };
+ for (let fn of this.popStateCallbacks) {
+ fn(change);
+ }
+ }
+
+ public toString() {
+ let result = [];
+
+ this.outlets.forEach((outlet) => {
+ const outletStates = outlet.states;
+ const outletLog = outletStates
+ // tslint:disable-next-line:max-line-length
+ .map((v, i) => `${outlet.outletKeys}.${i}.[${v.isPageNavigation ? 'PAGE' : 'INTERNAL'}].[${outlet.modalNavigationDepth ? 'MODAL' : 'BASE'}] "${v.segmentGroup.toString()}"`)
+ .reverse();
+
+ result = result.concat(outletLog);
+ });
+
+ return result.join('\n');
+ }
+
+ // Methods for syncing with page navigation in PageRouterOutlet
+ public _beginBackPageNavigation(frame: Frame) {
+ const outlet: Outlet = this.getOutletByFrame(frame);
+
+ if (!outlet || outlet.isPageNavigationBack) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerError('Attempted to call startGoBack while going back.');
+ }
+ return;
+ }
+
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy.startGoBack()');
+ }
+ outlet.isPageNavigationBack = true;
+
+ this.currentOutlet = outlet;
+ }
+
+ public _finishBackPageNavigation(frame: Frame) {
+ const outlet: Outlet = this.getOutletByFrame(frame);
+
+ if (!outlet || !outlet.isPageNavigationBack) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerError('Attempted to call endGoBack while not going back.');
+ }
+ return;
+ }
+
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy.finishBackPageNavigation()');
+ }
+ outlet.isPageNavigationBack = false;
+ }
+
+ public _beginModalNavigation(frame: Frame): void {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy._beginModalNavigation()');
+ }
+
+ this.currentOutlet = this.getOutletByFrame(frame) || this.currentOutlet;
+
+ // It is possible to have frame, but not corresponding Outlet, if
+ // showing modal dialog on app.component.ts ngOnInit() e.g. In that case
+ // the modal is treated as none modal navigation.
+ if (this.currentOutlet) {
+ this.currentOutlet.showingModal = true;
+ this._modalNavigationDepth++;
+ }
+ }
+
+ public _closeModalNavigation() {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy.closeModalNavigation()');
+ }
+
+ const isShowingModal = this._modalNavigationDepth > 0;
+
+ if (isShowingModal) {
+ this._modalNavigationDepth--;
+ }
+
+ // currentOutlet should be the one that corresponds to the topmost frame
+ const topmostOutlet = this.getOutletByFrame(this.frameService.getFrame());
+ const outlet = this.findOutletByModal(this._modalNavigationDepth, isShowingModal) || topmostOutlet;
+
+ if (outlet) {
+ this.currentOutlet = outlet;
+ this.currentOutlet.showingModal = false;
+ this.callPopState(this.currentOutlet.peekState(), false);
+ }
+ }
+
+ public _beginPageNavigation(frame: Frame): NavigationOptions {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy._beginPageNavigation()');
+ }
+
+ this.currentOutlet = this.getOutletByFrame(frame);
+ const lastState = this.currentOutlet.peekState();
+
+ if (lastState) {
+ lastState.isPageNavigation = true;
+ }
+
+ const navOptions = this._currentNavigationOptions || defaultNavOptions;
+ if (navOptions.clearHistory) {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy._beginPageNavigation clearing states history');
+ }
+ this.currentOutlet.states = [lastState];
+ }
+
+ this._currentNavigationOptions = undefined;
+ return navOptions;
+ }
+
+ public _setNavigationOptions(options: NavigationOptions) {
+ this._currentNavigationOptions = {
+ clearHistory: isPresent(options.clearHistory) ? options.clearHistory : false,
+ animated: isPresent(options.animated) ? options.animated : true,
+ transition: options.transition,
+ };
+
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy._setNavigationOptions(' + `${JSON.stringify(this._currentNavigationOptions)})`);
+ }
+ }
+
+ public _getOutlets(): Array {
+ return this.outlets;
+ }
+
+ updateOutletFrame(outlet: Outlet, frame: Frame, isEmptyOutletFrame: boolean) {
+ const lastState = outlet.peekState();
+
+ if (lastState && !lastState.frame && !isEmptyOutletFrame) {
+ lastState.frame = frame;
+ }
+
+ if (!outlet.containsFrame(frame)) {
+ outlet.frames.push(frame);
+ }
+ this.currentOutlet = outlet;
+ }
+
+ clearOutlet(frame: Frame) {
+ this.outlets = this.outlets.filter((currentOutlet) => {
+ let isEqualToCurrent;
+
+ if (this.currentOutlet) {
+ isEqualToCurrent = currentOutlet.pathByOutlets === this.currentOutlet.pathByOutlets;
+ }
+
+ // Remove outlet from the url tree.
+ if (currentOutlet.containsFrame(frame) && !isEqualToCurrent) {
+ this.callPopState(null, true, currentOutlet);
+ }
+
+ // Skip frames filtering since currentOutlet is when no frames available.
+ if (currentOutlet.frames.length && !currentOutlet.isNSEmptyOutlet) {
+ currentOutlet.frames = currentOutlet.frames.filter((currentFrame) => currentFrame !== frame);
+ return currentOutlet.frames.length;
+ }
+
+ return !currentOutlet.containsFrame(frame);
+ });
+ }
+
+ getSegmentGroupFullPath(segmentGroup: UrlSegmentGroup): string {
+ let fullPath = '';
+
+ while (segmentGroup) {
+ const url = segmentGroup.toString();
+
+ if (fullPath) {
+ fullPath = (url ? url + '/' : '') + fullPath;
+ } else {
+ fullPath = url;
+ }
+
+ segmentGroup = segmentGroup.parent;
+ }
+
+ return fullPath;
+ }
+
+ getRouteFullPath(currentRoute: any): string {
+ const outletName = currentRoute.outlet;
+ let fullPath;
+
+ currentRoute = currentRoute.parent;
+ while (currentRoute) {
+ const urls = currentRoute.url.value || currentRoute.url;
+ let url = urls;
+
+ if (Array.isArray(urls)) {
+ url = url.join('/');
+ }
+
+ fullPath = fullPath ? (url ? url + '/' : url) + fullPath : url;
+ currentRoute = currentRoute.parent;
+ }
+
+ return fullPath ? fullPath + '-' + outletName : outletName;
+ }
+
+ getPathByOutlets(urlSegmentGroup: any): string {
+ if (!urlSegmentGroup) {
+ return '';
+ }
+
+ let pathToOutlet;
+ let lastPath = urlSegmentGroup.outlet || 'primary';
+ let parent = urlSegmentGroup.parent;
+
+ while (parent && urlSegmentGroup.root !== parent) {
+ if (parent && parent.outlet !== lastPath) {
+ if (lastPath === 'primary') {
+ lastPath = parent.outlet;
+ } else {
+ lastPath = parent.outlet;
+ pathToOutlet = lastPath + '-' + (pathToOutlet || urlSegmentGroup.outlet);
+ }
+ }
+
+ parent = parent.parent;
+ }
+
+ return pathToOutlet || lastPath;
+ }
+
+ findOutlet(outletKey: string, activatedRouteSnapshot?: ActivatedRouteSnapshot): Outlet {
+ let outlet: Outlet = this.outlets.find((currentOutlet) => {
+ let equalModalDepth = currentOutlet.modalNavigationDepth === this._modalNavigationDepth;
+ return equalModalDepth && currentOutlet.outletKeys.indexOf(outletKey) > -1;
+ });
+
+ // No Outlet with the given outletKey could happen when using nested unnamed p-r-o
+ // primary -> primary -> prymary
+ if (!outlet && activatedRouteSnapshot) {
+ const pathByOutlets = this.getPathByOutlets(activatedRouteSnapshot);
+ outlet = this.outlets.find((currentOutlet) => {
+ let equalModalDepth = currentOutlet.modalNavigationDepth === this._modalNavigationDepth;
+ return equalModalDepth && currentOutlet.pathByOutlets === pathByOutlets;
+ });
+ }
+
+ return outlet;
+ }
+
+ private findOutletByModal(modalNavigation: number, isShowingModal?: boolean): Outlet {
+ return this.outlets.find((outlet) => {
+ let equalModalDepth = outlet.modalNavigationDepth === modalNavigation;
+ return isShowingModal ? equalModalDepth && outlet.showingModal : equalModalDepth;
+ });
+ }
+
+ private getOutletByFrame(frame: Frame): Outlet {
+ let outlet;
+
+ for (let index = 0; index < this.outlets.length; index++) {
+ const currentOutlet = this.outlets[index];
+
+ if (currentOutlet.containsFrame(frame)) {
+ outlet = currentOutlet;
+ break;
+ }
+ }
+
+ return outlet;
+ }
+
+ private updateStates(outlet: Outlet, currentSegmentGroup: UrlSegmentGroup, queryParams: Params): boolean {
+ const isNewPage = outlet.states.length === 0;
+ const lastState = outlet.states[outlet.states.length - 1];
+ const equalStateUrls = outlet.containsTopState(currentSegmentGroup.toString());
+
+ const locationState: LocationState = {
+ segmentGroup: currentSegmentGroup,
+ isRootSegmentGroup: false,
+ isPageNavigation: isNewPage,
+ queryParams: { ...queryParams },
+ };
+
+ if (!lastState || !equalStateUrls) {
+ outlet.states.push(locationState);
+
+ // Update last state segmentGroup of parent Outlet.
+ if (this._modalNavigationDepth === 0 && !outlet.showingModal) {
+ this.updateParentsStates(outlet, currentSegmentGroup.parent);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private updateParentsStates(outlet: Outlet, newSegmentGroup: UrlSegmentGroup) {
+ let parentOutlet = outlet.parent;
+
+ // Update parents lastState segmentGroups
+ while (parentOutlet && newSegmentGroup) {
+ const state = parentOutlet.peekState();
+
+ if (state) {
+ state.segmentGroup = newSegmentGroup;
+ newSegmentGroup = newSegmentGroup.parent;
+ parentOutlet = parentOutlet.parent;
+ }
+ }
+ }
+
+ // tslint:disable-next-line:max-line-length
+ private createOutlet(outletKey: string, path: string, segmentGroup: any, parent: Outlet, modalNavigation?: number, queryParams: Params = {}): Outlet {
+ const pathByOutlets = this.getPathByOutlets(segmentGroup);
+ const newOutlet = new Outlet(outletKey, path, pathByOutlets, modalNavigation);
+
+ const locationState: LocationState = {
+ segmentGroup: segmentGroup,
+ isRootSegmentGroup: false,
+ isPageNavigation: true, // It is a new OutletNode.
+ queryParams: { ...queryParams },
+ };
+
+ newOutlet.states = [locationState];
+ newOutlet.parent = parent;
+ this.outlets.push(newOutlet);
+
+ // Update last state segmentGroup of parent Outlet.
+ if (this._modalNavigationDepth === 0 && !newOutlet.showingModal) {
+ this.updateParentsStates(newOutlet, segmentGroup.parent);
+ }
+
+ return newOutlet;
+ }
+
+ private getSegmentGroupByOutlet(outlet: Outlet): UrlSegmentGroup {
+ const pathList = outlet.pathByOutlets.split('-');
+ let segmentGroup = this.currentUrlTree.root;
+ let pathToOutlet;
+
+ for (let index = 0; index < pathList.length; index++) {
+ const currentPath = pathList[index];
+ const childrenCount = Object.keys(segmentGroup.children).length;
+
+ if (childrenCount && segmentGroup.children[currentPath]) {
+ const url = segmentGroup.toString();
+ pathToOutlet = pathToOutlet ? pathToOutlet + '/' + url : url;
+ segmentGroup = segmentGroup.children[currentPath];
+ } else {
+ // If no child outlet found with the given name - forget about all previously found outlets.
+ // example: seaching for 'primary-second-primary' shouldn't return 'primary-second'
+ // if no 'primary' child available on 'second'.
+ segmentGroup = null;
+ break;
+ }
+ }
+
+ // Paths should also match since there could be another Outlet
+ // with the same pathByOutlets but different url path.
+ if (segmentGroup && outlet.path && pathToOutlet && outlet.path !== pathToOutlet) {
+ segmentGroup = null;
+ }
+
+ return segmentGroup;
+ }
+
+ // Traversal and replacement of segmentGroup.
+ private updateSegmentGroup(rootNode: any, oldSegmentGroup: UrlSegmentGroup, newSegmentGroup: UrlSegmentGroup) {
+ const queue = [];
+ let currentTree = rootNode;
+
+ while (currentTree) {
+ Object.keys(currentTree.children).forEach((outletName) => {
+ if (currentTree.children[outletName] === oldSegmentGroup) {
+ if (newSegmentGroup) {
+ currentTree.children[outletName] = newSegmentGroup;
+ } else {
+ delete currentTree.children[outletName];
+ }
+ }
+ queue.push(currentTree.children[outletName]);
+ });
+
+ currentTree = queue.shift();
+ }
+ }
+
+ private upsertModalOutlet(parentOutlet: Outlet, segmentedGroup: UrlSegmentGroup, queryParams: Params) {
+ let currentModalOutlet = this.findOutletByModal(this._modalNavigationDepth);
+
+ // We want to treat every p-r-o as a standalone Outlet.
+ if (!currentModalOutlet) {
+ if (this._modalNavigationDepth > 1) {
+ // The parent of the current Outlet should be the previous opened modal (if any).
+ parentOutlet = this.findOutletByModal(this._modalNavigationDepth - 1);
+ }
+
+ // No currentModalOutlet available when opening 'primary' p-r-o.
+ const outletName = 'primary';
+ const outletPath = parentOutlet.peekState().segmentGroup.toString();
+ const outletKey = this.getOutletKey(outletPath, outletName);
+ // tslint:disable-next-line:max-line-length
+ currentModalOutlet = this.createOutlet(outletKey, outletPath, segmentedGroup, parentOutlet, this._modalNavigationDepth, queryParams);
+ this.currentOutlet = currentModalOutlet;
+ } else if (this.updateStates(currentModalOutlet, segmentedGroup, queryParams)) {
+ this.currentOutlet = currentModalOutlet; // If states updated
+ }
+ }
+
+ private getOutletKey(path: string, outletName: string): string {
+ return path ? path + '-' + outletName : outletName;
+ }
+
+ ngOnDestroy() {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NSLocationStrategy.ngOnDestroy()');
+ }
+
+ this.outlets = [];
+ this.currentOutlet = null;
+ }
}
diff --git a/nativescript-angular/router/ns-location-utils.ts b/nativescript-angular/router/ns-location-utils.ts
new file mode 100644
index 000000000..aa5452820
--- /dev/null
+++ b/nativescript-angular/router/ns-location-utils.ts
@@ -0,0 +1,88 @@
+import { Frame, NavigationTransition } from '@nativescript/core';
+import { DefaultUrlSerializer, UrlSegmentGroup, UrlTree, ActivatedRouteSnapshot, Params } from '@angular/router';
+
+export interface LocationState {
+ queryParams: Params;
+ segmentGroup: UrlSegmentGroup;
+ isRootSegmentGroup: boolean;
+ isPageNavigation: boolean;
+ frame?: Frame;
+}
+
+export interface NavigationOptions {
+ clearHistory?: boolean;
+ animated?: boolean;
+ transition?: NavigationTransition;
+}
+
+export class Outlet {
+ showingModal: boolean;
+ modalNavigationDepth: number;
+ parent: Outlet;
+ isPageNavigationBack: boolean;
+
+ // More than one key available when using NSEmptyOutletComponent component
+ // in module that lazy loads children (loadChildren) and has outlet name.
+ outletKeys: Array;
+
+ // More than one frame available when using NSEmptyOutletComponent component
+ // in module that lazy loads children (loadChildren) and has outlet name.
+ frames: Array = [];
+ // The url path to the Outlet.
+ // E.G: the path to Outlet3 that is nested Outlet1(home)->Outlet2(nested1)->Outlet3(nested2)
+ // will be 'home/nested1'
+ path: string;
+ pathByOutlets: string;
+ states: Array = [];
+ isNSEmptyOutlet: boolean;
+
+ // Used in reuse-strategy by its children to determine if they should be detached too.
+ shouldDetach: boolean = true;
+ constructor(outletKey: string, path: string, pathByOutlets: string, modalNavigationDepth?: number) {
+ this.outletKeys = [outletKey];
+ this.isPageNavigationBack = false;
+ this.showingModal = false;
+ this.modalNavigationDepth = modalNavigationDepth || 0;
+ this.pathByOutlets = pathByOutlets;
+ this.path = path;
+ }
+
+ containsFrame(frame: Frame): boolean {
+ return this.frames.indexOf(frame) > -1;
+ }
+
+ peekState(): LocationState {
+ if (this.states.length > 0) {
+ return this.states[this.states.length - 1];
+ }
+ return null;
+ }
+
+ containsTopState(stateUrl: string): boolean {
+ const lastState = this.peekState();
+ return lastState && lastState.segmentGroup.toString() === stateUrl;
+ }
+
+ // Search for frame that can go back.
+ // Nested 'primary' outlets could result in Outlet with multiple navigatable frames.
+ getFrameToBack(): Frame {
+ let frame = this.frames[this.frames.length - 1];
+
+ if (!this.isNSEmptyOutlet) {
+ for (let index = this.frames.length - 1; index >= 0; index--) {
+ const currentFrame = this.frames[index];
+ if (currentFrame.canGoBack()) {
+ frame = currentFrame;
+ break;
+ }
+ }
+ }
+
+ return frame;
+ }
+}
+
+export const defaultNavOptions: NavigationOptions = {
+ clearHistory: false,
+ animated: true,
+};
diff --git a/nativescript-angular/router/ns-module-factory-loader.ts b/nativescript-angular/router/ns-module-factory-loader.ts
index 80dacef4b..4d825ad02 100644
--- a/nativescript-angular/router/ns-module-factory-loader.ts
+++ b/nativescript-angular/router/ns-module-factory-loader.ts
@@ -1,19 +1,9 @@
-import {
- Compiler,
- Injectable,
- Optional,
- SystemJsNgModuleLoader,
- SystemJsNgModuleLoaderConfig,
-} from "@angular/core";
+import { Compiler, Injectable, Optional, SystemJsNgModuleLoader, SystemJsNgModuleLoaderConfig } from '@angular/core';
@Injectable()
export class NSModuleFactoryLoader extends SystemJsNgModuleLoader {
- constructor(
- compiler: Compiler,
- @Optional() config?: SystemJsNgModuleLoaderConfig
- ) {
- super(compiler, config);
- console.log(`NSModuleFactoryLoader is deprecated! ` +
- `You no longer need to provide it as a module loader.`);
- }
+ constructor(compiler: Compiler, @Optional() config?: SystemJsNgModuleLoaderConfig) {
+ super(compiler, config);
+ console.log(`NSModuleFactoryLoader is deprecated! ` + `You no longer need to provide it as a module loader.`);
+ }
}
diff --git a/nativescript-angular/router/ns-platform-location.ts b/nativescript-angular/router/ns-platform-location.ts
index b1828e4e6..bd2dbdca7 100644
--- a/nativescript-angular/router/ns-platform-location.ts
+++ b/nativescript-angular/router/ns-platform-location.ts
@@ -1,64 +1,62 @@
-import { NSLocationStrategy } from "./ns-location-strategy";
-import { PlatformLocation, LocationChangeListener } from "@angular/common";
-import { Injectable } from "@angular/core";
-import { routerLog, isLogEnabled } from "../trace";
+import { NSLocationStrategy } from './ns-location-strategy';
+import { PlatformLocation, LocationChangeListener } from '@angular/common';
+import { Injectable } from '@angular/core';
+import { NativeScriptDebug } from '../trace';
@Injectable()
export class NativescriptPlatformLocation extends PlatformLocation {
-
- constructor(private locationStrategy: NSLocationStrategy) {
- super();
- if (isLogEnabled()) {
- routerLog("NativescriptPlatformLocation.constructor()");
- }
- }
-
- getState(): any {
- return undefined;
- }
-
- readonly hostname: string;
- readonly href: string;
- readonly port: string;
- readonly protocol: string;
-
- getBaseHrefFromDOM(): string {
- return "/";
- }
-
- onPopState(fn: LocationChangeListener): void {
- this.locationStrategy.onPopState(fn);
- }
-
- onHashChange(_fn: LocationChangeListener): void {
- }
-
- get search(): string {
- return "";
- }
- get hash(): string {
- return "";
- }
- get pathname(): string {
- 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.locationStrategy.pushState(state, title, url, null);
- }
-
- replaceState(state: any, title: string, url: string): void {
- this.locationStrategy.replaceState(state, title, url, null);
- }
-
- forward(): void {
- throw new Error("NativescriptPlatformLocation.forward() - not implemented");
- }
-
- back(): void {
- this.locationStrategy.back();
- }
+ constructor(private locationStrategy: NSLocationStrategy) {
+ super();
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog('NativescriptPlatformLocation.constructor()');
+ }
+ }
+
+ getState(): any {
+ return undefined;
+ }
+
+ readonly hostname: string;
+ readonly href: string;
+ readonly port: string;
+ readonly protocol: string;
+
+ getBaseHrefFromDOM(): string {
+ return '/';
+ }
+
+ onPopState(fn: LocationChangeListener): void {
+ this.locationStrategy.onPopState(fn);
+ }
+
+ onHashChange(_fn: LocationChangeListener): void {}
+
+ get search(): string {
+ return '';
+ }
+ get hash(): string {
+ return '';
+ }
+ get pathname(): string {
+ 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.locationStrategy.pushState(state, title, url, null);
+ }
+
+ replaceState(state: any, title: string, url: string): void {
+ this.locationStrategy.replaceState(state, title, url, null);
+ }
+
+ forward(): void {
+ throw new Error('NativescriptPlatformLocation.forward() - not implemented');
+ }
+
+ back(): void {
+ this.locationStrategy.back();
+ }
}
diff --git a/nativescript-angular/router/ns-route-reuse-strategy.ts b/nativescript-angular/router/ns-route-reuse-strategy.ts
index 4de61062d..e05179c4e 100644
--- a/nativescript-angular/router/ns-route-reuse-strategy.ts
+++ b/nativescript-angular/router/ns-route-reuse-strategy.ts
@@ -1,88 +1,88 @@
-import { Injectable } from "@angular/core";
-import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from "@angular/router";
+import { Injectable } from '@angular/core';
+import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router';
-import { routeReuseStrategyLog as log, isLogEnabled } from "../trace";
-import { NSLocationStrategy } from "./ns-location-strategy";
-import {
- destroyComponentRef,
- findTopActivatedRouteNodeForOutlet,
- pageRouterActivatedSymbol
-} from "./page-router-outlet";
+import { NativeScriptDebug } from '../trace';
+import { NSLocationStrategy } from './ns-location-strategy';
+import { destroyComponentRef, findTopActivatedRouteNodeForOutlet, pageRouterActivatedSymbol } from './page-router-outlet-utils';
interface CacheItem {
- key: string;
- state: DetachedRouteHandle;
- isModal: boolean;
+ key: string;
+ state: DetachedRouteHandle;
+ isModal: boolean;
}
+const getSnapshotKey = function (snapshot: ActivatedRouteSnapshot): string {
+ return snapshot.pathFromRoot.join('->');
+};
+
/**
* Detached state cache
*/
class DetachedStateCache {
- private cache = new Array();
-
- public get length(): number {
- return this.cache.length;
- }
-
- public push(cacheItem: CacheItem) {
- this.cache.push(cacheItem);
- }
-
- public pop(): CacheItem {
- return this.cache.pop();
- }
-
- public peek(): CacheItem {
- return this.cache[this.cache.length - 1];
- }
-
- public clear() {
- if (isLogEnabled()) {
- log(`DetachedStateCache.clear() ${this.cache.length} items will be destroyed`);
- }
-
- while (this.cache.length > 0) {
- const state = this.cache.pop().state;
- if (!state.componentRef) {
- throw new Error("No componentRed found in DetachedRouteHandle");
- }
-
- destroyComponentRef(state.componentRef);
- }
- }
-
- public clearModalCache() {
- let removedItemsCount = 0;
- const hasModalPages = this.cache.some(cacheItem => {
- return cacheItem.isModal;
- });
-
- if (hasModalPages) {
- let modalCacheCleared = false;
-
- while (!modalCacheCleared) {
- let cacheItem = this.peek();
- const state = cacheItem.state;
-
- if (!state.componentRef) {
- throw new Error("No componentRef found in DetachedRouteHandle");
- }
-
- destroyComponentRef(state.componentRef);
- if (cacheItem.isModal) {
- modalCacheCleared = true;
- }
-
- this.pop();
- removedItemsCount++;
- }
- }
-
- if (isLogEnabled()) {
- log(`DetachedStateCache.clearModalCache() ${removedItemsCount} items will be destroyed`);
- }
- }
+ private cache = new Array();
+
+ public get length(): number {
+ return this.cache.length;
+ }
+
+ public push(cacheItem: CacheItem) {
+ this.cache.push(cacheItem);
+ }
+
+ public pop(): CacheItem {
+ return this.cache.pop();
+ }
+
+ public peek(): CacheItem {
+ return this.cache[this.cache.length - 1];
+ }
+
+ public clear() {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routeReuseStrategyLog(`DetachedStateCache.clear() ${this.cache.length} items will be destroyed`);
+ }
+
+ while (this.cache.length > 0) {
+ const state = this.cache.pop().state;
+ if (!state.componentRef) {
+ throw new Error('No componentRed found in DetachedRouteHandle');
+ }
+
+ destroyComponentRef(state.componentRef);
+ }
+ }
+
+ public clearModalCache() {
+ let removedItemsCount = 0;
+ const hasModalPages = this.cache.some((cacheItem) => {
+ return cacheItem.isModal;
+ });
+
+ if (hasModalPages) {
+ let modalCacheCleared = false;
+
+ while (!modalCacheCleared) {
+ let cacheItem = this.peek();
+ const state = cacheItem.state;
+
+ if (!state.componentRef) {
+ throw new Error('No componentRef found in DetachedRouteHandle');
+ }
+
+ destroyComponentRef(state.componentRef);
+ if (cacheItem.isModal) {
+ modalCacheCleared = true;
+ }
+
+ this.pop();
+ removedItemsCount++;
+ }
+ }
+
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routeReuseStrategyLog(`DetachedStateCache.clearModalCache() ${removedItemsCount} items will be destroyed`);
+ }
+ }
}
/**
@@ -92,154 +92,149 @@ class DetachedStateCache {
*/
@Injectable()
export class NSRouteReuseStrategy implements RouteReuseStrategy {
- private cacheByOutlet: { [key: string]: DetachedStateCache } = {};
-
- constructor(private location: NSLocationStrategy) { }
-
- shouldDetach(route: ActivatedRouteSnapshot): boolean {
- route = findTopActivatedRouteNodeForOutlet(route);
-
- const outletKey = this.location.getRouteFullPath(route);
- const outlet = this.location.findOutlet(outletKey, route);
- const key = getSnapshotKey(route);
- const isPageActivated = route[pageRouterActivatedSymbol];
- const isBack = outlet ? outlet.isPageNavigationBack : false;
- let shouldDetach = outlet && !isBack && isPageActivated;
-
- if (outlet) {
- if (outlet.parent && !outlet.parent.shouldDetach) {
- shouldDetach = false;
- }
-
- outlet.shouldDetach = shouldDetach;
- }
-
- if (isLogEnabled()) {
- log(`shouldDetach isBack: ${isBack} key: ${key} result: ${shouldDetach}`);
- }
-
- return shouldDetach;
- }
-
- shouldAttach(route: ActivatedRouteSnapshot): boolean {
- route = findTopActivatedRouteNodeForOutlet(route);
-
- const outletKey = this.location.getRouteFullPath(route);
- const outlet = this.location.findOutlet(outletKey, route);
- const cache = this.cacheByOutlet[outletKey];
- if (!cache) {
- return false;
- }
-
- const key = getSnapshotKey(route);
- const isBack = outlet ? outlet.isPageNavigationBack : false;
- const shouldAttach = isBack && cache.peek().key === key;
-
- if (isLogEnabled()) {
- log(`shouldAttach isBack: ${isBack} key: ${key} result: ${shouldAttach}`);
- }
-
- if (outlet) {
- outlet.shouldDetach = true;
- }
-
- return shouldAttach;
- }
-
- store(route: ActivatedRouteSnapshot, state: DetachedRouteHandle): void {
- route = findTopActivatedRouteNodeForOutlet(route);
-
- const key = getSnapshotKey(route);
- if (isLogEnabled()) {
- log(`store key: ${key}, state: ${state}`);
- }
-
- const outletKey = this.location.getRouteFullPath(route);
-
- // tslint:disable-next-line:max-line-length
- const cache = this.cacheByOutlet[outletKey] = this.cacheByOutlet[outletKey] || new DetachedStateCache();
-
- if (state) {
- let isModal = false;
- if (this.location._modalNavigationDepth > 0) {
- isModal = true;
- }
-
- cache.push({ key, state, isModal });
- } else {
- const topItem = cache.peek();
- if (topItem.key === key) {
- cache.pop();
-
- if (!cache.length) {
- delete this.cacheByOutlet[outletKey];
- }
- } else {
- throw new Error("Trying to pop from DetachedStateCache but keys don't match. " +
- `expected: ${topItem.key} actual: ${key}`);
- }
- }
- }
-
- retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
- route = findTopActivatedRouteNodeForOutlet(route);
-
- const outletKey = this.location.getRouteFullPath(route);
- const outlet = this.location.findOutlet(outletKey, route);
- const cache = this.cacheByOutlet[outletKey];
- if (!cache) {
- return null;
- }
-
- const key = getSnapshotKey(route);
- const isBack = outlet ? outlet.isPageNavigationBack : false;
- const cachedItem = cache.peek();
-
- let state = null;
- if (isBack && cachedItem && cachedItem.key === key) {
- state = cachedItem.state;
- }
-
- if (isLogEnabled()) {
- log(`retrieved isBack: ${isBack} key: ${key} state: ${state}`);
- }
-
- return state;
- }
-
- shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
- const shouldReuse = future.routeConfig === curr.routeConfig;
-
- if (shouldReuse && curr && curr[pageRouterActivatedSymbol]) {
- // When reusing route - copy the pageRouterActivated to the new snapshot
- // It's needed in shouldDetach to determine if the route should be detached.
- future[pageRouterActivatedSymbol] = curr[pageRouterActivatedSymbol];
- }
-
- if (isLogEnabled()) {
- log(`shouldReuseRoute result: ${shouldReuse}`);
- }
-
- return shouldReuse;
- }
-
- clearCache(outletKey: string) {
- const cache = this.cacheByOutlet[outletKey];
-
- if (cache) {
- cache.clear();
- }
- }
-
- clearModalCache(outletKey: string) {
- const cache = this.cacheByOutlet[outletKey];
-
- if (cache) {
- cache.clearModalCache();
- }
- }
-}
-
-function getSnapshotKey(snapshot: ActivatedRouteSnapshot): string {
- return snapshot.pathFromRoot.join("->");
+ private cacheByOutlet: { [key: string]: DetachedStateCache } = {};
+
+ constructor(private location: NSLocationStrategy) {}
+
+ shouldDetach(route: ActivatedRouteSnapshot): boolean {
+ route = findTopActivatedRouteNodeForOutlet(route);
+
+ const outletKey = this.location.getRouteFullPath(route);
+ const outlet = this.location.findOutlet(outletKey, route);
+ const key = getSnapshotKey(route);
+ const isPageActivated = route[pageRouterActivatedSymbol];
+ const isBack = outlet ? outlet.isPageNavigationBack : false;
+ let shouldDetach = outlet && !isBack && isPageActivated;
+
+ if (outlet) {
+ if (outlet.parent && !outlet.parent.shouldDetach) {
+ shouldDetach = false;
+ }
+
+ outlet.shouldDetach = shouldDetach;
+ }
+
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routeReuseStrategyLog(`shouldDetach isBack: ${isBack} key: ${key} result: ${shouldDetach}`);
+ }
+
+ return shouldDetach;
+ }
+
+ shouldAttach(route: ActivatedRouteSnapshot): boolean {
+ route = findTopActivatedRouteNodeForOutlet(route);
+
+ const outletKey = this.location.getRouteFullPath(route);
+ const outlet = this.location.findOutlet(outletKey, route);
+ const cache = this.cacheByOutlet[outletKey];
+ if (!cache) {
+ return false;
+ }
+
+ const key = getSnapshotKey(route);
+ const isBack = outlet ? outlet.isPageNavigationBack : false;
+ const shouldAttach = isBack && cache.peek().key === key;
+
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routeReuseStrategyLog(`shouldAttach isBack: ${isBack} key: ${key} result: ${shouldAttach}`);
+ }
+
+ if (outlet) {
+ outlet.shouldDetach = true;
+ }
+
+ return shouldAttach;
+ }
+
+ store(route: ActivatedRouteSnapshot, state: DetachedRouteHandle): void {
+ route = findTopActivatedRouteNodeForOutlet(route);
+
+ const key = getSnapshotKey(route);
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routeReuseStrategyLog(`store key: ${key}, state: ${state}`);
+ }
+
+ const outletKey = this.location.getRouteFullPath(route);
+
+ // tslint:disable-next-line:max-line-length
+ const cache = (this.cacheByOutlet[outletKey] = this.cacheByOutlet[outletKey] || new DetachedStateCache());
+
+ if (state) {
+ let isModal = false;
+ if (this.location._modalNavigationDepth > 0) {
+ isModal = true;
+ }
+
+ cache.push({ key, state, isModal });
+ } else {
+ const topItem = cache.peek();
+ if (topItem.key === key) {
+ cache.pop();
+
+ if (!cache.length) {
+ delete this.cacheByOutlet[outletKey];
+ }
+ } else {
+ throw new Error("Trying to pop from DetachedStateCache but keys don't match. " + `expected: ${topItem.key} actual: ${key}`);
+ }
+ }
+ }
+
+ retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
+ route = findTopActivatedRouteNodeForOutlet(route);
+
+ const outletKey = this.location.getRouteFullPath(route);
+ const outlet = this.location.findOutlet(outletKey, route);
+ const cache = this.cacheByOutlet[outletKey];
+ if (!cache) {
+ return null;
+ }
+
+ const key = getSnapshotKey(route);
+ const isBack = outlet ? outlet.isPageNavigationBack : false;
+ const cachedItem = cache.peek();
+
+ let state = null;
+ if (isBack && cachedItem && cachedItem.key === key) {
+ state = cachedItem.state;
+ }
+
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routeReuseStrategyLog(`retrieved isBack: ${isBack} key: ${key} state: ${state}`);
+ }
+
+ return state;
+ }
+
+ shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
+ const shouldReuse = future.routeConfig === curr.routeConfig;
+
+ if (shouldReuse && curr && curr[pageRouterActivatedSymbol]) {
+ // When reusing route - copy the pageRouterActivated to the new snapshot
+ // It's needed in shouldDetach to determine if the route should be detached.
+ future[pageRouterActivatedSymbol] = curr[pageRouterActivatedSymbol];
+ }
+
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routeReuseStrategyLog(`shouldReuseRoute result: ${shouldReuse}`);
+ }
+
+ return shouldReuse;
+ }
+
+ clearCache(outletKey: string) {
+ const cache = this.cacheByOutlet[outletKey];
+
+ if (cache) {
+ cache.clear();
+ }
+ }
+
+ clearModalCache(outletKey: string) {
+ const cache = this.cacheByOutlet[outletKey];
+
+ if (cache) {
+ cache.clearModalCache();
+ }
+ }
}
diff --git a/nativescript-angular/router/ns-router-link-active.ts b/nativescript-angular/router/ns-router-link-active.ts
index 86ff6b05d..475dd7ead 100644
--- a/nativescript-angular/router/ns-router-link-active.ts
+++ b/nativescript-angular/router/ns-router-link-active.ts
@@ -1,15 +1,10 @@
-import {
- AfterContentInit, ContentChildren, Directive,
- ElementRef, Input, OnChanges, OnDestroy,
- QueryList, Renderer2
-} from "@angular/core";
-import { Subscription } from "rxjs";
+import { AfterContentInit, ContentChildren, Directive, ElementRef, Input, OnChanges, OnDestroy, QueryList, Renderer2 } from '@angular/core';
+import { Subscription } from 'rxjs';
-import { NavigationEnd, Router, UrlTree } from "@angular/router";
-import { containsTree } from "./private-imports/router-url-tree";
-
-import { NSRouterLink } from "./ns-router-link";
+import { NavigationEnd, Router, UrlTree } from '@angular/router';
+import { containsTree } from './private-imports/router-url-tree';
+import { NSRouterLink } from './ns-router-link';
/**
* The NSRouterLinkActive directive lets you add a CSS class to an element when the link"s route
@@ -54,77 +49,77 @@ import { NSRouterLink } from "./ns-router-link";
* @stable
*/
@Directive({
- selector: "[nsRouterLinkActive]",
- exportAs: "routerLinkActive",
+ selector: '[nsRouterLinkActive]',
+ exportAs: 'routerLinkActive',
})
-export class NSRouterLinkActive implements OnChanges, OnDestroy, AfterContentInit { // tslint:disable-line:max-line-length directive-class-suffix
- @ContentChildren(NSRouterLink) links: QueryList;
+export class NSRouterLinkActive implements OnChanges, OnDestroy, AfterContentInit {
+ // tslint:disable-line:max-line-length directive-class-suffix
+ @ContentChildren(NSRouterLink) links: QueryList;
- private classes: string[] = [];
- private subscription: Subscription;
- private active: boolean = false;
+ private classes: string[] = [];
+ private subscription: Subscription;
+ private active: boolean = false;
- @Input() nsRouterLinkActiveOptions: { exact: boolean } = { exact: false };
+ @Input() nsRouterLinkActiveOptions: { exact: boolean } = { exact: false };
- constructor(private router: Router, private element: ElementRef, private renderer: Renderer2) {
- this.subscription = router.events.subscribe(s => {
- if (s instanceof NavigationEnd) {
- this.update();
- }
- });
- }
+ constructor(private router: Router, private element: ElementRef, private renderer: Renderer2) {
+ this.subscription = router.events.subscribe((s) => {
+ if (s instanceof NavigationEnd) {
+ this.update();
+ }
+ });
+ }
- get isActive(): boolean {
- return this.active;
- }
+ get isActive(): boolean {
+ return this.active;
+ }
- ngAfterContentInit(): void {
- this.links.changes.subscribe(() => this.update());
- this.update();
- }
+ ngAfterContentInit(): void {
+ this.links.changes.subscribe(() => this.update());
+ this.update();
+ }
- @Input("nsRouterLinkActive")
- set nsRouterLinkActive(data: string[] | string) {
- if (Array.isArray(data)) {
- this.classes = data;
- } else {
- this.classes = data.split(" ");
- }
- }
+ @Input('nsRouterLinkActive')
+ set nsRouterLinkActive(data: string[] | string) {
+ if (Array.isArray(data)) {
+ this.classes = data;
+ } else {
+ this.classes = data.split(' ');
+ }
+ }
- ngOnChanges(_: {}): any { this.update(); }
- ngOnDestroy(): any { this.subscription.unsubscribe(); }
+ ngOnChanges(_: {}): any {
+ this.update();
+ }
+ ngOnDestroy(): any {
+ this.subscription.unsubscribe();
+ }
- private update(): void {
- if (!this.links) {
- return;
- }
- const hasActiveLinks = this.hasActiveLinks();
- // react only when status has changed to prevent unnecessary dom updates
- if (this.active !== hasActiveLinks) {
- const currentUrlTree = this.router.parseUrl(this.router.url);
- const isActiveLinks = this.reduceList(currentUrlTree, this.links);
- this.classes.forEach(
- c => this.renderer.setStyle(
- this.element.nativeElement, c, isActiveLinks));
- }
- Promise.resolve(hasActiveLinks).then(active => this.active = active);
- }
+ private update(): void {
+ if (!this.links) {
+ return;
+ }
+ const hasActiveLinks = this.hasActiveLinks();
+ // react only when status has changed to prevent unnecessary dom updates
+ if (this.active !== hasActiveLinks) {
+ const currentUrlTree = this.router.parseUrl(this.router.url);
+ const isActiveLinks = this.reduceList(currentUrlTree, this.links);
+ this.classes.forEach((c) => this.renderer.setStyle(this.element.nativeElement, c, isActiveLinks));
+ }
+ Promise.resolve(hasActiveLinks).then((active) => (this.active = active));
+ }
- private reduceList(currentUrlTree: UrlTree, q: QueryList): boolean {
- return q.reduce(
- (res: boolean, link: NSRouterLink) => {
- return res || containsTree(currentUrlTree, link.urlTree,
- this.nsRouterLinkActiveOptions.exact);
- }, false);
- }
+ private reduceList(currentUrlTree: UrlTree, q: QueryList): boolean {
+ return q.reduce((res: boolean, link: NSRouterLink) => {
+ return res || containsTree(currentUrlTree, link.urlTree, this.nsRouterLinkActiveOptions.exact);
+ }, false);
+ }
- private isLinkActive(router: Router): (link: NSRouterLink) => boolean {
- return (link: NSRouterLink) =>
- router.isActive(link.urlTree, this.nsRouterLinkActiveOptions.exact);
- }
+ private isLinkActive(router: Router): (link: NSRouterLink) => boolean {
+ return (link: NSRouterLink) => router.isActive(link.urlTree, this.nsRouterLinkActiveOptions.exact);
+ }
- private hasActiveLinks(): boolean {
- return this.links.some(this.isLinkActive(this.router));
- }
+ private hasActiveLinks(): boolean {
+ return this.links.some(this.isLinkActive(this.router));
+ }
}
diff --git a/nativescript-angular/router/ns-router-link.ts b/nativescript-angular/router/ns-router-link.ts
index 6fb2d7cfc..00c9b0c59 100644
--- a/nativescript-angular/router/ns-router-link.ts
+++ b/nativescript-angular/router/ns-router-link.ts
@@ -1,14 +1,13 @@
-import { Directive, HostListener, Input } from "@angular/core";
-import { NavigationExtras } from "@angular/router";
-import { ActivatedRoute, Router, UrlTree } from "@angular/router";
-import { routerLog, isLogEnabled } from "../trace";
-import { RouterExtensions } from "./router-extensions";
-import { NavigationOptions } from "./ns-location-strategy";
-import { NavigationTransition } from "@nativescript/core/ui/frame";
-import { isString } from "@nativescript/core/utils/types";
+import { Directive, HostListener, Input } from '@angular/core';
+import { NavigationExtras } from '@angular/router';
+import { ActivatedRoute, Router, UrlTree } from '@angular/router';
+import { NavigationTransition } from '@nativescript/core';
+import { NativeScriptDebug } from '../trace';
+import { RouterExtensions } from './router-extensions';
+import { NavigationOptions } from './ns-location-utils';
// Copied from "@angular/router/src/config"
-export type QueryParamsHandling = "merge" | "preserve" | "";
+export type QueryParamsHandling = 'merge' | 'preserve' | '';
/**
* The nsRouterLink directive lets you link to specific parts of your app.
@@ -34,116 +33,109 @@ export type QueryParamsHandling = "merge" | "preserve" | "";
* instead look in the current component"s children for the route.
* And if the segment begins with `../`, the router will go up one level.
*/
-@Directive({ selector: "[nsRouterLink]" })
-export class NSRouterLink { // tslint:disable-line:directive-class-suffix
- @Input() target: string;
- @Input() queryParams: { [k: string]: any };
- @Input() fragment: string;
-
- @Input() queryParamsHandling: QueryParamsHandling;
- @Input() preserveQueryParams: boolean;
- @Input() preserveFragment: boolean;
- @Input() skipLocationChange: boolean;
- @Input() replaceUrl: boolean;
-
- @Input() clearHistory: boolean;
- @Input() pageTransition: boolean | string | NavigationTransition = true;
- @Input() pageTransitionDuration;
-
- private commands: any[] = [];
-
- constructor(
- private router: Router,
- private navigator: RouterExtensions,
- private route: ActivatedRoute) {
- }
-
- @Input("nsRouterLink")
- set params(data: any[] | string) {
- if (Array.isArray(data)) {
- this.commands = data;
- } else {
- this.commands = [data];
- }
- }
-
- @HostListener("tap")
- onTap() {
- if (isLogEnabled()) {
- routerLog(`nsRouterLink.tapped: ${this.commands} ` +
- `clear: ${this.clearHistory} ` +
- `transition: ${JSON.stringify(this.pageTransition)} ` +
- `duration: ${this.pageTransitionDuration}`);
- }
-
- const extras = this.getExtras();
- this.navigator.navigateByUrl(this.urlTree, extras);
- }
-
- private getExtras(): NavigationExtras & NavigationOptions {
- const transition = this.getTransition();
- return {
- skipLocationChange: attrBoolValue(this.skipLocationChange),
- replaceUrl: attrBoolValue(this.replaceUrl),
-
- clearHistory: this.convertClearHistory(this.clearHistory),
- animated: transition.animated,
- transition: transition.transition,
- };
- }
-
- get urlTree(): UrlTree {
- const urlTree = this.router.createUrlTree(this.commands, {
- relativeTo: this.route,
- queryParams: this.queryParams,
- fragment: this.fragment,
- preserveQueryParams: attrBoolValue(this.preserveQueryParams),
- queryParamsHandling: this.queryParamsHandling,
- preserveFragment: attrBoolValue(this.preserveFragment),
- });
-
- if (isLogEnabled()) {
- routerLog(`nsRouterLink urlTree created: ${urlTree}`);
- }
-
- return urlTree;
- }
-
-
- private convertClearHistory(value: boolean | string): boolean {
- return value === true || value === "true";
- }
-
- private getTransition(): { animated: boolean, transition?: NavigationTransition } {
- let transition: NavigationTransition;
- let animated: boolean;
-
- if (typeof this.pageTransition === "boolean") {
- animated = this.pageTransition;
- } else if (isString(this.pageTransition)) {
- if (this.pageTransition === "none" || this.pageTransition === "false") {
- animated = false;
- } else {
- animated = true;
- transition = {
- name: this.pageTransition
- };
- }
- } else {
- animated = true;
- transition = this.pageTransition;
- }
-
- let duration = +this.pageTransitionDuration;
- if (!isNaN(duration)) {
- transition = transition || {};
- transition.duration = duration;
- }
-
- return { animated, transition };
- }
+@Directive({ selector: '[nsRouterLink]' })
+export class NSRouterLink {
+ // tslint:disable-line:directive-class-suffix
+ @Input() target: string;
+ @Input() queryParams: { [k: string]: any };
+ @Input() fragment: string;
+
+ @Input() queryParamsHandling: QueryParamsHandling;
+ @Input() preserveQueryParams: boolean;
+ @Input() preserveFragment: boolean;
+ @Input() skipLocationChange: boolean;
+ @Input() replaceUrl: boolean;
+
+ @Input() clearHistory: boolean;
+ @Input() pageTransition: boolean | string | NavigationTransition = true;
+ @Input() pageTransitionDuration;
+
+ private commands: any[] = [];
+
+ constructor(private router: Router, private navigator: RouterExtensions, private route: ActivatedRoute) {}
+
+ @Input('nsRouterLink')
+ set params(data: any[] | string) {
+ if (Array.isArray(data)) {
+ this.commands = data;
+ } else {
+ this.commands = [data];
+ }
+ }
+
+ @HostListener('tap')
+ onTap() {
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog(`nsRouterLink.tapped: ${this.commands} ` + `clear: ${this.clearHistory} ` + `transition: ${JSON.stringify(this.pageTransition)} ` + `duration: ${this.pageTransitionDuration}`);
+ }
+
+ const extras = this.getExtras();
+ this.navigator.navigateByUrl(this.urlTree, extras);
+ }
+
+ private getExtras(): NavigationExtras & NavigationOptions {
+ const transition = this.getTransition();
+ return {
+ skipLocationChange: attrBoolValue(this.skipLocationChange),
+ replaceUrl: attrBoolValue(this.replaceUrl),
+
+ clearHistory: this.convertClearHistory(this.clearHistory),
+ animated: transition.animated,
+ transition: transition.transition,
+ };
+ }
+
+ get urlTree(): UrlTree {
+ const urlTree = this.router.createUrlTree(this.commands, {
+ relativeTo: this.route,
+ queryParams: this.queryParams,
+ fragment: this.fragment,
+ preserveQueryParams: attrBoolValue(this.preserveQueryParams),
+ queryParamsHandling: this.queryParamsHandling,
+ preserveFragment: attrBoolValue(this.preserveFragment),
+ });
+
+ if (NativeScriptDebug.isLogEnabled()) {
+ NativeScriptDebug.routerLog(`nsRouterLink urlTree created: ${urlTree}`);
+ }
+
+ return urlTree;
+ }
+
+ private convertClearHistory(value: boolean | string): boolean {
+ return value === true || value === 'true';
+ }
+
+ private getTransition(): { animated: boolean; transition?: NavigationTransition } {
+ let transition: NavigationTransition;
+ let animated: boolean;
+
+ if (typeof this.pageTransition === 'boolean') {
+ animated = this.pageTransition;
+ } else if (typeof this.pageTransition === 'string') {
+ if (this.pageTransition === 'none' || this.pageTransition === 'false') {
+ animated = false;
+ } else {
+ animated = true;
+ transition = {
+ name: this.pageTransition,
+ };
+ }
+ } else {
+ animated = true;
+ transition = this.pageTransition;
+ }
+
+ let duration = +this.pageTransitionDuration;
+ if (!isNaN(duration)) {
+ transition = transition || {};
+ transition.duration = duration;
+ }
+
+ return { animated, transition };
+ }
}
function attrBoolValue(s: any): boolean {
- return s === "" || !!s;
+ return s === '' || !!s;
}
diff --git a/nativescript-angular/router/page-router-outlet-utils.ts b/nativescript-angular/router/page-router-outlet-utils.ts
new file mode 100644
index 000000000..4b9db354c
--- /dev/null
+++ b/nativescript-angular/router/page-router-outlet-utils.ts
@@ -0,0 +1,43 @@
+import { ComponentRef } from '@angular/core';
+import { ActivatedRoute, ActivatedRouteSnapshot, ChildrenOutletContexts, PRIMARY_OUTLET } from '@angular/router';
+
+/**
+ * There are cases where multiple activatedRoute nodes should be associated/handled by the same PageRouterOutlet.
+ * We can gat additional ActivatedRoutes nodes when there is:
+ * - Lazy loading - there is an additional ActivatedRoute node for the RouteConfig with the `loadChildren` setup
+ * - Componentless routes - there is an additional ActivatedRoute node for the componentless RouteConfig
+ *
+ * Example:
+ * R <-- root
+ * |
+ * feature (lazy module) <-- RouteConfig: { path: "lazy", loadChildren: "./feature/feature.module#FeatureModule" }
+ * |
+ * module (componentless route) <-- RouteConfig: { path: "module", children: [...] } // Note: No 'component'
+ * |
+ * home <-- RouteConfig: { path: "module", component: MyComponent } - this is what we get as activatedRoute param
+ *
+ * In these cases we will mark the top-most node (feature). NSRouteReuseStrategy will detach the tree there and
+ * use this ActivateRoute as a kay for caching.
+ */
+export function findTopActivatedRouteNodeForOutlet(activatedRoute: ActivatedRouteSnapshot): ActivatedRouteSnapshot {
+ let outletActivatedRoute = activatedRoute;
+
+ while (outletActivatedRoute.parent && outletActivatedRoute.parent.routeConfig && !outletActivatedRoute.parent.routeConfig.component) {
+ outletActivatedRoute = outletActivatedRoute.parent;
+ }
+
+ return outletActivatedRoute;
+}
+
+export const pageRouterActivatedSymbol = Symbol('page-router-activated');
+export const loaderRefSymbol = Symbol('loader-ref');
+
+export function destroyComponentRef(componentRef: ComponentRef) {
+ if (componentRef) {
+ const loaderRef = componentRef[loaderRefSymbol];
+ if (loaderRef) {
+ loaderRef.destroy();
+ }
+ componentRef.destroy();
+ }
+}
diff --git a/nativescript-angular/router/page-router-outlet.ts b/nativescript-angular/router/page-router-outlet.ts
index 28f200ab8..879378732 100644
--- a/nativescript-angular/router/page-router-outlet.ts
+++ b/nativescript-angular/router/page-router-outlet.ts
@@ -1,472 +1,401 @@
-import {
- Attribute, ChangeDetectorRef,
- ComponentFactory, ComponentFactoryResolver, ComponentRef,
- Directive, Inject, InjectionToken, Injector,
- OnDestroy, EventEmitter, Output,
- Type, ViewContainerRef, ElementRef, InjectFlags
-} from "@angular/core";
-import {
- ActivatedRoute,
- ActivatedRouteSnapshot,
- ChildrenOutletContexts,
- PRIMARY_OUTLET,
-} from "@angular/router";
-
-import { Device } from "@nativescript/core/platform";
-import { Frame } from "@nativescript/core/ui/frame";
-import { Page, NavigatedData } from "@nativescript/core/ui/page";
-import { profile } from "@nativescript/core/profiling";
-
-import { BehaviorSubject } from "rxjs";
-
-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";
-import { NSLocationStrategy, Outlet } from "./ns-location-strategy";
-import { NSRouteReuseStrategy } from "./ns-route-reuse-strategy";
+import { Attribute, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, Directive, Inject, InjectionToken, Injector, OnDestroy, EventEmitter, Output, Type, ViewContainerRef, ElementRef, InjectFlags, NgZone } from '@angular/core';
+import { ActivatedRoute, ActivatedRouteSnapshot, ChildrenOutletContexts, PRIMARY_OUTLET } from '@angular/router';
-export class PageRoute {
- activatedRoute: BehaviorSubject;
+import { Frame, Page, NavigatedData, profile, Device } from '@nativescript/core';
- constructor(startRoute: ActivatedRoute) {
- this.activatedRoute = new BehaviorSubject(startRoute);
- }
-}
+import { BehaviorSubject } from 'rxjs';
-// Used to "mark" ActivatedRoute snapshots that are handled in PageRouterOutlet
-export const pageRouterActivatedSymbol = Symbol("page-router-activated");
-export const loaderRefSymbol = Symbol("loader-ref");
-
-export function destroyComponentRef(componentRef: ComponentRef) {
- if (componentRef) {
- const loaderRef = componentRef[loaderRefSymbol];
- if (loaderRef) {
- loaderRef.destroy();
- }
- componentRef.destroy();
- }
-}
-
-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 ref;
- }
- destroy() {
- this.refs.forEach((ref) => {
- if (ref.ngOnDestroy instanceof Function) {
- ref.ngOnDestroy();
- }
- });
- this.refs.clear();
- }
-}
+import { PAGE_FACTORY, PageFactory } from '../platform-providers';
+import { NativeScriptDebug } from '../trace';
+import { DetachedLoader } from '../common/detached-loader';
+import { ViewUtil } from '../view-util';
+import { NSLocationStrategy } from './ns-location-strategy';
+import { Outlet } from './ns-location-utils';
+import { NSRouteReuseStrategy } from './ns-route-reuse-strategy';
+import { findTopActivatedRouteNodeForOutlet, pageRouterActivatedSymbol, loaderRefSymbol, destroyComponentRef } from './page-router-outlet-utils';
-type ProviderSet = Set | InjectionToken>;
+export class PageRoute {
+ activatedRoute: BehaviorSubject;
-/**
- * There are cases where multiple activatedRoute nodes should be associated/handled by the same PageRouterOutlet.
- * We can gat additional ActivatedRoutes nodes when there is:
- * - Lazy loading - there is an additional ActivatedRoute node for the RouteConfig with the `loadChildren` setup
- * - Componentless routes - there is an additional ActivatedRoute node for the componentless RouteConfig
- *
- * Example:
- * R <-- root
- * |
- * feature (lazy module) <-- RouteConfig: { path: "lazy", loadChildren: "./feature/feature.module#FeatureModule" }
- * |
- * module (componentless route) <-- RouteConfig: { path: "module", children: [...] } // Note: No 'component'
- * |
- * home <-- RouteConfig: { path: "module", component: MyComponent } - this is what we get as activatedRoute param
- *
- * In these cases we will mark the top-most node (feature). NSRouteReuseStrategy will detach the tree there and
- * use this ActivateRoute as a kay for caching.
- */
-export function findTopActivatedRouteNodeForOutlet(activatedRoute: ActivatedRouteSnapshot): ActivatedRouteSnapshot {
- let outletActivatedRoute = activatedRoute;
-
- while (outletActivatedRoute.parent &&
- outletActivatedRoute.parent.routeConfig &&
- !outletActivatedRoute.parent.routeConfig.component) {
-
- outletActivatedRoute = outletActivatedRoute.parent;
- }
-
- return outletActivatedRoute;
+ constructor(startRoute: ActivatedRoute) {
+ this.activatedRoute = new BehaviorSubject(startRoute);
+ }
}
-function routeToString(activatedRoute: ActivatedRoute | ActivatedRouteSnapshot): string {
- return activatedRoute.pathFromRoot.join("->");
+export 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 ref;
+ }
+ destroy() {
+ this.refs.forEach((ref) => {
+ if (ref.ngOnDestroy instanceof Function) {
+ ref.ngOnDestroy();
+ }
+ });
+ this.refs.clear();
+ }
}
-@Directive({ selector: "page-router-outlet" }) // tslint:disable-line:directive-selector
-export class PageRouterOutlet implements OnDestroy { // tslint:disable-line:directive-class-suffix
- private activated: ComponentRef | null = null;
- private _activatedRoute: ActivatedRoute | null = null;
- private detachedLoaderFactory: ComponentFactory;
-
- private outlet: Outlet;
- private name: string;
- private isEmptyOutlet: boolean;
- private viewUtil: ViewUtil;
- private frame: Frame;
-
- @Output("activate") activateEvents = new EventEmitter(); // tslint:disable-line:no-output-rename
- @Output("deactivate") deactivateEvents = new EventEmitter(); // tslint:disable-line:no-output-rename
-
- /** @deprecated from Angular since v4 */
- get locationInjector(): Injector { return this.location.injector; }
- /** @deprecated from Angular since v4 */
- get locationFactoryResolver(): ComponentFactoryResolver { return this.resolver; }
-
- get isActivated(): boolean {
- return !!this.activated;
- }
-
- get component(): Object {
- if (!this.activated) {
- if (isLogEnabled()) {
- log("Outlet is not activated");
- }
- return;
- }
-
- return this.activated.instance;
- }
- get activatedRoute(): ActivatedRoute {
- if (!this.activated) {
- if (isLogEnabled()) {
- log("Outlet is not activated");
- }
- return;
- }
-
- return this._activatedRoute;
- }
-
- constructor(
- private parentContexts: ChildrenOutletContexts,
- private location: ViewContainerRef,
- @Attribute("name") name: string,
- @Attribute("actionBarVisibility") actionBarVisibility: string,
- @Attribute("isEmptyOutlet") isEmptyOutlet: boolean,
- private locationStrategy: NSLocationStrategy,
- private componentFactoryResolver: ComponentFactoryResolver,
- private resolver: ComponentFactoryResolver,
- private changeDetector: ChangeDetectorRef,
- @Inject(DEVICE) device: Device,
- @Inject(PAGE_FACTORY) private pageFactory: PageFactory,
- private routeReuseStrategy: NSRouteReuseStrategy,
- elRef: ElementRef
- ) {
- this.isEmptyOutlet = isEmptyOutlet;
- this.frame = elRef.nativeElement;
- this.setActionBarVisibility(actionBarVisibility);
- if (isLogEnabled()) {
- log(`PageRouterOutlet.constructor frame: ${this.frame}`);
- }
-
- this.name = name || PRIMARY_OUTLET;
- parentContexts.onChildOutletCreated(this.name, this);
-
- this.viewUtil = new ViewUtil(device);
- this.detachedLoaderFactory = resolver.resolveComponentFactory(DetachedLoader);
- }
-
- setActionBarVisibility(actionBarVisibility: string): void {
- switch (actionBarVisibility) {
- case "always":
- case "never":
- this.frame.actionBarVisibility = actionBarVisibility;
- return;
-
- default:
- this.frame.actionBarVisibility = "auto";
- }
- }
-
- ngOnDestroy(): void {
- // Clear accumulated modal view page cache when page-router-outlet
- // destroyed on modal view closing
- this.parentContexts.onChildOutletDestroyed(this.name);
-
- if (this.outlet) {
- this.outlet.outletKeys.forEach(key => {
- this.routeReuseStrategy.clearModalCache(key);
- });
- this.locationStrategy.clearOutlet(this.frame);
- } else {
- log("PageRouterOutlet.ngOnDestroy: no outlet available for page-router-outlet");
- }
-
- if (this.isActivated) {
- const c = this.activated.instance;
- this.activated.hostView.detach();
- destroyComponentRef(this.activated);
-
- this.deactivateEvents.emit(c);
- this.activated = null;
- }
- }
-
- deactivate(): void {
- if (!this.outlet || !this.outlet.isPageNavigationBack) {
- if (isLogEnabled()) {
- log("Currently not in page back navigation - component should be detached instead of deactivated.");
- }
- return;
- }
-
- if (isLogEnabled()) {
- log("PageRouterOutlet.deactivate() while going back - should destroy");
- }
-
- if (!this.isActivated) {
- return;
- }
-
- const c = this.activated.instance;
- destroyComponentRef(this.activated);
-
- this.activated = null;
- this._activatedRoute = null;
-
- this.deactivateEvents.emit(c);
- }
-
- /**
- * Called when the `RouteReuseStrategy` instructs to detach the subtree
- */
- detach(): ComponentRef {
- if (!this.isActivated) {
- if (isLogEnabled()) {
- log("Outlet is not activated");
- }
- return;
- }
-
- if (isLogEnabled()) {
- log(`PageRouterOutlet.detach() - ${routeToString(this._activatedRoute)}`);
- }
-
- // Detach from ChangeDetection
- this.activated.hostView.detach();
-
- const component = this.activated;
- this.activated = null;
- this._activatedRoute = null;
- return component;
- }
-
- /**
- * Called when the `RouteReuseStrategy` instructs to re-attach a previously detached subtree
- */
- attach(ref: ComponentRef, activatedRoute: ActivatedRoute) {
- if (isLogEnabled()) {
- log(`PageRouterOutlet.attach() - ${routeToString(activatedRoute)}`);
- }
-
- this.activated = ref;
-
- // reattach to ChangeDetection
- this.activated.hostView.markForCheck();
- this.activated.hostView.reattach();
- this._activatedRoute = activatedRoute;
- this.markActivatedRoute(activatedRoute);
-
- this.locationStrategy._finishBackPageNavigation(this.frame);
- }
-
- /**
- * Called by the Router to instantiate a new component during the commit phase of a navigation.
- * This method in turn is responsible for calling the `routerOnActivate` hook of its child.
- */
- @profile
- activateWith(
- activatedRoute: ActivatedRoute,
- resolver: ComponentFactoryResolver | null): void {
-
- this.outlet = this.outlet || this.getOutlet(activatedRoute.snapshot);
- if (!this.outlet) {
- if (isLogEnabled()) {
- error("No outlet found relative to activated route");
- }
- return;
- }
-
- this.outlet.isNSEmptyOutlet = this.isEmptyOutlet;
- this.locationStrategy.updateOutletFrame(this.outlet, this.frame, this.isEmptyOutlet);
-
- if (this.outlet && this.outlet.isPageNavigationBack) {
- if (isLogEnabled()) {
- log("Currently in page back navigation - component should be reattached instead of activated.");
- }
- this.locationStrategy._finishBackPageNavigation(this.frame);
- }
-
- if (isLogEnabled()) {
- log(`PageRouterOutlet.activateWith() - ${routeToString(activatedRoute)}`);
- }
-
- this._activatedRoute = activatedRoute;
-
- this.markActivatedRoute(activatedRoute);
-
- resolver = resolver || this.resolver;
-
- this.activateOnGoForward(activatedRoute, resolver);
- this.activateEvents.emit(this.activated.instance);
- }
-
- private activateOnGoForward(
- activatedRoute: ActivatedRoute,
- loadedResolver: ComponentFactoryResolver
- ): void {
- if (isLogEnabled()) {
- log("PageRouterOutlet.activate() forward navigation - " +
- "create detached loader in the loader container");
- }
-
- const factory = this.getComponentFactory(activatedRoute, loadedResolver);
- const page = this.pageFactory({
- isNavigation: true,
- componentType: factory.componentType,
- });
-
- const destructables = new Set([PageService]);
- const injector = Injector.create({
- providers: [
- { provide: Page, useValue: page },
- { provide: PageService, useClass: PageService, deps: [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 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);
- this.loadComponentInPage(page, this.activated, { activatedRoute });
-
- this.activated[loaderRefSymbol] = loaderRef;
- }
-
- @profile
- private loadComponentInPage(page: Page, componentRef: ComponentRef, navigationContext): void {
- // Component loaded. Find its root native view.
- const componentView = componentRef.location.nativeElement;
- // Remove it from original native parent.
- this.viewUtil.removeChild(componentView.parent, componentView);
- // Add it to the new page
- this.viewUtil.insertChild(page, componentView);
-
- const navigatedFromCallback = (