diff --git a/e2e/single-page/.gitignore b/e2e/single-page/.gitignore
new file mode 100644
index 000000000..8e824499e
--- /dev/null
+++ b/e2e/single-page/.gitignore
@@ -0,0 +1,10 @@
+.vscode
+
+platforms
+node_modules
+hooks
+
+/**/*.js
+/**/*.map
+e2e/reports
+test-results.xml
\ No newline at end of file
diff --git a/e2e/single-page/app/App_Resources/Android/AndroidManifest.xml b/e2e/single-page/app/App_Resources/Android/AndroidManifest.xml
new file mode 100644
index 000000000..9db832151
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/Android/AndroidManifest.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/single-page/app/App_Resources/Android/app.gradle b/e2e/single-page/app/App_Resources/Android/app.gradle
new file mode 100644
index 000000000..23f5c481b
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/Android/app.gradle
@@ -0,0 +1,23 @@
+// Add your native dependencies here:
+
+// Uncomment to add recyclerview-v7 dependency
+//dependencies {
+// compile 'com.android.support:recyclerview-v7:+'
+//}
+
+android {
+ defaultConfig {
+ generatedDensities = []
+ applicationId = "org.nativescript.singlepage"
+
+ //override supported platforms
+ // ndk {
+ // abiFilters.clear()
+ // abiFilters "armeabi-v7a"
+ // }
+
+ }
+ aaptOptions {
+ additionalParameters "--no-version-vectors"
+ }
+}
diff --git a/e2e/single-page/app/App_Resources/Android/drawable-nodpi/background.png b/e2e/single-page/app/App_Resources/Android/drawable-nodpi/background.png
new file mode 100644
index 000000000..748b2adf5
Binary files /dev/null and b/e2e/single-page/app/App_Resources/Android/drawable-nodpi/background.png differ
diff --git a/e2e/single-page/app/App_Resources/Android/drawable-nodpi/icon.png b/e2e/single-page/app/App_Resources/Android/drawable-nodpi/icon.png
new file mode 100755
index 000000000..ddfc17a71
Binary files /dev/null and b/e2e/single-page/app/App_Resources/Android/drawable-nodpi/icon.png differ
diff --git a/e2e/single-page/app/App_Resources/Android/drawable-nodpi/logo.png b/e2e/single-page/app/App_Resources/Android/drawable-nodpi/logo.png
new file mode 100644
index 000000000..b9e102a76
Binary files /dev/null and b/e2e/single-page/app/App_Resources/Android/drawable-nodpi/logo.png differ
diff --git a/e2e/single-page/app/App_Resources/Android/drawable-nodpi/splash_screen.xml b/e2e/single-page/app/App_Resources/Android/drawable-nodpi/splash_screen.xml
new file mode 100644
index 000000000..ada77f92c
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/Android/drawable-nodpi/splash_screen.xml
@@ -0,0 +1,8 @@
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/e2e/single-page/app/App_Resources/Android/values-v21/colors.xml b/e2e/single-page/app/App_Resources/Android/values-v21/colors.xml
new file mode 100644
index 000000000..a64641a9d
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/Android/values-v21/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #3d5afe
+
\ No newline at end of file
diff --git a/e2e/single-page/app/App_Resources/Android/values-v21/styles.xml b/e2e/single-page/app/App_Resources/Android/values-v21/styles.xml
new file mode 100644
index 000000000..dac8727c8
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/Android/values-v21/styles.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/single-page/app/App_Resources/Android/values/colors.xml b/e2e/single-page/app/App_Resources/Android/values/colors.xml
new file mode 100644
index 000000000..74ad8829c
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/Android/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #F5F5F5
+ #757575
+ #33B5E5
+ #272734
+
\ No newline at end of file
diff --git a/e2e/single-page/app/App_Resources/Android/values/styles.xml b/e2e/single-page/app/App_Resources/Android/values/styles.xml
new file mode 100644
index 000000000..1e8c7f29b
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/Android/values/styles.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..1953734f4
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,92 @@
+{
+ "images" : [
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "icon-29.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "icon-29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "icon-29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "icon-40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "icon-40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "icon-60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "icon-60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "icon-29.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "icon-29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "icon-40.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "icon-40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "icon-76.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "icon-76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "icon-83.5@2x.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png
new file mode 100644
index 000000000..9e15af09d
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png
new file mode 100644
index 000000000..7b9e55537
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png
new file mode 100644
index 000000000..76f61ec1f
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png
new file mode 100644
index 000000000..15b06db11
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png
new file mode 100644
index 000000000..585065f94
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png
new file mode 100644
index 000000000..a450c421d
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png
new file mode 100644
index 000000000..457b6d94c
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png
new file mode 100644
index 000000000..fa5a6ac86
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png
new file mode 100644
index 000000000..94abcf70d
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png
new file mode 100644
index 000000000..2e71dd3a0
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png
new file mode 100644
index 000000000..4abc9ec50
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/Contents.json b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/Contents.json
new file mode 100644
index 000000000..da4a164c9
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 000000000..4414bad08
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,158 @@
+{
+ "images" : [
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "736h",
+ "filename" : "Default-736h@3x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "portrait",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "736h",
+ "filename" : "Default-Landscape@3x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "landscape",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "667h",
+ "filename" : "Default-667h@2x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "portrait",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "filename" : "Default@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "retina4",
+ "filename" : "Default-568h@2x.png",
+ "minimum-system-version" : "7.0",
+ "orientation" : "portrait",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "filename" : "Default.png",
+ "extent" : "full-screen",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "filename" : "Default@2x.png",
+ "extent" : "full-screen",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "filename" : "Default-568h@2x.png",
+ "extent" : "full-screen",
+ "subtype" : "retina4",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "extent" : "to-status-bar",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait.png",
+ "extent" : "full-screen",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "extent" : "to-status-bar",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape.png",
+ "extent" : "full-screen",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "extent" : "to-status-bar",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait@2x.png",
+ "extent" : "full-screen",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "extent" : "to-status-bar",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape@2x.png",
+ "extent" : "full-screen",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png
new file mode 100644
index 000000000..d7f17fcd2
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png
new file mode 100644
index 000000000..b88415405
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png
new file mode 100644
index 000000000..faab4b631
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png
new file mode 100644
index 000000000..3365ba3cd
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
new file mode 100644
index 000000000..a44945c1a
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png
new file mode 100644
index 000000000..e6dca6269
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png
new file mode 100644
index 000000000..1a5007962
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
new file mode 100644
index 000000000..73d8b920f
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png
new file mode 100644
index 000000000..9f1f6ce3e
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png
new file mode 100644
index 000000000..514fc5cde
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json
new file mode 100644
index 000000000..4f4e9c506
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchScreen-AspectFill.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchScreen-AspectFill@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png
new file mode 100644
index 000000000..c293f9c7a
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png
new file mode 100644
index 000000000..233693a6e
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json
new file mode 100644
index 000000000..23c0ffd7a
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchScreen-Center.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchScreen-Center@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png
new file mode 100644
index 000000000..a5a775a2b
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png
new file mode 100644
index 000000000..154c19343
Binary files /dev/null and b/e2e/single-page/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png differ
diff --git a/e2e/single-page/app/App_Resources/iOS/Info.plist b/e2e/single-page/app/App_Resources/iOS/Info.plist
new file mode 100644
index 000000000..ea3e3ea23
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/iOS/Info.plist
@@ -0,0 +1,47 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ ${PRODUCT_NAME}
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIRequiresFullScreen
+
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+
+
diff --git a/e2e/single-page/app/App_Resources/iOS/LaunchScreen.storyboard b/e2e/single-page/app/App_Resources/iOS/LaunchScreen.storyboard
new file mode 100644
index 000000000..2ad9471e1
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/iOS/LaunchScreen.storyboard
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/single-page/app/App_Resources/iOS/build.xcconfig b/e2e/single-page/app/App_Resources/iOS/build.xcconfig
new file mode 100644
index 000000000..4b0118490
--- /dev/null
+++ b/e2e/single-page/app/App_Resources/iOS/build.xcconfig
@@ -0,0 +1,7 @@
+// You can add custom settings here
+// for example you can uncomment the following line to force distribution code signing
+// CODE_SIGN_IDENTITY = iPhone Distribution
+// 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/single-page/app/README.md b/e2e/single-page/app/README.md
new file mode 100644
index 000000000..ebe60c416
--- /dev/null
+++ b/e2e/single-page/app/README.md
@@ -0,0 +1,5 @@
+# NativeScript Tutorial Angular Template
+
+This repo serves as the starting point for NativeScript’s [Angular Getting Started Guide](https://docs.nativescript.org/angular/tutorial/ng-chapter-0).
+
+Please file any issues with this template on the [NativeScript/docs repository](https://github.com/nativescript/docs), which is where the tutorial content lives.
\ No newline at end of file
diff --git a/e2e/single-page/app/app-routing.module.ts b/e2e/single-page/app/app-routing.module.ts
new file mode 100644
index 000000000..2b650ee77
--- /dev/null
+++ b/e2e/single-page/app/app-routing.module.ts
@@ -0,0 +1,32 @@
+import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
+import { NativeScriptRouterModule } from "nativescript-angular/router";
+
+import { FirstComponent } from "./first/first.component"
+import { SecondComponent } from "./second/second.component"
+
+export const routes = [
+ {
+ path: "",
+ redirectTo: "/first",
+ pathMatch: "full"
+ },
+ {
+ path: "first",
+ component: FirstComponent,
+ },
+ {
+ path: "second/:id", component: SecondComponent,
+ },
+];
+
+export const navigatableComponents = [
+ FirstComponent,
+ SecondComponent,
+];
+
+@NgModule({
+ imports: [NativeScriptRouterModule.forRoot(routes)],
+ exports: [NativeScriptRouterModule],
+})
+export class AppRoutingModule { }
+
diff --git a/e2e/single-page/app/app.component.ts b/e2e/single-page/app/app.component.ts
new file mode 100644
index 000000000..7b9069e2c
--- /dev/null
+++ b/e2e/single-page/app/app.component.ts
@@ -0,0 +1,19 @@
+import { Component } from "@angular/core";
+
+@Component({
+ template: `
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class AppComponent { }
+
diff --git a/e2e/single-page/app/app.css b/e2e/single-page/app/app.css
new file mode 100644
index 000000000..c9e18996d
--- /dev/null
+++ b/e2e/single-page/app/app.css
@@ -0,0 +1,24 @@
+.title {
+ font-size: 30;
+ margin: 16;
+}
+
+.nav {
+ orientation: horizontal;
+ horizontal-align: stretch;
+ vertical-align: bottom;
+ padding: 4;
+ background-color: lightblue;
+ justify-content: space-around;
+ align-content: center;
+}
+
+.link {
+ margin: 10 30;
+ horizontal-align: center;
+}
+
+.active {
+ color: orangered;
+ background-color: lightgreen;
+}
diff --git a/e2e/single-page/app/app.module.ngfactory.d.ts b/e2e/single-page/app/app.module.ngfactory.d.ts
new file mode 100644
index 000000000..793157de3
--- /dev/null
+++ b/e2e/single-page/app/app.module.ngfactory.d.ts
@@ -0,0 +1,4 @@
+/**
+ * A dynamically generated module when compiled with AoT.
+ */
+export const AppModuleNgFactory: any;
\ No newline at end of file
diff --git a/e2e/single-page/app/app.module.ts b/e2e/single-page/app/app.module.ts
new file mode 100644
index 000000000..5d7dcbdbc
--- /dev/null
+++ b/e2e/single-page/app/app.module.ts
@@ -0,0 +1,35 @@
+import { NgModule, NgModuleFactoryLoader, NO_ERRORS_SCHEMA } from "@angular/core";
+import { NativeScriptModule } from "nativescript-angular/nativescript.module";
+import { NSModuleFactoryLoader } from "nativescript-angular/router";
+
+import "./rxjs-operators";
+
+import {
+ AppRoutingModule,
+ navigatableComponents,
+} from "./app-routing.module";
+
+import { AppComponent } from "./app.component";
+
+import { rendererTraceCategory, viewUtilCategory, routeReuseStrategyTraceCategory, routerTraceCategory } from "nativescript-angular/trace";
+import { setCategories, enable } from "trace";
+setCategories(routerTraceCategory + "," + routeReuseStrategyTraceCategory);
+enable();
+
+@NgModule({
+ declarations: [
+ AppComponent,
+ ...navigatableComponents,
+ ],
+ bootstrap: [AppComponent],
+ providers: [
+ { provide: NgModuleFactoryLoader, useClass: NSModuleFactoryLoader }
+ ],
+ imports: [
+ NativeScriptModule,
+ AppRoutingModule,
+ ],
+ schemas: [NO_ERRORS_SCHEMA],
+})
+export class AppModule { }
+
diff --git a/e2e/single-page/app/first/first.component.ts b/e2e/single-page/app/first/first.component.ts
new file mode 100644
index 000000000..436beb321
--- /dev/null
+++ b/e2e/single-page/app/first/first.component.ts
@@ -0,0 +1,31 @@
+import { Component, OnInit, OnDestroy, OnChanges } from "@angular/core";
+import { ActivatedRoute, Router, Route } from "@angular/router";
+import { Location } from "@angular/common";
+
+import { Page } from "ui/page";
+import { Observable } from "rxjs/Observable";
+import { FrameService } from "nativescript-angular/platform-providers";
+
+@Component({
+ selector: "first",
+ template: `
+
+
+
+
+
+ `
+})
+export class FirstComponent implements OnInit, OnDestroy {
+ constructor(page: Page) {
+ console.log("FirstComponent - constructor() page: " + page);
+ }
+
+ ngOnInit() {
+ console.log("FirstComponent - ngOnInit()");
+ }
+
+ ngOnDestroy() {
+ console.log("FirstComponent - ngOnDestroy()");
+ }
+}
diff --git a/e2e/single-page/app/main.aot.ts b/e2e/single-page/app/main.aot.ts
new file mode 100644
index 000000000..10352ce3c
--- /dev/null
+++ b/e2e/single-page/app/main.aot.ts
@@ -0,0 +1,4 @@
+import { platformNativeScript } from "nativescript-angular/platform-static";
+import { AppModuleNgFactory } from "./app.module.ngfactory";
+
+platformNativeScript({ createFrameOnBootstrap: true }).bootstrapModuleFactory(AppModuleNgFactory);
diff --git a/e2e/single-page/app/main.ts b/e2e/single-page/app/main.ts
new file mode 100644
index 000000000..864356357
--- /dev/null
+++ b/e2e/single-page/app/main.ts
@@ -0,0 +1,4 @@
+import { platformNativeScriptDynamic } from "nativescript-angular/platform";
+import { AppModule } from "./app.module";
+
+platformNativeScriptDynamic({ createFrameOnBootstrap: true }).bootstrapModule(AppModule);
diff --git a/e2e/single-page/app/package.json b/e2e/single-page/app/package.json
new file mode 100644
index 000000000..d5356c0cc
--- /dev/null
+++ b/e2e/single-page/app/package.json
@@ -0,0 +1,5 @@
+{
+ "main": "main.js",
+ "name": "nativescript-template-ng-tutorial",
+ "version": "3.1.0"
+}
\ No newline at end of file
diff --git a/e2e/single-page/app/rxjs-operators.ts b/e2e/single-page/app/rxjs-operators.ts
new file mode 100644
index 000000000..480bbe40f
--- /dev/null
+++ b/e2e/single-page/app/rxjs-operators.ts
@@ -0,0 +1 @@
+import "rxjs/add/operator/map";
\ No newline at end of file
diff --git a/e2e/single-page/app/second/second.component.ts b/e2e/single-page/app/second/second.component.ts
new file mode 100644
index 000000000..edb8aac7e
--- /dev/null
+++ b/e2e/single-page/app/second/second.component.ts
@@ -0,0 +1,35 @@
+import { Component, OnInit, OnDestroy } from "@angular/core";
+import { ActivatedRoute, Router, Route } from "@angular/router";
+
+import { Page } from "ui/page";
+import { Observable } from "rxjs/Observable";
+
+@Component({
+ selector: "second",
+ template: `
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class SecondComponent implements OnInit, OnDestroy {
+ public id$: Observable;
+ constructor(route: ActivatedRoute) {
+ this.id$ = route.params.map(r => +r["id"]);
+ }
+
+ ngOnInit() {
+ console.log("SecondComponent - ngOnInit()");
+ }
+
+ ngOnDestroy() {
+ console.log("SecondComponent - ngOnDestroy()");
+ }
+}
\ No newline at end of file
diff --git a/e2e/single-page/app/vendor-platform.android.ts b/e2e/single-page/app/vendor-platform.android.ts
new file mode 100644
index 000000000..719f26498
--- /dev/null
+++ b/e2e/single-page/app/vendor-platform.android.ts
@@ -0,0 +1,9 @@
+require("application");
+if (!global["__snapshot"]) {
+ // In case snapshot generation is enabled these modules will get into the bundle
+ // but will not be required/evaluated.
+ // The snapshot webpack plugin will add them to the tns-java-classes.js bundle file.
+ // This way, they will be evaluated on app start as early as possible.
+ require("ui/frame");
+ require("ui/frame/activity");
+}
diff --git a/e2e/single-page/app/vendor-platform.ios.ts b/e2e/single-page/app/vendor-platform.ios.ts
new file mode 100644
index 000000000..7f3e7f039
--- /dev/null
+++ b/e2e/single-page/app/vendor-platform.ios.ts
@@ -0,0 +1 @@
+void 0;
diff --git a/e2e/single-page/app/vendor.ts b/e2e/single-page/app/vendor.ts
new file mode 100644
index 000000000..4526eca9b
--- /dev/null
+++ b/e2e/single-page/app/vendor.ts
@@ -0,0 +1,20 @@
+// 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("./vendor-platform");
+
+require("reflect-metadata");
+require("@angular/platform-browser");
+require("@angular/core");
+require("@angular/common");
+require("@angular/forms");
+require("@angular/http");
+require("@angular/router");
+
+require("nativescript-angular/platform-static");
+require("nativescript-angular/forms");
+require("nativescript-angular/router");
diff --git a/e2e/single-page/e2e/setup.ts b/e2e/single-page/e2e/setup.ts
new file mode 100644
index 000000000..0c0add14a
--- /dev/null
+++ b/e2e/single-page/e2e/setup.ts
@@ -0,0 +1,19 @@
+import { startServer, stopServer , createDriver , AppiumDriver } from "nativescript-dev-appium";
+
+let driver: AppiumDriver;
+
+before("start server", async () => {
+ await startServer();
+ driver = await createDriver();
+});
+
+after("stop server", async () => {
+ await driver.quit();
+ await stopServer();
+});
+
+afterEach(async function () {
+ if (this.currentTest.state === "failed") {
+ await driver.logScreenshot(this.currentTest.title);
+ }
+});
\ No newline at end of file
diff --git a/e2e/single-page/e2e/tests.e2e-spec.ts b/e2e/single-page/e2e/tests.e2e-spec.ts
new file mode 100644
index 000000000..275c216bf
--- /dev/null
+++ b/e2e/single-page/e2e/tests.e2e-spec.ts
@@ -0,0 +1,54 @@
+import {
+ AppiumDriver,
+ UIElement,
+ createDriver,
+ SearchOptions,
+} from "nativescript-dev-appium";
+
+describe("Single page app", () => {
+ let driver: AppiumDriver;
+
+ before(async () => {
+ driver = await createDriver();
+ await driver.resetApp();
+ });
+
+ it("should load first page", async () => {
+ await driver.findElementByText("First Component", SearchOptions.exact);
+
+ // ActionBar Title and item
+ await driver.findElementByText("First Title", SearchOptions.exact);
+ await driver.findElementByText("ACTION1", SearchOptions.exact);
+ });
+
+ it("should load second(1) page", async () => {
+ await findAndClick(driver, "SECOND(1)")
+
+ await driver.findElementByText("Second Component: 1", SearchOptions.exact);
+
+ // ActionBar Title and item
+ await driver.findElementByText("Second Title", SearchOptions.exact);
+ await driver.findElementByText("ACTION2", SearchOptions.exact);
+ });
+
+ it("should load second(2) page", async () => {
+ await findAndClick(driver, "SECOND(2)")
+
+ await driver.findElementByText("Second Component: 1", SearchOptions.exact);
+
+ // ActionBar Title and items
+ await driver.findElementByText("Second Title", SearchOptions.exact);
+ await driver.findElementByText("ACTION2", SearchOptions.exact);
+ await driver.findElementByText("ADD", SearchOptions.exact);
+ });
+});
+
+async function assureFirstComponent(driver: AppiumDriver) {
+ await driver.findElementByText("First Component", SearchOptions.exact);
+}
+
+async function findAndClick(driver: AppiumDriver, text: string) {
+ const navigationButton =
+ await driver.findElementByText(text, SearchOptions.exact);
+ navigationButton.click();
+}
\ No newline at end of file
diff --git a/e2e/single-page/e2e/tsconfig.json b/e2e/single-page/e2e/tsconfig.json
new file mode 100644
index 000000000..18b6c4302
--- /dev/null
+++ b/e2e/single-page/e2e/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es6",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "importHelpers": false,
+ "types": [
+ "node",
+ "mocha",
+ "chai"
+ ],
+ "lib": [
+ "es6",
+ "dom"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/e2e/single-page/package.json b/e2e/single-page/package.json
new file mode 100644
index 000000000..4e7f98b8a
--- /dev/null
+++ b/e2e/single-page/package.json
@@ -0,0 +1,69 @@
+{
+ "description": "NativeScript Application",
+ "license": "SEE LICENSE IN ",
+ "readme": "NativeScript Application",
+ "repository": "",
+ "nativescript": {
+ "id": "org.nativescript.singlepage",
+ "tns-ios": {
+ "version": "4.0.1-2018-03-26-01"
+ },
+ "tns-android": {
+ "version": "4.0.0-2018.3.22.2"
+ }
+ },
+ "dependencies": {
+ "@angular/animations": "~5.2.0",
+ "@angular/common": "~5.2.0",
+ "@angular/compiler": "~5.2.0",
+ "@angular/core": "~5.2.0",
+ "@angular/forms": "~5.2.0",
+ "@angular/http": "~5.2.0",
+ "@angular/platform-browser": "~5.2.0",
+ "@angular/platform-browser-dynamic": "~5.2.0",
+ "@angular/router": "~5.2.0",
+ "nativescript-angular": "file:../../nativescript-angular",
+ "nativescript-intl": "^3.0.0",
+ "reflect-metadata": "~0.1.8",
+ "rxjs": "^5.5.4",
+ "tns-core-modules": "next",
+ "zone.js": "^0.8.4"
+ },
+ "devDependencies": {
+ "@angular/compiler-cli": "~5.2.0",
+ "@ngtools/webpack": "~1.9.4",
+ "@types/chai": "^4.0.2",
+ "@types/mocha": "^2.2.41",
+ "@types/node": "^7.0.5",
+ "babel-traverse": "6.25.0",
+ "babel-types": "6.25.0",
+ "babylon": "6.17.4",
+ "chai": "~4.1.1",
+ "chai-as-promised": "~7.1.1",
+ "colors": "^1.1.2",
+ "copy-webpack-plugin": "~4.3.0",
+ "css-loader": "~0.28.7",
+ "extract-text-webpack-plugin": "~3.0.2",
+ "lazy": "1.0.11",
+ "mocha": "~3.5.0",
+ "mocha-junit-reporter": "^1.13.0",
+ "mocha-multi": "^0.11.0",
+ "nativescript-dev-appium": "next",
+ "nativescript-dev-typescript": "~0.4.0",
+ "nativescript-dev-webpack": "^0.9.2",
+ "nativescript-worker-loader": "~0.8.1",
+ "raw-loader": "~0.5.1",
+ "resolve-url-loader": "~2.2.1",
+ "tslib": "^1.7.1",
+ "typescript": "~2.6.2",
+ "uglifyjs-webpack-plugin": "~1.1.6",
+ "webpack": "~3.10.0",
+ "webpack-bundle-analyzer": "^2.9.1",
+ "webpack-sources": "~1.1.0"
+ },
+ "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"
+ }
+}
diff --git a/e2e/single-page/tsconfig.json b/e2e/single-page/tsconfig.json
new file mode 100644
index 000000000..f89ae0f58
--- /dev/null
+++ b/e2e/single-page/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es5",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "noEmitHelpers": true,
+ "noEmitOnError": true,
+ "lib": [
+ "es6",
+ "dom",
+ "es2015.iterable"
+ ],
+ "baseUrl": ".",
+ "paths": {
+ "*": [
+ "./node_modules/tns-core-modules/*",
+ "./node_modules/*"
+ ]
+ }
+ },
+ "exclude": [
+ "node_modules",
+ "platforms",
+ "e2e"
+ ]
+}
diff --git a/nativescript-angular/directives/action-bar.ts b/nativescript-angular/directives/action-bar.ts
index d559d47b1..7dce38f0f 100644
--- a/nativescript-angular/directives/action-bar.ts
+++ b/nativescript-angular/directives/action-bar.ts
@@ -92,6 +92,10 @@ registerElement("NavigationButton", () => require("ui/action-bar").NavigationBut
})
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;
}
@@ -106,6 +110,9 @@ export class ActionBarComponent {
})
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) {
diff --git a/nativescript-angular/platform-common.ts b/nativescript-angular/platform-common.ts
index 935abf880..42baad04c 100644
--- a/nativescript-angular/platform-common.ts
+++ b/nativescript-angular/platform-common.ts
@@ -23,7 +23,7 @@ import {
import { DOCUMENT } from "@angular/common";
import { bootstrapLog, bootstrapLogError } from "./trace";
-import { defaultPageFactoryProvider, setRootPage } from "./platform-providers";
+import { defaultPageFactoryProvider, setRootPage, PageFactory, PAGE_FACTORY } from "./platform-providers";
import { AppHostView } from "./app-host-view";
import {
@@ -39,6 +39,7 @@ import { TextView } from "tns-core-modules/ui/text-view";
import "nativescript-intl";
import { Color, View } from "tns-core-modules/ui/core/view/view";
+import { Frame } from "tns-core-modules/ui/frame";
export const onBeforeLivesync = new EventEmitter>();
export const onAfterLivesync = new EventEmitter<{ moduleRef?: NgModuleRef; error?: Error }>();
@@ -55,6 +56,7 @@ export interface AppOptions {
bootInExistingPage?: boolean;
cssFile?: string;
startPageActionBarHidden?: boolean;
+ createFrameOnBootstrap?: boolean;
}
export type PlatformFactory = (extraProviders?: StaticProvider[]) => PlatformRef;
@@ -143,11 +145,20 @@ export class NativeScriptPlatformRef extends PlatformRef {
@profile
private bootstrapNativeScriptApp() {
- // Create a temp page for root of the renderer
- const tempAppHostView = new AppHostView();
- setRootPage(tempAppHostView);
+ const autoCreateFrame = !!this.appOptions.createFrameOnBootstrap;
+ let tempAppHostView: AppHostView;
let rootContent: View;
+ if (autoCreateFrame) {
+ const { page, frame } = this.createFrameAndPage(false);
+ setRootPage(page);
+ rootContent = frame;
+ } else {
+ // Create a temp page for root of the renderer
+ tempAppHostView = new AppHostView();
+ setRootPage(tempAppHostView);
+ }
+
bootstrapLog("NativeScriptPlatform bootstrap started.");
const launchCallback = profile(
"nativescript-angular/platform-common.launchCallback",
@@ -161,10 +172,11 @@ export class NativeScriptPlatformRef extends PlatformRef {
bootstrapPromiseCompleted = true;
bootstrapLog(`Angular bootstrap bootstrap done. uptime: ${uptime()}`);
- rootContent = tempAppHostView.content;
- tempAppHostView.content = null;
- tempAppHostView.ngAppRoot = rootContent;
- rootContent.parentNode = tempAppHostView;
+
+ if (!autoCreateFrame) {
+ rootContent = this.extractContentFromHost(tempAppHostView);
+ }
+
lastBootstrappedModule = new WeakRef(moduleRef);
},
err => {
@@ -199,13 +211,22 @@ export class NativeScriptPlatformRef extends PlatformRef {
@profile
private livesync() {
bootstrapLog("Angular livesync started.");
-
onBeforeLivesync.next(lastBootstrappedModule ? lastBootstrappedModule.get() : null);
- const tempAppHostView = new AppHostView();
- setRootPage(tempAppHostView);
+ const autoCreateFrame = !!this.appOptions.createFrameOnBootstrap;
+ let tempAppHostView: AppHostView;
let rootContent: View;
+ if (autoCreateFrame) {
+ const { page, frame } = this.createFrameAndPage(true);
+ setRootPage(page);
+ rootContent = frame;
+ } else {
+ // Create a temp page for root of the renderer
+ tempAppHostView = new AppHostView();
+ setRootPage(tempAppHostView);
+ }
+
let bootstrapPromiseCompleted = false;
this._bootstrapper().then(
moduleRef => {
@@ -213,10 +234,10 @@ export class NativeScriptPlatformRef extends PlatformRef {
bootstrapLog("Angular livesync done.");
onAfterLivesync.next({ moduleRef });
- rootContent = tempAppHostView.content;
- tempAppHostView.content = null;
- tempAppHostView.ngAppRoot = rootContent;
- rootContent.parentNode = tempAppHostView;
+ if (!autoCreateFrame) {
+ rootContent = this.extractContentFromHost(tempAppHostView);
+ }
+
lastBootstrappedModule = new WeakRef(moduleRef);
},
error => {
@@ -236,11 +257,11 @@ export class NativeScriptPlatformRef extends PlatformRef {
bootstrapLog("livesync bootstrapAction called, draining micro tasks queue finished! Root: " + rootContent);
if (!bootstrapPromiseCompleted) {
- const errorMessage = "Livesync bootstrap promise didn't resolve";
- bootstrapLogError(errorMessage);
- rootContent = this.createErrorUI(errorMessage);
+ const result = "Livesync bootstrap promise didn't resolve";
+ bootstrapLogError(result);
+ rootContent = this.createErrorUI(result);
- onAfterLivesync.next({ error: new Error(errorMessage) });
+ onAfterLivesync.next({ error: new Error(result) });
}
applicationRerun({
@@ -254,4 +275,21 @@ export class NativeScriptPlatformRef extends PlatformRef {
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 extractContentFromHost(tempAppHostView: AppHostView) {
+ const result = tempAppHostView.content;
+ tempAppHostView.content = null;
+ tempAppHostView.ngAppRoot = result;
+ result.parentNode = tempAppHostView;
+ return result;
+ }
}
diff --git a/nativescript-angular/platform-providers.ts b/nativescript-angular/platform-providers.ts
index fa58beb80..9c1cef6f6 100644
--- a/nativescript-angular/platform-providers.ts
+++ b/nativescript-angular/platform-providers.ts
@@ -21,8 +21,17 @@ export function getRootPage(): Page {
// 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 = topmost();
- return getRootPage() || (frame && frame.currentPage);
+ if (frame && frame.currentPage) {
+ return frame.currentPage;
+ }
+
+ return null;
}
export const defaultPageProvider = { provide: Page, useFactory: getDefaultPage };